Merge branch 'master' of http://git.roojs.com/roojs1
[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.layout.Border}
16637    var layout = new Roo.layout.Border(...);
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         
17017         if(this.rendered){
17018             return this;
17019         }
17020         
17021         if(this.fireEvent("beforerender", this) === false){
17022             return false;
17023         }
17024         
17025         if(!container && this.el){
17026             this.el = Roo.get(this.el);
17027             container = this.el.dom.parentNode;
17028             this.allowDomMove = false;
17029         }
17030         this.container = Roo.get(container);
17031         this.rendered = true;
17032         if(position !== undefined){
17033             if(typeof position == 'number'){
17034                 position = this.container.dom.childNodes[position];
17035             }else{
17036                 position = Roo.getDom(position);
17037             }
17038         }
17039         this.onRender(this.container, position || null);
17040         if(this.cls){
17041             this.el.addClass(this.cls);
17042             delete this.cls;
17043         }
17044         if(this.style){
17045             this.el.applyStyles(this.style);
17046             delete this.style;
17047         }
17048         this.fireEvent("render", this);
17049         this.afterRender(this.container);
17050         if(this.hidden){
17051             this.hide();
17052         }
17053         if(this.disabled){
17054             this.disable();
17055         }
17056
17057         return this;
17058         
17059     },
17060
17061     /** @private */
17062     // default function is not really useful
17063     onRender : function(ct, position){
17064         if(this.el){
17065             this.el = Roo.get(this.el);
17066             if(this.allowDomMove !== false){
17067                 ct.dom.insertBefore(this.el.dom, position);
17068             }
17069         }
17070     },
17071
17072     /** @private */
17073     getAutoCreate : function(){
17074         var cfg = typeof this.autoCreate == "object" ?
17075                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
17076         if(this.id && !cfg.id){
17077             cfg.id = this.id;
17078         }
17079         return cfg;
17080     },
17081
17082     /** @private */
17083     afterRender : Roo.emptyFn,
17084
17085     /**
17086      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
17087      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
17088      */
17089     destroy : function(){
17090         if(this.fireEvent("beforedestroy", this) !== false){
17091             this.purgeListeners();
17092             this.beforeDestroy();
17093             if(this.rendered){
17094                 this.el.removeAllListeners();
17095                 this.el.remove();
17096                 if(this.actionMode == "container"){
17097                     this.container.remove();
17098                 }
17099             }
17100             this.onDestroy();
17101             Roo.ComponentMgr.unregister(this);
17102             this.fireEvent("destroy", this);
17103         }
17104     },
17105
17106         /** @private */
17107     beforeDestroy : function(){
17108
17109     },
17110
17111         /** @private */
17112         onDestroy : function(){
17113
17114     },
17115
17116     /**
17117      * Returns the underlying {@link Roo.Element}.
17118      * @return {Roo.Element} The element
17119      */
17120     getEl : function(){
17121         return this.el;
17122     },
17123
17124     /**
17125      * Returns the id of this component.
17126      * @return {String}
17127      */
17128     getId : function(){
17129         return this.id;
17130     },
17131
17132     /**
17133      * Try to focus this component.
17134      * @param {Boolean} selectText True to also select the text in this component (if applicable)
17135      * @return {Roo.Component} this
17136      */
17137     focus : function(selectText){
17138         if(this.rendered){
17139             this.el.focus();
17140             if(selectText === true){
17141                 this.el.dom.select();
17142             }
17143         }
17144         return this;
17145     },
17146
17147     /** @private */
17148     blur : function(){
17149         if(this.rendered){
17150             this.el.blur();
17151         }
17152         return this;
17153     },
17154
17155     /**
17156      * Disable this component.
17157      * @return {Roo.Component} this
17158      */
17159     disable : function(){
17160         if(this.rendered){
17161             this.onDisable();
17162         }
17163         this.disabled = true;
17164         this.fireEvent("disable", this);
17165         return this;
17166     },
17167
17168         // private
17169     onDisable : function(){
17170         this.getActionEl().addClass(this.disabledClass);
17171         this.el.dom.disabled = true;
17172     },
17173
17174     /**
17175      * Enable this component.
17176      * @return {Roo.Component} this
17177      */
17178     enable : function(){
17179         if(this.rendered){
17180             this.onEnable();
17181         }
17182         this.disabled = false;
17183         this.fireEvent("enable", this);
17184         return this;
17185     },
17186
17187         // private
17188     onEnable : function(){
17189         this.getActionEl().removeClass(this.disabledClass);
17190         this.el.dom.disabled = false;
17191     },
17192
17193     /**
17194      * Convenience function for setting disabled/enabled by boolean.
17195      * @param {Boolean} disabled
17196      */
17197     setDisabled : function(disabled){
17198         this[disabled ? "disable" : "enable"]();
17199     },
17200
17201     /**
17202      * Show this component.
17203      * @return {Roo.Component} this
17204      */
17205     show: function(){
17206         if(this.fireEvent("beforeshow", this) !== false){
17207             this.hidden = false;
17208             if(this.rendered){
17209                 this.onShow();
17210             }
17211             this.fireEvent("show", this);
17212         }
17213         return this;
17214     },
17215
17216     // private
17217     onShow : function(){
17218         var ae = this.getActionEl();
17219         if(this.hideMode == 'visibility'){
17220             ae.dom.style.visibility = "visible";
17221         }else if(this.hideMode == 'offsets'){
17222             ae.removeClass('x-hidden');
17223         }else{
17224             ae.dom.style.display = "";
17225         }
17226     },
17227
17228     /**
17229      * Hide this component.
17230      * @return {Roo.Component} this
17231      */
17232     hide: function(){
17233         if(this.fireEvent("beforehide", this) !== false){
17234             this.hidden = true;
17235             if(this.rendered){
17236                 this.onHide();
17237             }
17238             this.fireEvent("hide", this);
17239         }
17240         return this;
17241     },
17242
17243     // private
17244     onHide : function(){
17245         var ae = this.getActionEl();
17246         if(this.hideMode == 'visibility'){
17247             ae.dom.style.visibility = "hidden";
17248         }else if(this.hideMode == 'offsets'){
17249             ae.addClass('x-hidden');
17250         }else{
17251             ae.dom.style.display = "none";
17252         }
17253     },
17254
17255     /**
17256      * Convenience function to hide or show this component by boolean.
17257      * @param {Boolean} visible True to show, false to hide
17258      * @return {Roo.Component} this
17259      */
17260     setVisible: function(visible){
17261         if(visible) {
17262             this.show();
17263         }else{
17264             this.hide();
17265         }
17266         return this;
17267     },
17268
17269     /**
17270      * Returns true if this component is visible.
17271      */
17272     isVisible : function(){
17273         return this.getActionEl().isVisible();
17274     },
17275
17276     cloneConfig : function(overrides){
17277         overrides = overrides || {};
17278         var id = overrides.id || Roo.id();
17279         var cfg = Roo.applyIf(overrides, this.initialConfig);
17280         cfg.id = id; // prevent dup id
17281         return new this.constructor(cfg);
17282     }
17283 });/*
17284  * Based on:
17285  * Ext JS Library 1.1.1
17286  * Copyright(c) 2006-2007, Ext JS, LLC.
17287  *
17288  * Originally Released Under LGPL - original licence link has changed is not relivant.
17289  *
17290  * Fork - LGPL
17291  * <script type="text/javascript">
17292  */
17293
17294 /**
17295  * @class Roo.BoxComponent
17296  * @extends Roo.Component
17297  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
17298  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
17299  * container classes should subclass BoxComponent so that they will work consistently when nested within other Roo
17300  * layout containers.
17301  * @constructor
17302  * @param {Roo.Element/String/Object} config The configuration options.
17303  */
17304 Roo.BoxComponent = function(config){
17305     Roo.Component.call(this, config);
17306     this.addEvents({
17307         /**
17308          * @event resize
17309          * Fires after the component is resized.
17310              * @param {Roo.Component} this
17311              * @param {Number} adjWidth The box-adjusted width that was set
17312              * @param {Number} adjHeight The box-adjusted height that was set
17313              * @param {Number} rawWidth The width that was originally specified
17314              * @param {Number} rawHeight The height that was originally specified
17315              */
17316         resize : true,
17317         /**
17318          * @event move
17319          * Fires after the component is moved.
17320              * @param {Roo.Component} this
17321              * @param {Number} x The new x position
17322              * @param {Number} y The new y position
17323              */
17324         move : true
17325     });
17326 };
17327
17328 Roo.extend(Roo.BoxComponent, Roo.Component, {
17329     // private, set in afterRender to signify that the component has been rendered
17330     boxReady : false,
17331     // private, used to defer height settings to subclasses
17332     deferHeight: false,
17333     /** @cfg {Number} width
17334      * width (optional) size of component
17335      */
17336      /** @cfg {Number} height
17337      * height (optional) size of component
17338      */
17339      
17340     /**
17341      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
17342      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
17343      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
17344      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
17345      * @return {Roo.BoxComponent} this
17346      */
17347     setSize : function(w, h){
17348         // support for standard size objects
17349         if(typeof w == 'object'){
17350             h = w.height;
17351             w = w.width;
17352         }
17353         // not rendered
17354         if(!this.boxReady){
17355             this.width = w;
17356             this.height = h;
17357             return this;
17358         }
17359
17360         // prevent recalcs when not needed
17361         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
17362             return this;
17363         }
17364         this.lastSize = {width: w, height: h};
17365
17366         var adj = this.adjustSize(w, h);
17367         var aw = adj.width, ah = adj.height;
17368         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
17369             var rz = this.getResizeEl();
17370             if(!this.deferHeight && aw !== undefined && ah !== undefined){
17371                 rz.setSize(aw, ah);
17372             }else if(!this.deferHeight && ah !== undefined){
17373                 rz.setHeight(ah);
17374             }else if(aw !== undefined){
17375                 rz.setWidth(aw);
17376             }
17377             this.onResize(aw, ah, w, h);
17378             this.fireEvent('resize', this, aw, ah, w, h);
17379         }
17380         return this;
17381     },
17382
17383     /**
17384      * Gets the current size of the component's underlying element.
17385      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
17386      */
17387     getSize : function(){
17388         return this.el.getSize();
17389     },
17390
17391     /**
17392      * Gets the current XY position of the component's underlying element.
17393      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
17394      * @return {Array} The XY position of the element (e.g., [100, 200])
17395      */
17396     getPosition : function(local){
17397         if(local === true){
17398             return [this.el.getLeft(true), this.el.getTop(true)];
17399         }
17400         return this.xy || this.el.getXY();
17401     },
17402
17403     /**
17404      * Gets the current box measurements of the component's underlying element.
17405      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
17406      * @returns {Object} box An object in the format {x, y, width, height}
17407      */
17408     getBox : function(local){
17409         var s = this.el.getSize();
17410         if(local){
17411             s.x = this.el.getLeft(true);
17412             s.y = this.el.getTop(true);
17413         }else{
17414             var xy = this.xy || this.el.getXY();
17415             s.x = xy[0];
17416             s.y = xy[1];
17417         }
17418         return s;
17419     },
17420
17421     /**
17422      * Sets the current box measurements of the component's underlying element.
17423      * @param {Object} box An object in the format {x, y, width, height}
17424      * @returns {Roo.BoxComponent} this
17425      */
17426     updateBox : function(box){
17427         this.setSize(box.width, box.height);
17428         this.setPagePosition(box.x, box.y);
17429         return this;
17430     },
17431
17432     // protected
17433     getResizeEl : function(){
17434         return this.resizeEl || this.el;
17435     },
17436
17437     // protected
17438     getPositionEl : function(){
17439         return this.positionEl || this.el;
17440     },
17441
17442     /**
17443      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
17444      * This method fires the move event.
17445      * @param {Number} left The new left
17446      * @param {Number} top The new top
17447      * @returns {Roo.BoxComponent} this
17448      */
17449     setPosition : function(x, y){
17450         this.x = x;
17451         this.y = y;
17452         if(!this.boxReady){
17453             return this;
17454         }
17455         var adj = this.adjustPosition(x, y);
17456         var ax = adj.x, ay = adj.y;
17457
17458         var el = this.getPositionEl();
17459         if(ax !== undefined || ay !== undefined){
17460             if(ax !== undefined && ay !== undefined){
17461                 el.setLeftTop(ax, ay);
17462             }else if(ax !== undefined){
17463                 el.setLeft(ax);
17464             }else if(ay !== undefined){
17465                 el.setTop(ay);
17466             }
17467             this.onPosition(ax, ay);
17468             this.fireEvent('move', this, ax, ay);
17469         }
17470         return this;
17471     },
17472
17473     /**
17474      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
17475      * This method fires the move event.
17476      * @param {Number} x The new x position
17477      * @param {Number} y The new y position
17478      * @returns {Roo.BoxComponent} this
17479      */
17480     setPagePosition : function(x, y){
17481         this.pageX = x;
17482         this.pageY = y;
17483         if(!this.boxReady){
17484             return;
17485         }
17486         if(x === undefined || y === undefined){ // cannot translate undefined points
17487             return;
17488         }
17489         var p = this.el.translatePoints(x, y);
17490         this.setPosition(p.left, p.top);
17491         return this;
17492     },
17493
17494     // private
17495     onRender : function(ct, position){
17496         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
17497         if(this.resizeEl){
17498             this.resizeEl = Roo.get(this.resizeEl);
17499         }
17500         if(this.positionEl){
17501             this.positionEl = Roo.get(this.positionEl);
17502         }
17503     },
17504
17505     // private
17506     afterRender : function(){
17507         Roo.BoxComponent.superclass.afterRender.call(this);
17508         this.boxReady = true;
17509         this.setSize(this.width, this.height);
17510         if(this.x || this.y){
17511             this.setPosition(this.x, this.y);
17512         }
17513         if(this.pageX || this.pageY){
17514             this.setPagePosition(this.pageX, this.pageY);
17515         }
17516     },
17517
17518     /**
17519      * Force the component's size to recalculate based on the underlying element's current height and width.
17520      * @returns {Roo.BoxComponent} this
17521      */
17522     syncSize : function(){
17523         delete this.lastSize;
17524         this.setSize(this.el.getWidth(), this.el.getHeight());
17525         return this;
17526     },
17527
17528     /**
17529      * Called after the component is resized, this method is empty by default but can be implemented by any
17530      * subclass that needs to perform custom logic after a resize occurs.
17531      * @param {Number} adjWidth The box-adjusted width that was set
17532      * @param {Number} adjHeight The box-adjusted height that was set
17533      * @param {Number} rawWidth The width that was originally specified
17534      * @param {Number} rawHeight The height that was originally specified
17535      */
17536     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
17537
17538     },
17539
17540     /**
17541      * Called after the component is moved, this method is empty by default but can be implemented by any
17542      * subclass that needs to perform custom logic after a move occurs.
17543      * @param {Number} x The new x position
17544      * @param {Number} y The new y position
17545      */
17546     onPosition : function(x, y){
17547
17548     },
17549
17550     // private
17551     adjustSize : function(w, h){
17552         if(this.autoWidth){
17553             w = 'auto';
17554         }
17555         if(this.autoHeight){
17556             h = 'auto';
17557         }
17558         return {width : w, height: h};
17559     },
17560
17561     // private
17562     adjustPosition : function(x, y){
17563         return {x : x, y: y};
17564     }
17565 });/*
17566  * Based on:
17567  * Ext JS Library 1.1.1
17568  * Copyright(c) 2006-2007, Ext JS, LLC.
17569  *
17570  * Originally Released Under LGPL - original licence link has changed is not relivant.
17571  *
17572  * Fork - LGPL
17573  * <script type="text/javascript">
17574  */
17575  (function(){ 
17576 /**
17577  * @class Roo.Layer
17578  * @extends Roo.Element
17579  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
17580  * automatic maintaining of shadow/shim positions.
17581  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
17582  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
17583  * you can pass a string with a CSS class name. False turns off the shadow.
17584  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
17585  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
17586  * @cfg {String} cls CSS class to add to the element
17587  * @cfg {Number} zindex Starting z-index (defaults to 11000)
17588  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
17589  * @constructor
17590  * @param {Object} config An object with config options.
17591  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
17592  */
17593
17594 Roo.Layer = function(config, existingEl){
17595     config = config || {};
17596     var dh = Roo.DomHelper;
17597     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
17598     if(existingEl){
17599         this.dom = Roo.getDom(existingEl);
17600     }
17601     if(!this.dom){
17602         var o = config.dh || {tag: "div", cls: "x-layer"};
17603         this.dom = dh.append(pel, o);
17604     }
17605     if(config.cls){
17606         this.addClass(config.cls);
17607     }
17608     this.constrain = config.constrain !== false;
17609     this.visibilityMode = Roo.Element.VISIBILITY;
17610     if(config.id){
17611         this.id = this.dom.id = config.id;
17612     }else{
17613         this.id = Roo.id(this.dom);
17614     }
17615     this.zindex = config.zindex || this.getZIndex();
17616     this.position("absolute", this.zindex);
17617     if(config.shadow){
17618         this.shadowOffset = config.shadowOffset || 4;
17619         this.shadow = new Roo.Shadow({
17620             offset : this.shadowOffset,
17621             mode : config.shadow
17622         });
17623     }else{
17624         this.shadowOffset = 0;
17625     }
17626     this.useShim = config.shim !== false && Roo.useShims;
17627     this.useDisplay = config.useDisplay;
17628     this.hide();
17629 };
17630
17631 var supr = Roo.Element.prototype;
17632
17633 // shims are shared among layer to keep from having 100 iframes
17634 var shims = [];
17635
17636 Roo.extend(Roo.Layer, Roo.Element, {
17637
17638     getZIndex : function(){
17639         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
17640     },
17641
17642     getShim : function(){
17643         if(!this.useShim){
17644             return null;
17645         }
17646         if(this.shim){
17647             return this.shim;
17648         }
17649         var shim = shims.shift();
17650         if(!shim){
17651             shim = this.createShim();
17652             shim.enableDisplayMode('block');
17653             shim.dom.style.display = 'none';
17654             shim.dom.style.visibility = 'visible';
17655         }
17656         var pn = this.dom.parentNode;
17657         if(shim.dom.parentNode != pn){
17658             pn.insertBefore(shim.dom, this.dom);
17659         }
17660         shim.setStyle('z-index', this.getZIndex()-2);
17661         this.shim = shim;
17662         return shim;
17663     },
17664
17665     hideShim : function(){
17666         if(this.shim){
17667             this.shim.setDisplayed(false);
17668             shims.push(this.shim);
17669             delete this.shim;
17670         }
17671     },
17672
17673     disableShadow : function(){
17674         if(this.shadow){
17675             this.shadowDisabled = true;
17676             this.shadow.hide();
17677             this.lastShadowOffset = this.shadowOffset;
17678             this.shadowOffset = 0;
17679         }
17680     },
17681
17682     enableShadow : function(show){
17683         if(this.shadow){
17684             this.shadowDisabled = false;
17685             this.shadowOffset = this.lastShadowOffset;
17686             delete this.lastShadowOffset;
17687             if(show){
17688                 this.sync(true);
17689             }
17690         }
17691     },
17692
17693     // private
17694     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
17695     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
17696     sync : function(doShow){
17697         var sw = this.shadow;
17698         if(!this.updating && this.isVisible() && (sw || this.useShim)){
17699             var sh = this.getShim();
17700
17701             var w = this.getWidth(),
17702                 h = this.getHeight();
17703
17704             var l = this.getLeft(true),
17705                 t = this.getTop(true);
17706
17707             if(sw && !this.shadowDisabled){
17708                 if(doShow && !sw.isVisible()){
17709                     sw.show(this);
17710                 }else{
17711                     sw.realign(l, t, w, h);
17712                 }
17713                 if(sh){
17714                     if(doShow){
17715                        sh.show();
17716                     }
17717                     // fit the shim behind the shadow, so it is shimmed too
17718                     var a = sw.adjusts, s = sh.dom.style;
17719                     s.left = (Math.min(l, l+a.l))+"px";
17720                     s.top = (Math.min(t, t+a.t))+"px";
17721                     s.width = (w+a.w)+"px";
17722                     s.height = (h+a.h)+"px";
17723                 }
17724             }else if(sh){
17725                 if(doShow){
17726                    sh.show();
17727                 }
17728                 sh.setSize(w, h);
17729                 sh.setLeftTop(l, t);
17730             }
17731             
17732         }
17733     },
17734
17735     // private
17736     destroy : function(){
17737         this.hideShim();
17738         if(this.shadow){
17739             this.shadow.hide();
17740         }
17741         this.removeAllListeners();
17742         var pn = this.dom.parentNode;
17743         if(pn){
17744             pn.removeChild(this.dom);
17745         }
17746         Roo.Element.uncache(this.id);
17747     },
17748
17749     remove : function(){
17750         this.destroy();
17751     },
17752
17753     // private
17754     beginUpdate : function(){
17755         this.updating = true;
17756     },
17757
17758     // private
17759     endUpdate : function(){
17760         this.updating = false;
17761         this.sync(true);
17762     },
17763
17764     // private
17765     hideUnders : function(negOffset){
17766         if(this.shadow){
17767             this.shadow.hide();
17768         }
17769         this.hideShim();
17770     },
17771
17772     // private
17773     constrainXY : function(){
17774         if(this.constrain){
17775             var vw = Roo.lib.Dom.getViewWidth(),
17776                 vh = Roo.lib.Dom.getViewHeight();
17777             var s = Roo.get(document).getScroll();
17778
17779             var xy = this.getXY();
17780             var x = xy[0], y = xy[1];   
17781             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
17782             // only move it if it needs it
17783             var moved = false;
17784             // first validate right/bottom
17785             if((x + w) > vw+s.left){
17786                 x = vw - w - this.shadowOffset;
17787                 moved = true;
17788             }
17789             if((y + h) > vh+s.top){
17790                 y = vh - h - this.shadowOffset;
17791                 moved = true;
17792             }
17793             // then make sure top/left isn't negative
17794             if(x < s.left){
17795                 x = s.left;
17796                 moved = true;
17797             }
17798             if(y < s.top){
17799                 y = s.top;
17800                 moved = true;
17801             }
17802             if(moved){
17803                 if(this.avoidY){
17804                     var ay = this.avoidY;
17805                     if(y <= ay && (y+h) >= ay){
17806                         y = ay-h-5;   
17807                     }
17808                 }
17809                 xy = [x, y];
17810                 this.storeXY(xy);
17811                 supr.setXY.call(this, xy);
17812                 this.sync();
17813             }
17814         }
17815     },
17816
17817     isVisible : function(){
17818         return this.visible;    
17819     },
17820
17821     // private
17822     showAction : function(){
17823         this.visible = true; // track visibility to prevent getStyle calls
17824         if(this.useDisplay === true){
17825             this.setDisplayed("");
17826         }else if(this.lastXY){
17827             supr.setXY.call(this, this.lastXY);
17828         }else if(this.lastLT){
17829             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
17830         }
17831     },
17832
17833     // private
17834     hideAction : function(){
17835         this.visible = false;
17836         if(this.useDisplay === true){
17837             this.setDisplayed(false);
17838         }else{
17839             this.setLeftTop(-10000,-10000);
17840         }
17841     },
17842
17843     // overridden Element method
17844     setVisible : function(v, a, d, c, e){
17845         if(v){
17846             this.showAction();
17847         }
17848         if(a && v){
17849             var cb = function(){
17850                 this.sync(true);
17851                 if(c){
17852                     c();
17853                 }
17854             }.createDelegate(this);
17855             supr.setVisible.call(this, true, true, d, cb, e);
17856         }else{
17857             if(!v){
17858                 this.hideUnders(true);
17859             }
17860             var cb = c;
17861             if(a){
17862                 cb = function(){
17863                     this.hideAction();
17864                     if(c){
17865                         c();
17866                     }
17867                 }.createDelegate(this);
17868             }
17869             supr.setVisible.call(this, v, a, d, cb, e);
17870             if(v){
17871                 this.sync(true);
17872             }else if(!a){
17873                 this.hideAction();
17874             }
17875         }
17876     },
17877
17878     storeXY : function(xy){
17879         delete this.lastLT;
17880         this.lastXY = xy;
17881     },
17882
17883     storeLeftTop : function(left, top){
17884         delete this.lastXY;
17885         this.lastLT = [left, top];
17886     },
17887
17888     // private
17889     beforeFx : function(){
17890         this.beforeAction();
17891         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
17892     },
17893
17894     // private
17895     afterFx : function(){
17896         Roo.Layer.superclass.afterFx.apply(this, arguments);
17897         this.sync(this.isVisible());
17898     },
17899
17900     // private
17901     beforeAction : function(){
17902         if(!this.updating && this.shadow){
17903             this.shadow.hide();
17904         }
17905     },
17906
17907     // overridden Element method
17908     setLeft : function(left){
17909         this.storeLeftTop(left, this.getTop(true));
17910         supr.setLeft.apply(this, arguments);
17911         this.sync();
17912     },
17913
17914     setTop : function(top){
17915         this.storeLeftTop(this.getLeft(true), top);
17916         supr.setTop.apply(this, arguments);
17917         this.sync();
17918     },
17919
17920     setLeftTop : function(left, top){
17921         this.storeLeftTop(left, top);
17922         supr.setLeftTop.apply(this, arguments);
17923         this.sync();
17924     },
17925
17926     setXY : function(xy, a, d, c, e){
17927         this.fixDisplay();
17928         this.beforeAction();
17929         this.storeXY(xy);
17930         var cb = this.createCB(c);
17931         supr.setXY.call(this, xy, a, d, cb, e);
17932         if(!a){
17933             cb();
17934         }
17935     },
17936
17937     // private
17938     createCB : function(c){
17939         var el = this;
17940         return function(){
17941             el.constrainXY();
17942             el.sync(true);
17943             if(c){
17944                 c();
17945             }
17946         };
17947     },
17948
17949     // overridden Element method
17950     setX : function(x, a, d, c, e){
17951         this.setXY([x, this.getY()], a, d, c, e);
17952     },
17953
17954     // overridden Element method
17955     setY : function(y, a, d, c, e){
17956         this.setXY([this.getX(), y], a, d, c, e);
17957     },
17958
17959     // overridden Element method
17960     setSize : function(w, h, a, d, c, e){
17961         this.beforeAction();
17962         var cb = this.createCB(c);
17963         supr.setSize.call(this, w, h, a, d, cb, e);
17964         if(!a){
17965             cb();
17966         }
17967     },
17968
17969     // overridden Element method
17970     setWidth : function(w, a, d, c, e){
17971         this.beforeAction();
17972         var cb = this.createCB(c);
17973         supr.setWidth.call(this, w, a, d, cb, e);
17974         if(!a){
17975             cb();
17976         }
17977     },
17978
17979     // overridden Element method
17980     setHeight : function(h, a, d, c, e){
17981         this.beforeAction();
17982         var cb = this.createCB(c);
17983         supr.setHeight.call(this, h, a, d, cb, e);
17984         if(!a){
17985             cb();
17986         }
17987     },
17988
17989     // overridden Element method
17990     setBounds : function(x, y, w, h, a, d, c, e){
17991         this.beforeAction();
17992         var cb = this.createCB(c);
17993         if(!a){
17994             this.storeXY([x, y]);
17995             supr.setXY.call(this, [x, y]);
17996             supr.setSize.call(this, w, h, a, d, cb, e);
17997             cb();
17998         }else{
17999             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
18000         }
18001         return this;
18002     },
18003     
18004     /**
18005      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
18006      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
18007      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
18008      * @param {Number} zindex The new z-index to set
18009      * @return {this} The Layer
18010      */
18011     setZIndex : function(zindex){
18012         this.zindex = zindex;
18013         this.setStyle("z-index", zindex + 2);
18014         if(this.shadow){
18015             this.shadow.setZIndex(zindex + 1);
18016         }
18017         if(this.shim){
18018             this.shim.setStyle("z-index", zindex);
18019         }
18020     }
18021 });
18022 })();/*
18023  * Original code for Roojs - LGPL
18024  * <script type="text/javascript">
18025  */
18026  
18027 /**
18028  * @class Roo.XComponent
18029  * A delayed Element creator...
18030  * Or a way to group chunks of interface together.
18031  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
18032  *  used in conjunction with XComponent.build() it will create an instance of each element,
18033  *  then call addxtype() to build the User interface.
18034  * 
18035  * Mypart.xyx = new Roo.XComponent({
18036
18037     parent : 'Mypart.xyz', // empty == document.element.!!
18038     order : '001',
18039     name : 'xxxx'
18040     region : 'xxxx'
18041     disabled : function() {} 
18042      
18043     tree : function() { // return an tree of xtype declared components
18044         var MODULE = this;
18045         return 
18046         {
18047             xtype : 'NestedLayoutPanel',
18048             // technicall
18049         }
18050      ]
18051  *})
18052  *
18053  *
18054  * It can be used to build a big heiracy, with parent etc.
18055  * or you can just use this to render a single compoent to a dom element
18056  * MYPART.render(Roo.Element | String(id) | dom_element )
18057  *
18058  *
18059  * Usage patterns.
18060  *
18061  * Classic Roo
18062  *
18063  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
18064  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
18065  *
18066  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
18067  *
18068  * When the top level is false, a 'Roo.layout.Border' is created and the element is flagged as 'topModule'
18069  * - if mulitple topModules exist, the last one is defined as the top module.
18070  *
18071  * Embeded Roo
18072  * 
18073  * When the top level or multiple modules are to embedded into a existing HTML page,
18074  * the parent element can container '#id' of the element where the module will be drawn.
18075  *
18076  * Bootstrap Roo
18077  *
18078  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
18079  * it relies more on a include mechanism, where sub modules are included into an outer page.
18080  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
18081  * 
18082  * Bootstrap Roo Included elements
18083  *
18084  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
18085  * hence confusing the component builder as it thinks there are multiple top level elements. 
18086  *
18087  * String Over-ride & Translations
18088  *
18089  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
18090  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
18091  * are needed. @see Roo.XComponent.overlayString  
18092  * 
18093  * 
18094  * 
18095  * @extends Roo.util.Observable
18096  * @constructor
18097  * @param cfg {Object} configuration of component
18098  * 
18099  */
18100 Roo.XComponent = function(cfg) {
18101     Roo.apply(this, cfg);
18102     this.addEvents({ 
18103         /**
18104              * @event built
18105              * Fires when this the componnt is built
18106              * @param {Roo.XComponent} c the component
18107              */
18108         'built' : true
18109         
18110     });
18111     this.region = this.region || 'center'; // default..
18112     Roo.XComponent.register(this);
18113     this.modules = false;
18114     this.el = false; // where the layout goes..
18115     
18116     
18117 }
18118 Roo.extend(Roo.XComponent, Roo.util.Observable, {
18119     /**
18120      * @property el
18121      * The created element (with Roo.factory())
18122      * @type {Roo.Layout}
18123      */
18124     el  : false,
18125     
18126     /**
18127      * @property el
18128      * for BC  - use el in new code
18129      * @type {Roo.Layout}
18130      */
18131     panel : false,
18132     
18133     /**
18134      * @property layout
18135      * for BC  - use el in new code
18136      * @type {Roo.Layout}
18137      */
18138     layout : false,
18139     
18140      /**
18141      * @cfg {Function|boolean} disabled
18142      * If this module is disabled by some rule, return true from the funtion
18143      */
18144     disabled : false,
18145     
18146     /**
18147      * @cfg {String} parent 
18148      * Name of parent element which it get xtype added to..
18149      */
18150     parent: false,
18151     
18152     /**
18153      * @cfg {String} order
18154      * Used to set the order in which elements are created (usefull for multiple tabs)
18155      */
18156     
18157     order : false,
18158     /**
18159      * @cfg {String} name
18160      * String to display while loading.
18161      */
18162     name : false,
18163     /**
18164      * @cfg {String} region
18165      * Region to render component to (defaults to center)
18166      */
18167     region : 'center',
18168     
18169     /**
18170      * @cfg {Array} items
18171      * A single item array - the first element is the root of the tree..
18172      * It's done this way to stay compatible with the Xtype system...
18173      */
18174     items : false,
18175     
18176     /**
18177      * @property _tree
18178      * The method that retuns the tree of parts that make up this compoennt 
18179      * @type {function}
18180      */
18181     _tree  : false,
18182     
18183      /**
18184      * render
18185      * render element to dom or tree
18186      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
18187      */
18188     
18189     render : function(el)
18190     {
18191         
18192         el = el || false;
18193         var hp = this.parent ? 1 : 0;
18194         Roo.debug &&  Roo.log(this);
18195         
18196         var tree = this._tree ? this._tree() : this.tree();
18197
18198         
18199         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
18200             // if parent is a '#.....' string, then let's use that..
18201             var ename = this.parent.substr(1);
18202             this.parent = false;
18203             Roo.debug && Roo.log(ename);
18204             switch (ename) {
18205                 case 'bootstrap-body':
18206                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
18207                         // this is the BorderLayout standard?
18208                        this.parent = { el : true };
18209                        break;
18210                     }
18211                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
18212                         // need to insert stuff...
18213                         this.parent =  {
18214                              el : new Roo.bootstrap.layout.Border({
18215                                  el : document.body, 
18216                      
18217                                  center: {
18218                                     titlebar: false,
18219                                     autoScroll:false,
18220                                     closeOnTab: true,
18221                                     tabPosition: 'top',
18222                                       //resizeTabs: true,
18223                                     alwaysShowTabs: true,
18224                                     hideTabs: false
18225                                      //minTabWidth: 140
18226                                  }
18227                              })
18228                         
18229                          };
18230                          break;
18231                     }
18232                          
18233                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
18234                         this.parent = { el :  new  Roo.bootstrap.Body() };
18235                         Roo.debug && Roo.log("setting el to doc body");
18236                          
18237                     } else {
18238                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
18239                     }
18240                     break;
18241                 case 'bootstrap':
18242                     this.parent = { el : true};
18243                     // fall through
18244                 default:
18245                     el = Roo.get(ename);
18246                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
18247                         this.parent = { el : true};
18248                     }
18249                     
18250                     break;
18251             }
18252                 
18253             
18254             if (!el && !this.parent) {
18255                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
18256                 return;
18257             }
18258         }
18259         
18260         Roo.debug && Roo.log("EL:");
18261         Roo.debug && Roo.log(el);
18262         Roo.debug && Roo.log("this.parent.el:");
18263         Roo.debug && Roo.log(this.parent.el);
18264         
18265
18266         // altertive root elements ??? - we need a better way to indicate these.
18267         var is_alt = Roo.XComponent.is_alt ||
18268                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
18269                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
18270                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
18271         
18272         
18273         
18274         if (!this.parent && is_alt) {
18275             //el = Roo.get(document.body);
18276             this.parent = { el : true };
18277         }
18278             
18279             
18280         
18281         if (!this.parent) {
18282             
18283             Roo.debug && Roo.log("no parent - creating one");
18284             
18285             el = el ? Roo.get(el) : false;      
18286             
18287             if (typeof(Roo.layout.Border) == 'undefined' ) {
18288                 
18289                 this.parent =  {
18290                     el : new Roo.bootstrap.layout.Border({
18291                         el: el || document.body,
18292                     
18293                         center: {
18294                             titlebar: false,
18295                             autoScroll:false,
18296                             closeOnTab: true,
18297                             tabPosition: 'top',
18298                              //resizeTabs: true,
18299                             alwaysShowTabs: false,
18300                             hideTabs: true,
18301                             minTabWidth: 140,
18302                             overflow: 'visible'
18303                          }
18304                      })
18305                 };
18306             } else {
18307             
18308                 // it's a top level one..
18309                 this.parent =  {
18310                     el : new Roo.layout.Border(el || document.body, {
18311                         center: {
18312                             titlebar: false,
18313                             autoScroll:false,
18314                             closeOnTab: true,
18315                             tabPosition: 'top',
18316                              //resizeTabs: true,
18317                             alwaysShowTabs: el && hp? false :  true,
18318                             hideTabs: el || !hp ? true :  false,
18319                             minTabWidth: 140
18320                          }
18321                     })
18322                 };
18323             }
18324         }
18325         
18326         if (!this.parent.el) {
18327                 // probably an old style ctor, which has been disabled.
18328                 return;
18329
18330         }
18331                 // The 'tree' method is  '_tree now' 
18332             
18333         tree.region = tree.region || this.region;
18334         var is_body = false;
18335         if (this.parent.el === true) {
18336             // bootstrap... - body..
18337             if (el) {
18338                 tree.el = el;
18339             }
18340             this.parent.el = Roo.factory(tree);
18341             is_body = true;
18342         }
18343         
18344         this.el = this.parent.el.addxtype(tree, undefined, is_body);
18345         this.fireEvent('built', this);
18346         
18347         this.panel = this.el;
18348         this.layout = this.panel.layout;
18349         this.parentLayout = this.parent.layout  || false;  
18350          
18351     }
18352     
18353 });
18354
18355 Roo.apply(Roo.XComponent, {
18356     /**
18357      * @property  hideProgress
18358      * true to disable the building progress bar.. usefull on single page renders.
18359      * @type Boolean
18360      */
18361     hideProgress : false,
18362     /**
18363      * @property  buildCompleted
18364      * True when the builder has completed building the interface.
18365      * @type Boolean
18366      */
18367     buildCompleted : false,
18368      
18369     /**
18370      * @property  topModule
18371      * the upper most module - uses document.element as it's constructor.
18372      * @type Object
18373      */
18374      
18375     topModule  : false,
18376       
18377     /**
18378      * @property  modules
18379      * array of modules to be created by registration system.
18380      * @type {Array} of Roo.XComponent
18381      */
18382     
18383     modules : [],
18384     /**
18385      * @property  elmodules
18386      * array of modules to be created by which use #ID 
18387      * @type {Array} of Roo.XComponent
18388      */
18389      
18390     elmodules : [],
18391
18392      /**
18393      * @property  is_alt
18394      * Is an alternative Root - normally used by bootstrap or other systems,
18395      *    where the top element in the tree can wrap 'body' 
18396      * @type {boolean}  (default false)
18397      */
18398      
18399     is_alt : false,
18400     /**
18401      * @property  build_from_html
18402      * Build elements from html - used by bootstrap HTML stuff 
18403      *    - this is cleared after build is completed
18404      * @type {boolean}    (default false)
18405      */
18406      
18407     build_from_html : false,
18408     /**
18409      * Register components to be built later.
18410      *
18411      * This solves the following issues
18412      * - Building is not done on page load, but after an authentication process has occured.
18413      * - Interface elements are registered on page load
18414      * - Parent Interface elements may not be loaded before child, so this handles that..
18415      * 
18416      *
18417      * example:
18418      * 
18419      * MyApp.register({
18420           order : '000001',
18421           module : 'Pman.Tab.projectMgr',
18422           region : 'center',
18423           parent : 'Pman.layout',
18424           disabled : false,  // or use a function..
18425         })
18426      
18427      * * @param {Object} details about module
18428      */
18429     register : function(obj) {
18430                 
18431         Roo.XComponent.event.fireEvent('register', obj);
18432         switch(typeof(obj.disabled) ) {
18433                 
18434             case 'undefined':
18435                 break;
18436             
18437             case 'function':
18438                 if ( obj.disabled() ) {
18439                         return;
18440                 }
18441                 break;
18442             
18443             default:
18444                 if (obj.disabled || obj.region == '#disabled') {
18445                         return;
18446                 }
18447                 break;
18448         }
18449                 
18450         this.modules.push(obj);
18451          
18452     },
18453     /**
18454      * convert a string to an object..
18455      * eg. 'AAA.BBB' -> finds AAA.BBB
18456
18457      */
18458     
18459     toObject : function(str)
18460     {
18461         if (!str || typeof(str) == 'object') {
18462             return str;
18463         }
18464         if (str.substring(0,1) == '#') {
18465             return str;
18466         }
18467
18468         var ar = str.split('.');
18469         var rt, o;
18470         rt = ar.shift();
18471             /** eval:var:o */
18472         try {
18473             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
18474         } catch (e) {
18475             throw "Module not found : " + str;
18476         }
18477         
18478         if (o === false) {
18479             throw "Module not found : " + str;
18480         }
18481         Roo.each(ar, function(e) {
18482             if (typeof(o[e]) == 'undefined') {
18483                 throw "Module not found : " + str;
18484             }
18485             o = o[e];
18486         });
18487         
18488         return o;
18489         
18490     },
18491     
18492     
18493     /**
18494      * move modules into their correct place in the tree..
18495      * 
18496      */
18497     preBuild : function ()
18498     {
18499         var _t = this;
18500         Roo.each(this.modules , function (obj)
18501         {
18502             Roo.XComponent.event.fireEvent('beforebuild', obj);
18503             
18504             var opar = obj.parent;
18505             try { 
18506                 obj.parent = this.toObject(opar);
18507             } catch(e) {
18508                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
18509                 return;
18510             }
18511             
18512             if (!obj.parent) {
18513                 Roo.debug && Roo.log("GOT top level module");
18514                 Roo.debug && Roo.log(obj);
18515                 obj.modules = new Roo.util.MixedCollection(false, 
18516                     function(o) { return o.order + '' }
18517                 );
18518                 this.topModule = obj;
18519                 return;
18520             }
18521                         // parent is a string (usually a dom element name..)
18522             if (typeof(obj.parent) == 'string') {
18523                 this.elmodules.push(obj);
18524                 return;
18525             }
18526             if (obj.parent.constructor != Roo.XComponent) {
18527                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
18528             }
18529             if (!obj.parent.modules) {
18530                 obj.parent.modules = new Roo.util.MixedCollection(false, 
18531                     function(o) { return o.order + '' }
18532                 );
18533             }
18534             if (obj.parent.disabled) {
18535                 obj.disabled = true;
18536             }
18537             obj.parent.modules.add(obj);
18538         }, this);
18539     },
18540     
18541      /**
18542      * make a list of modules to build.
18543      * @return {Array} list of modules. 
18544      */ 
18545     
18546     buildOrder : function()
18547     {
18548         var _this = this;
18549         var cmp = function(a,b) {   
18550             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
18551         };
18552         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
18553             throw "No top level modules to build";
18554         }
18555         
18556         // make a flat list in order of modules to build.
18557         var mods = this.topModule ? [ this.topModule ] : [];
18558                 
18559         
18560         // elmodules (is a list of DOM based modules )
18561         Roo.each(this.elmodules, function(e) {
18562             mods.push(e);
18563             if (!this.topModule &&
18564                 typeof(e.parent) == 'string' &&
18565                 e.parent.substring(0,1) == '#' &&
18566                 Roo.get(e.parent.substr(1))
18567                ) {
18568                 
18569                 _this.topModule = e;
18570             }
18571             
18572         });
18573
18574         
18575         // add modules to their parents..
18576         var addMod = function(m) {
18577             Roo.debug && Roo.log("build Order: add: " + m.name);
18578                 
18579             mods.push(m);
18580             if (m.modules && !m.disabled) {
18581                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
18582                 m.modules.keySort('ASC',  cmp );
18583                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
18584     
18585                 m.modules.each(addMod);
18586             } else {
18587                 Roo.debug && Roo.log("build Order: no child modules");
18588             }
18589             // not sure if this is used any more..
18590             if (m.finalize) {
18591                 m.finalize.name = m.name + " (clean up) ";
18592                 mods.push(m.finalize);
18593             }
18594             
18595         }
18596         if (this.topModule && this.topModule.modules) { 
18597             this.topModule.modules.keySort('ASC',  cmp );
18598             this.topModule.modules.each(addMod);
18599         } 
18600         return mods;
18601     },
18602     
18603      /**
18604      * Build the registered modules.
18605      * @param {Object} parent element.
18606      * @param {Function} optional method to call after module has been added.
18607      * 
18608      */ 
18609    
18610     build : function(opts) 
18611     {
18612         
18613         if (typeof(opts) != 'undefined') {
18614             Roo.apply(this,opts);
18615         }
18616         
18617         this.preBuild();
18618         var mods = this.buildOrder();
18619       
18620         //this.allmods = mods;
18621         //Roo.debug && Roo.log(mods);
18622         //return;
18623         if (!mods.length) { // should not happen
18624             throw "NO modules!!!";
18625         }
18626         
18627         
18628         var msg = "Building Interface...";
18629         // flash it up as modal - so we store the mask!?
18630         if (!this.hideProgress && Roo.MessageBox) {
18631             Roo.MessageBox.show({ title: 'loading' });
18632             Roo.MessageBox.show({
18633                title: "Please wait...",
18634                msg: msg,
18635                width:450,
18636                progress:true,
18637                buttons : false,
18638                closable:false,
18639                modal: false
18640               
18641             });
18642         }
18643         var total = mods.length;
18644         
18645         var _this = this;
18646         var progressRun = function() {
18647             if (!mods.length) {
18648                 Roo.debug && Roo.log('hide?');
18649                 if (!this.hideProgress && Roo.MessageBox) {
18650                     Roo.MessageBox.hide();
18651                 }
18652                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
18653                 
18654                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
18655                 
18656                 // THE END...
18657                 return false;   
18658             }
18659             
18660             var m = mods.shift();
18661             
18662             
18663             Roo.debug && Roo.log(m);
18664             // not sure if this is supported any more.. - modules that are are just function
18665             if (typeof(m) == 'function') { 
18666                 m.call(this);
18667                 return progressRun.defer(10, _this);
18668             } 
18669             
18670             
18671             msg = "Building Interface " + (total  - mods.length) + 
18672                     " of " + total + 
18673                     (m.name ? (' - ' + m.name) : '');
18674                         Roo.debug && Roo.log(msg);
18675             if (!_this.hideProgress &&  Roo.MessageBox) { 
18676                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
18677             }
18678             
18679          
18680             // is the module disabled?
18681             var disabled = (typeof(m.disabled) == 'function') ?
18682                 m.disabled.call(m.module.disabled) : m.disabled;    
18683             
18684             
18685             if (disabled) {
18686                 return progressRun(); // we do not update the display!
18687             }
18688             
18689             // now build 
18690             
18691                         
18692                         
18693             m.render();
18694             // it's 10 on top level, and 1 on others??? why...
18695             return progressRun.defer(10, _this);
18696              
18697         }
18698         progressRun.defer(1, _this);
18699      
18700         
18701         
18702     },
18703     /**
18704      * Overlay a set of modified strings onto a component
18705      * This is dependant on our builder exporting the strings and 'named strings' elements.
18706      * 
18707      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
18708      * @param {Object} associative array of 'named' string and it's new value.
18709      * 
18710      */
18711         overlayStrings : function( component, strings )
18712     {
18713         if (typeof(component['_named_strings']) == 'undefined') {
18714             throw "ERROR: component does not have _named_strings";
18715         }
18716         for ( var k in strings ) {
18717             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
18718             if (md !== false) {
18719                 component['_strings'][md] = strings[k];
18720             } else {
18721                 Roo.log('could not find named string: ' + k + ' in');
18722                 Roo.log(component);
18723             }
18724             
18725         }
18726         
18727     },
18728     
18729         
18730         /**
18731          * Event Object.
18732          *
18733          *
18734          */
18735         event: false, 
18736     /**
18737          * wrapper for event.on - aliased later..  
18738          * Typically use to register a event handler for register:
18739          *
18740          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
18741          *
18742          */
18743     on : false
18744    
18745     
18746     
18747 });
18748
18749 Roo.XComponent.event = new Roo.util.Observable({
18750                 events : { 
18751                         /**
18752                          * @event register
18753                          * Fires when an Component is registered,
18754                          * set the disable property on the Component to stop registration.
18755                          * @param {Roo.XComponent} c the component being registerd.
18756                          * 
18757                          */
18758                         'register' : true,
18759             /**
18760                          * @event beforebuild
18761                          * Fires before each Component is built
18762                          * can be used to apply permissions.
18763                          * @param {Roo.XComponent} c the component being registerd.
18764                          * 
18765                          */
18766                         'beforebuild' : true,
18767                         /**
18768                          * @event buildcomplete
18769                          * Fires on the top level element when all elements have been built
18770                          * @param {Roo.XComponent} the top level component.
18771                          */
18772                         'buildcomplete' : true
18773                         
18774                 }
18775 });
18776
18777 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
18778  //
18779  /**
18780  * marked - a markdown parser
18781  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
18782  * https://github.com/chjj/marked
18783  */
18784
18785
18786 /**
18787  *
18788  * Roo.Markdown - is a very crude wrapper around marked..
18789  *
18790  * usage:
18791  * 
18792  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
18793  * 
18794  * Note: move the sample code to the bottom of this
18795  * file before uncommenting it.
18796  *
18797  */
18798
18799 Roo.Markdown = {};
18800 Roo.Markdown.toHtml = function(text) {
18801     
18802     var c = new Roo.Markdown.marked.setOptions({
18803             renderer: new Roo.Markdown.marked.Renderer(),
18804             gfm: true,
18805             tables: true,
18806             breaks: false,
18807             pedantic: false,
18808             sanitize: false,
18809             smartLists: true,
18810             smartypants: false
18811           });
18812     // A FEW HACKS!!?
18813     
18814     text = text.replace(/\\\n/g,' ');
18815     return Roo.Markdown.marked(text);
18816 };
18817 //
18818 // converter
18819 //
18820 // Wraps all "globals" so that the only thing
18821 // exposed is makeHtml().
18822 //
18823 (function() {
18824     
18825      /**
18826          * eval:var:escape
18827          * eval:var:unescape
18828          * eval:var:replace
18829          */
18830       
18831     /**
18832      * Helpers
18833      */
18834     
18835     var escape = function (html, encode) {
18836       return html
18837         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
18838         .replace(/</g, '&lt;')
18839         .replace(/>/g, '&gt;')
18840         .replace(/"/g, '&quot;')
18841         .replace(/'/g, '&#39;');
18842     }
18843     
18844     var unescape = function (html) {
18845         // explicitly match decimal, hex, and named HTML entities 
18846       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
18847         n = n.toLowerCase();
18848         if (n === 'colon') { return ':'; }
18849         if (n.charAt(0) === '#') {
18850           return n.charAt(1) === 'x'
18851             ? String.fromCharCode(parseInt(n.substring(2), 16))
18852             : String.fromCharCode(+n.substring(1));
18853         }
18854         return '';
18855       });
18856     }
18857     
18858     var replace = function (regex, opt) {
18859       regex = regex.source;
18860       opt = opt || '';
18861       return function self(name, val) {
18862         if (!name) { return new RegExp(regex, opt); }
18863         val = val.source || val;
18864         val = val.replace(/(^|[^\[])\^/g, '$1');
18865         regex = regex.replace(name, val);
18866         return self;
18867       };
18868     }
18869
18870
18871          /**
18872          * eval:var:noop
18873     */
18874     var noop = function () {}
18875     noop.exec = noop;
18876     
18877          /**
18878          * eval:var:merge
18879     */
18880     var merge = function (obj) {
18881       var i = 1
18882         , target
18883         , key;
18884     
18885       for (; i < arguments.length; i++) {
18886         target = arguments[i];
18887         for (key in target) {
18888           if (Object.prototype.hasOwnProperty.call(target, key)) {
18889             obj[key] = target[key];
18890           }
18891         }
18892       }
18893     
18894       return obj;
18895     }
18896     
18897     
18898     /**
18899      * Block-Level Grammar
18900      */
18901     
18902     
18903     
18904     
18905     var block = {
18906       newline: /^\n+/,
18907       code: /^( {4}[^\n]+\n*)+/,
18908       fences: noop,
18909       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
18910       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
18911       nptable: noop,
18912       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
18913       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
18914       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
18915       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
18916       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
18917       table: noop,
18918       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
18919       text: /^[^\n]+/
18920     };
18921     
18922     block.bullet = /(?:[*+-]|\d+\.)/;
18923     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
18924     block.item = replace(block.item, 'gm')
18925       (/bull/g, block.bullet)
18926       ();
18927     
18928     block.list = replace(block.list)
18929       (/bull/g, block.bullet)
18930       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
18931       ('def', '\\n+(?=' + block.def.source + ')')
18932       ();
18933     
18934     block.blockquote = replace(block.blockquote)
18935       ('def', block.def)
18936       ();
18937     
18938     block._tag = '(?!(?:'
18939       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
18940       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
18941       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
18942     
18943     block.html = replace(block.html)
18944       ('comment', /<!--[\s\S]*?-->/)
18945       ('closed', /<(tag)[\s\S]+?<\/\1>/)
18946       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
18947       (/tag/g, block._tag)
18948       ();
18949     
18950     block.paragraph = replace(block.paragraph)
18951       ('hr', block.hr)
18952       ('heading', block.heading)
18953       ('lheading', block.lheading)
18954       ('blockquote', block.blockquote)
18955       ('tag', '<' + block._tag)
18956       ('def', block.def)
18957       ();
18958     
18959     /**
18960      * Normal Block Grammar
18961      */
18962     
18963     block.normal = merge({}, block);
18964     
18965     /**
18966      * GFM Block Grammar
18967      */
18968     
18969     block.gfm = merge({}, block.normal, {
18970       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
18971       paragraph: /^/,
18972       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
18973     });
18974     
18975     block.gfm.paragraph = replace(block.paragraph)
18976       ('(?!', '(?!'
18977         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
18978         + block.list.source.replace('\\1', '\\3') + '|')
18979       ();
18980     
18981     /**
18982      * GFM + Tables Block Grammar
18983      */
18984     
18985     block.tables = merge({}, block.gfm, {
18986       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
18987       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
18988     });
18989     
18990     /**
18991      * Block Lexer
18992      */
18993     
18994     var Lexer = function (options) {
18995       this.tokens = [];
18996       this.tokens.links = {};
18997       this.options = options || marked.defaults;
18998       this.rules = block.normal;
18999     
19000       if (this.options.gfm) {
19001         if (this.options.tables) {
19002           this.rules = block.tables;
19003         } else {
19004           this.rules = block.gfm;
19005         }
19006       }
19007     }
19008     
19009     /**
19010      * Expose Block Rules
19011      */
19012     
19013     Lexer.rules = block;
19014     
19015     /**
19016      * Static Lex Method
19017      */
19018     
19019     Lexer.lex = function(src, options) {
19020       var lexer = new Lexer(options);
19021       return lexer.lex(src);
19022     };
19023     
19024     /**
19025      * Preprocessing
19026      */
19027     
19028     Lexer.prototype.lex = function(src) {
19029       src = src
19030         .replace(/\r\n|\r/g, '\n')
19031         .replace(/\t/g, '    ')
19032         .replace(/\u00a0/g, ' ')
19033         .replace(/\u2424/g, '\n');
19034     
19035       return this.token(src, true);
19036     };
19037     
19038     /**
19039      * Lexing
19040      */
19041     
19042     Lexer.prototype.token = function(src, top, bq) {
19043       var src = src.replace(/^ +$/gm, '')
19044         , next
19045         , loose
19046         , cap
19047         , bull
19048         , b
19049         , item
19050         , space
19051         , i
19052         , l;
19053     
19054       while (src) {
19055         // newline
19056         if (cap = this.rules.newline.exec(src)) {
19057           src = src.substring(cap[0].length);
19058           if (cap[0].length > 1) {
19059             this.tokens.push({
19060               type: 'space'
19061             });
19062           }
19063         }
19064     
19065         // code
19066         if (cap = this.rules.code.exec(src)) {
19067           src = src.substring(cap[0].length);
19068           cap = cap[0].replace(/^ {4}/gm, '');
19069           this.tokens.push({
19070             type: 'code',
19071             text: !this.options.pedantic
19072               ? cap.replace(/\n+$/, '')
19073               : cap
19074           });
19075           continue;
19076         }
19077     
19078         // fences (gfm)
19079         if (cap = this.rules.fences.exec(src)) {
19080           src = src.substring(cap[0].length);
19081           this.tokens.push({
19082             type: 'code',
19083             lang: cap[2],
19084             text: cap[3] || ''
19085           });
19086           continue;
19087         }
19088     
19089         // heading
19090         if (cap = this.rules.heading.exec(src)) {
19091           src = src.substring(cap[0].length);
19092           this.tokens.push({
19093             type: 'heading',
19094             depth: cap[1].length,
19095             text: cap[2]
19096           });
19097           continue;
19098         }
19099     
19100         // table no leading pipe (gfm)
19101         if (top && (cap = this.rules.nptable.exec(src))) {
19102           src = src.substring(cap[0].length);
19103     
19104           item = {
19105             type: 'table',
19106             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
19107             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
19108             cells: cap[3].replace(/\n$/, '').split('\n')
19109           };
19110     
19111           for (i = 0; i < item.align.length; i++) {
19112             if (/^ *-+: *$/.test(item.align[i])) {
19113               item.align[i] = 'right';
19114             } else if (/^ *:-+: *$/.test(item.align[i])) {
19115               item.align[i] = 'center';
19116             } else if (/^ *:-+ *$/.test(item.align[i])) {
19117               item.align[i] = 'left';
19118             } else {
19119               item.align[i] = null;
19120             }
19121           }
19122     
19123           for (i = 0; i < item.cells.length; i++) {
19124             item.cells[i] = item.cells[i].split(/ *\| */);
19125           }
19126     
19127           this.tokens.push(item);
19128     
19129           continue;
19130         }
19131     
19132         // lheading
19133         if (cap = this.rules.lheading.exec(src)) {
19134           src = src.substring(cap[0].length);
19135           this.tokens.push({
19136             type: 'heading',
19137             depth: cap[2] === '=' ? 1 : 2,
19138             text: cap[1]
19139           });
19140           continue;
19141         }
19142     
19143         // hr
19144         if (cap = this.rules.hr.exec(src)) {
19145           src = src.substring(cap[0].length);
19146           this.tokens.push({
19147             type: 'hr'
19148           });
19149           continue;
19150         }
19151     
19152         // blockquote
19153         if (cap = this.rules.blockquote.exec(src)) {
19154           src = src.substring(cap[0].length);
19155     
19156           this.tokens.push({
19157             type: 'blockquote_start'
19158           });
19159     
19160           cap = cap[0].replace(/^ *> ?/gm, '');
19161     
19162           // Pass `top` to keep the current
19163           // "toplevel" state. This is exactly
19164           // how markdown.pl works.
19165           this.token(cap, top, true);
19166     
19167           this.tokens.push({
19168             type: 'blockquote_end'
19169           });
19170     
19171           continue;
19172         }
19173     
19174         // list
19175         if (cap = this.rules.list.exec(src)) {
19176           src = src.substring(cap[0].length);
19177           bull = cap[2];
19178     
19179           this.tokens.push({
19180             type: 'list_start',
19181             ordered: bull.length > 1
19182           });
19183     
19184           // Get each top-level item.
19185           cap = cap[0].match(this.rules.item);
19186     
19187           next = false;
19188           l = cap.length;
19189           i = 0;
19190     
19191           for (; i < l; i++) {
19192             item = cap[i];
19193     
19194             // Remove the list item's bullet
19195             // so it is seen as the next token.
19196             space = item.length;
19197             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
19198     
19199             // Outdent whatever the
19200             // list item contains. Hacky.
19201             if (~item.indexOf('\n ')) {
19202               space -= item.length;
19203               item = !this.options.pedantic
19204                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
19205                 : item.replace(/^ {1,4}/gm, '');
19206             }
19207     
19208             // Determine whether the next list item belongs here.
19209             // Backpedal if it does not belong in this list.
19210             if (this.options.smartLists && i !== l - 1) {
19211               b = block.bullet.exec(cap[i + 1])[0];
19212               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
19213                 src = cap.slice(i + 1).join('\n') + src;
19214                 i = l - 1;
19215               }
19216             }
19217     
19218             // Determine whether item is loose or not.
19219             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
19220             // for discount behavior.
19221             loose = next || /\n\n(?!\s*$)/.test(item);
19222             if (i !== l - 1) {
19223               next = item.charAt(item.length - 1) === '\n';
19224               if (!loose) { loose = next; }
19225             }
19226     
19227             this.tokens.push({
19228               type: loose
19229                 ? 'loose_item_start'
19230                 : 'list_item_start'
19231             });
19232     
19233             // Recurse.
19234             this.token(item, false, bq);
19235     
19236             this.tokens.push({
19237               type: 'list_item_end'
19238             });
19239           }
19240     
19241           this.tokens.push({
19242             type: 'list_end'
19243           });
19244     
19245           continue;
19246         }
19247     
19248         // html
19249         if (cap = this.rules.html.exec(src)) {
19250           src = src.substring(cap[0].length);
19251           this.tokens.push({
19252             type: this.options.sanitize
19253               ? 'paragraph'
19254               : 'html',
19255             pre: !this.options.sanitizer
19256               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
19257             text: cap[0]
19258           });
19259           continue;
19260         }
19261     
19262         // def
19263         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
19264           src = src.substring(cap[0].length);
19265           this.tokens.links[cap[1].toLowerCase()] = {
19266             href: cap[2],
19267             title: cap[3]
19268           };
19269           continue;
19270         }
19271     
19272         // table (gfm)
19273         if (top && (cap = this.rules.table.exec(src))) {
19274           src = src.substring(cap[0].length);
19275     
19276           item = {
19277             type: 'table',
19278             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
19279             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
19280             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
19281           };
19282     
19283           for (i = 0; i < item.align.length; i++) {
19284             if (/^ *-+: *$/.test(item.align[i])) {
19285               item.align[i] = 'right';
19286             } else if (/^ *:-+: *$/.test(item.align[i])) {
19287               item.align[i] = 'center';
19288             } else if (/^ *:-+ *$/.test(item.align[i])) {
19289               item.align[i] = 'left';
19290             } else {
19291               item.align[i] = null;
19292             }
19293           }
19294     
19295           for (i = 0; i < item.cells.length; i++) {
19296             item.cells[i] = item.cells[i]
19297               .replace(/^ *\| *| *\| *$/g, '')
19298               .split(/ *\| */);
19299           }
19300     
19301           this.tokens.push(item);
19302     
19303           continue;
19304         }
19305     
19306         // top-level paragraph
19307         if (top && (cap = this.rules.paragraph.exec(src))) {
19308           src = src.substring(cap[0].length);
19309           this.tokens.push({
19310             type: 'paragraph',
19311             text: cap[1].charAt(cap[1].length - 1) === '\n'
19312               ? cap[1].slice(0, -1)
19313               : cap[1]
19314           });
19315           continue;
19316         }
19317     
19318         // text
19319         if (cap = this.rules.text.exec(src)) {
19320           // Top-level should never reach here.
19321           src = src.substring(cap[0].length);
19322           this.tokens.push({
19323             type: 'text',
19324             text: cap[0]
19325           });
19326           continue;
19327         }
19328     
19329         if (src) {
19330           throw new
19331             Error('Infinite loop on byte: ' + src.charCodeAt(0));
19332         }
19333       }
19334     
19335       return this.tokens;
19336     };
19337     
19338     /**
19339      * Inline-Level Grammar
19340      */
19341     
19342     var inline = {
19343       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
19344       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
19345       url: noop,
19346       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
19347       link: /^!?\[(inside)\]\(href\)/,
19348       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
19349       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
19350       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
19351       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
19352       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
19353       br: /^ {2,}\n(?!\s*$)/,
19354       del: noop,
19355       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
19356     };
19357     
19358     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
19359     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
19360     
19361     inline.link = replace(inline.link)
19362       ('inside', inline._inside)
19363       ('href', inline._href)
19364       ();
19365     
19366     inline.reflink = replace(inline.reflink)
19367       ('inside', inline._inside)
19368       ();
19369     
19370     /**
19371      * Normal Inline Grammar
19372      */
19373     
19374     inline.normal = merge({}, inline);
19375     
19376     /**
19377      * Pedantic Inline Grammar
19378      */
19379     
19380     inline.pedantic = merge({}, inline.normal, {
19381       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
19382       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
19383     });
19384     
19385     /**
19386      * GFM Inline Grammar
19387      */
19388     
19389     inline.gfm = merge({}, inline.normal, {
19390       escape: replace(inline.escape)('])', '~|])')(),
19391       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
19392       del: /^~~(?=\S)([\s\S]*?\S)~~/,
19393       text: replace(inline.text)
19394         (']|', '~]|')
19395         ('|', '|https?://|')
19396         ()
19397     });
19398     
19399     /**
19400      * GFM + Line Breaks Inline Grammar
19401      */
19402     
19403     inline.breaks = merge({}, inline.gfm, {
19404       br: replace(inline.br)('{2,}', '*')(),
19405       text: replace(inline.gfm.text)('{2,}', '*')()
19406     });
19407     
19408     /**
19409      * Inline Lexer & Compiler
19410      */
19411     
19412     var InlineLexer  = function (links, options) {
19413       this.options = options || marked.defaults;
19414       this.links = links;
19415       this.rules = inline.normal;
19416       this.renderer = this.options.renderer || new Renderer;
19417       this.renderer.options = this.options;
19418     
19419       if (!this.links) {
19420         throw new
19421           Error('Tokens array requires a `links` property.');
19422       }
19423     
19424       if (this.options.gfm) {
19425         if (this.options.breaks) {
19426           this.rules = inline.breaks;
19427         } else {
19428           this.rules = inline.gfm;
19429         }
19430       } else if (this.options.pedantic) {
19431         this.rules = inline.pedantic;
19432       }
19433     }
19434     
19435     /**
19436      * Expose Inline Rules
19437      */
19438     
19439     InlineLexer.rules = inline;
19440     
19441     /**
19442      * Static Lexing/Compiling Method
19443      */
19444     
19445     InlineLexer.output = function(src, links, options) {
19446       var inline = new InlineLexer(links, options);
19447       return inline.output(src);
19448     };
19449     
19450     /**
19451      * Lexing/Compiling
19452      */
19453     
19454     InlineLexer.prototype.output = function(src) {
19455       var out = ''
19456         , link
19457         , text
19458         , href
19459         , cap;
19460     
19461       while (src) {
19462         // escape
19463         if (cap = this.rules.escape.exec(src)) {
19464           src = src.substring(cap[0].length);
19465           out += cap[1];
19466           continue;
19467         }
19468     
19469         // autolink
19470         if (cap = this.rules.autolink.exec(src)) {
19471           src = src.substring(cap[0].length);
19472           if (cap[2] === '@') {
19473             text = cap[1].charAt(6) === ':'
19474               ? this.mangle(cap[1].substring(7))
19475               : this.mangle(cap[1]);
19476             href = this.mangle('mailto:') + text;
19477           } else {
19478             text = escape(cap[1]);
19479             href = text;
19480           }
19481           out += this.renderer.link(href, null, text);
19482           continue;
19483         }
19484     
19485         // url (gfm)
19486         if (!this.inLink && (cap = this.rules.url.exec(src))) {
19487           src = src.substring(cap[0].length);
19488           text = escape(cap[1]);
19489           href = text;
19490           out += this.renderer.link(href, null, text);
19491           continue;
19492         }
19493     
19494         // tag
19495         if (cap = this.rules.tag.exec(src)) {
19496           if (!this.inLink && /^<a /i.test(cap[0])) {
19497             this.inLink = true;
19498           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
19499             this.inLink = false;
19500           }
19501           src = src.substring(cap[0].length);
19502           out += this.options.sanitize
19503             ? this.options.sanitizer
19504               ? this.options.sanitizer(cap[0])
19505               : escape(cap[0])
19506             : cap[0];
19507           continue;
19508         }
19509     
19510         // link
19511         if (cap = this.rules.link.exec(src)) {
19512           src = src.substring(cap[0].length);
19513           this.inLink = true;
19514           out += this.outputLink(cap, {
19515             href: cap[2],
19516             title: cap[3]
19517           });
19518           this.inLink = false;
19519           continue;
19520         }
19521     
19522         // reflink, nolink
19523         if ((cap = this.rules.reflink.exec(src))
19524             || (cap = this.rules.nolink.exec(src))) {
19525           src = src.substring(cap[0].length);
19526           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
19527           link = this.links[link.toLowerCase()];
19528           if (!link || !link.href) {
19529             out += cap[0].charAt(0);
19530             src = cap[0].substring(1) + src;
19531             continue;
19532           }
19533           this.inLink = true;
19534           out += this.outputLink(cap, link);
19535           this.inLink = false;
19536           continue;
19537         }
19538     
19539         // strong
19540         if (cap = this.rules.strong.exec(src)) {
19541           src = src.substring(cap[0].length);
19542           out += this.renderer.strong(this.output(cap[2] || cap[1]));
19543           continue;
19544         }
19545     
19546         // em
19547         if (cap = this.rules.em.exec(src)) {
19548           src = src.substring(cap[0].length);
19549           out += this.renderer.em(this.output(cap[2] || cap[1]));
19550           continue;
19551         }
19552     
19553         // code
19554         if (cap = this.rules.code.exec(src)) {
19555           src = src.substring(cap[0].length);
19556           out += this.renderer.codespan(escape(cap[2], true));
19557           continue;
19558         }
19559     
19560         // br
19561         if (cap = this.rules.br.exec(src)) {
19562           src = src.substring(cap[0].length);
19563           out += this.renderer.br();
19564           continue;
19565         }
19566     
19567         // del (gfm)
19568         if (cap = this.rules.del.exec(src)) {
19569           src = src.substring(cap[0].length);
19570           out += this.renderer.del(this.output(cap[1]));
19571           continue;
19572         }
19573     
19574         // text
19575         if (cap = this.rules.text.exec(src)) {
19576           src = src.substring(cap[0].length);
19577           out += this.renderer.text(escape(this.smartypants(cap[0])));
19578           continue;
19579         }
19580     
19581         if (src) {
19582           throw new
19583             Error('Infinite loop on byte: ' + src.charCodeAt(0));
19584         }
19585       }
19586     
19587       return out;
19588     };
19589     
19590     /**
19591      * Compile Link
19592      */
19593     
19594     InlineLexer.prototype.outputLink = function(cap, link) {
19595       var href = escape(link.href)
19596         , title = link.title ? escape(link.title) : null;
19597     
19598       return cap[0].charAt(0) !== '!'
19599         ? this.renderer.link(href, title, this.output(cap[1]))
19600         : this.renderer.image(href, title, escape(cap[1]));
19601     };
19602     
19603     /**
19604      * Smartypants Transformations
19605      */
19606     
19607     InlineLexer.prototype.smartypants = function(text) {
19608       if (!this.options.smartypants)  { return text; }
19609       return text
19610         // em-dashes
19611         .replace(/---/g, '\u2014')
19612         // en-dashes
19613         .replace(/--/g, '\u2013')
19614         // opening singles
19615         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
19616         // closing singles & apostrophes
19617         .replace(/'/g, '\u2019')
19618         // opening doubles
19619         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
19620         // closing doubles
19621         .replace(/"/g, '\u201d')
19622         // ellipses
19623         .replace(/\.{3}/g, '\u2026');
19624     };
19625     
19626     /**
19627      * Mangle Links
19628      */
19629     
19630     InlineLexer.prototype.mangle = function(text) {
19631       if (!this.options.mangle) { return text; }
19632       var out = ''
19633         , l = text.length
19634         , i = 0
19635         , ch;
19636     
19637       for (; i < l; i++) {
19638         ch = text.charCodeAt(i);
19639         if (Math.random() > 0.5) {
19640           ch = 'x' + ch.toString(16);
19641         }
19642         out += '&#' + ch + ';';
19643       }
19644     
19645       return out;
19646     };
19647     
19648     /**
19649      * Renderer
19650      */
19651     
19652      /**
19653          * eval:var:Renderer
19654     */
19655     
19656     var Renderer   = function (options) {
19657       this.options = options || {};
19658     }
19659     
19660     Renderer.prototype.code = function(code, lang, escaped) {
19661       if (this.options.highlight) {
19662         var out = this.options.highlight(code, lang);
19663         if (out != null && out !== code) {
19664           escaped = true;
19665           code = out;
19666         }
19667       } else {
19668             // hack!!! - it's already escapeD?
19669             escaped = true;
19670       }
19671     
19672       if (!lang) {
19673         return '<pre><code>'
19674           + (escaped ? code : escape(code, true))
19675           + '\n</code></pre>';
19676       }
19677     
19678       return '<pre><code class="'
19679         + this.options.langPrefix
19680         + escape(lang, true)
19681         + '">'
19682         + (escaped ? code : escape(code, true))
19683         + '\n</code></pre>\n';
19684     };
19685     
19686     Renderer.prototype.blockquote = function(quote) {
19687       return '<blockquote>\n' + quote + '</blockquote>\n';
19688     };
19689     
19690     Renderer.prototype.html = function(html) {
19691       return html;
19692     };
19693     
19694     Renderer.prototype.heading = function(text, level, raw) {
19695       return '<h'
19696         + level
19697         + ' id="'
19698         + this.options.headerPrefix
19699         + raw.toLowerCase().replace(/[^\w]+/g, '-')
19700         + '">'
19701         + text
19702         + '</h'
19703         + level
19704         + '>\n';
19705     };
19706     
19707     Renderer.prototype.hr = function() {
19708       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
19709     };
19710     
19711     Renderer.prototype.list = function(body, ordered) {
19712       var type = ordered ? 'ol' : 'ul';
19713       return '<' + type + '>\n' + body + '</' + type + '>\n';
19714     };
19715     
19716     Renderer.prototype.listitem = function(text) {
19717       return '<li>' + text + '</li>\n';
19718     };
19719     
19720     Renderer.prototype.paragraph = function(text) {
19721       return '<p>' + text + '</p>\n';
19722     };
19723     
19724     Renderer.prototype.table = function(header, body) {
19725       return '<table class="table table-striped">\n'
19726         + '<thead>\n'
19727         + header
19728         + '</thead>\n'
19729         + '<tbody>\n'
19730         + body
19731         + '</tbody>\n'
19732         + '</table>\n';
19733     };
19734     
19735     Renderer.prototype.tablerow = function(content) {
19736       return '<tr>\n' + content + '</tr>\n';
19737     };
19738     
19739     Renderer.prototype.tablecell = function(content, flags) {
19740       var type = flags.header ? 'th' : 'td';
19741       var tag = flags.align
19742         ? '<' + type + ' style="text-align:' + flags.align + '">'
19743         : '<' + type + '>';
19744       return tag + content + '</' + type + '>\n';
19745     };
19746     
19747     // span level renderer
19748     Renderer.prototype.strong = function(text) {
19749       return '<strong>' + text + '</strong>';
19750     };
19751     
19752     Renderer.prototype.em = function(text) {
19753       return '<em>' + text + '</em>';
19754     };
19755     
19756     Renderer.prototype.codespan = function(text) {
19757       return '<code>' + text + '</code>';
19758     };
19759     
19760     Renderer.prototype.br = function() {
19761       return this.options.xhtml ? '<br/>' : '<br>';
19762     };
19763     
19764     Renderer.prototype.del = function(text) {
19765       return '<del>' + text + '</del>';
19766     };
19767     
19768     Renderer.prototype.link = function(href, title, text) {
19769       if (this.options.sanitize) {
19770         try {
19771           var prot = decodeURIComponent(unescape(href))
19772             .replace(/[^\w:]/g, '')
19773             .toLowerCase();
19774         } catch (e) {
19775           return '';
19776         }
19777         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
19778           return '';
19779         }
19780       }
19781       var out = '<a href="' + href + '"';
19782       if (title) {
19783         out += ' title="' + title + '"';
19784       }
19785       out += '>' + text + '</a>';
19786       return out;
19787     };
19788     
19789     Renderer.prototype.image = function(href, title, text) {
19790       var out = '<img src="' + href + '" alt="' + text + '"';
19791       if (title) {
19792         out += ' title="' + title + '"';
19793       }
19794       out += this.options.xhtml ? '/>' : '>';
19795       return out;
19796     };
19797     
19798     Renderer.prototype.text = function(text) {
19799       return text;
19800     };
19801     
19802     /**
19803      * Parsing & Compiling
19804      */
19805          /**
19806          * eval:var:Parser
19807     */
19808     
19809     var Parser= function (options) {
19810       this.tokens = [];
19811       this.token = null;
19812       this.options = options || marked.defaults;
19813       this.options.renderer = this.options.renderer || new Renderer;
19814       this.renderer = this.options.renderer;
19815       this.renderer.options = this.options;
19816     }
19817     
19818     /**
19819      * Static Parse Method
19820      */
19821     
19822     Parser.parse = function(src, options, renderer) {
19823       var parser = new Parser(options, renderer);
19824       return parser.parse(src);
19825     };
19826     
19827     /**
19828      * Parse Loop
19829      */
19830     
19831     Parser.prototype.parse = function(src) {
19832       this.inline = new InlineLexer(src.links, this.options, this.renderer);
19833       this.tokens = src.reverse();
19834     
19835       var out = '';
19836       while (this.next()) {
19837         out += this.tok();
19838       }
19839     
19840       return out;
19841     };
19842     
19843     /**
19844      * Next Token
19845      */
19846     
19847     Parser.prototype.next = function() {
19848       return this.token = this.tokens.pop();
19849     };
19850     
19851     /**
19852      * Preview Next Token
19853      */
19854     
19855     Parser.prototype.peek = function() {
19856       return this.tokens[this.tokens.length - 1] || 0;
19857     };
19858     
19859     /**
19860      * Parse Text Tokens
19861      */
19862     
19863     Parser.prototype.parseText = function() {
19864       var body = this.token.text;
19865     
19866       while (this.peek().type === 'text') {
19867         body += '\n' + this.next().text;
19868       }
19869     
19870       return this.inline.output(body);
19871     };
19872     
19873     /**
19874      * Parse Current Token
19875      */
19876     
19877     Parser.prototype.tok = function() {
19878       switch (this.token.type) {
19879         case 'space': {
19880           return '';
19881         }
19882         case 'hr': {
19883           return this.renderer.hr();
19884         }
19885         case 'heading': {
19886           return this.renderer.heading(
19887             this.inline.output(this.token.text),
19888             this.token.depth,
19889             this.token.text);
19890         }
19891         case 'code': {
19892           return this.renderer.code(this.token.text,
19893             this.token.lang,
19894             this.token.escaped);
19895         }
19896         case 'table': {
19897           var header = ''
19898             , body = ''
19899             , i
19900             , row
19901             , cell
19902             , flags
19903             , j;
19904     
19905           // header
19906           cell = '';
19907           for (i = 0; i < this.token.header.length; i++) {
19908             flags = { header: true, align: this.token.align[i] };
19909             cell += this.renderer.tablecell(
19910               this.inline.output(this.token.header[i]),
19911               { header: true, align: this.token.align[i] }
19912             );
19913           }
19914           header += this.renderer.tablerow(cell);
19915     
19916           for (i = 0; i < this.token.cells.length; i++) {
19917             row = this.token.cells[i];
19918     
19919             cell = '';
19920             for (j = 0; j < row.length; j++) {
19921               cell += this.renderer.tablecell(
19922                 this.inline.output(row[j]),
19923                 { header: false, align: this.token.align[j] }
19924               );
19925             }
19926     
19927             body += this.renderer.tablerow(cell);
19928           }
19929           return this.renderer.table(header, body);
19930         }
19931         case 'blockquote_start': {
19932           var body = '';
19933     
19934           while (this.next().type !== 'blockquote_end') {
19935             body += this.tok();
19936           }
19937     
19938           return this.renderer.blockquote(body);
19939         }
19940         case 'list_start': {
19941           var body = ''
19942             , ordered = this.token.ordered;
19943     
19944           while (this.next().type !== 'list_end') {
19945             body += this.tok();
19946           }
19947     
19948           return this.renderer.list(body, ordered);
19949         }
19950         case 'list_item_start': {
19951           var body = '';
19952     
19953           while (this.next().type !== 'list_item_end') {
19954             body += this.token.type === 'text'
19955               ? this.parseText()
19956               : this.tok();
19957           }
19958     
19959           return this.renderer.listitem(body);
19960         }
19961         case 'loose_item_start': {
19962           var body = '';
19963     
19964           while (this.next().type !== 'list_item_end') {
19965             body += this.tok();
19966           }
19967     
19968           return this.renderer.listitem(body);
19969         }
19970         case 'html': {
19971           var html = !this.token.pre && !this.options.pedantic
19972             ? this.inline.output(this.token.text)
19973             : this.token.text;
19974           return this.renderer.html(html);
19975         }
19976         case 'paragraph': {
19977           return this.renderer.paragraph(this.inline.output(this.token.text));
19978         }
19979         case 'text': {
19980           return this.renderer.paragraph(this.parseText());
19981         }
19982       }
19983     };
19984   
19985     
19986     /**
19987      * Marked
19988      */
19989          /**
19990          * eval:var:marked
19991     */
19992     var marked = function (src, opt, callback) {
19993       if (callback || typeof opt === 'function') {
19994         if (!callback) {
19995           callback = opt;
19996           opt = null;
19997         }
19998     
19999         opt = merge({}, marked.defaults, opt || {});
20000     
20001         var highlight = opt.highlight
20002           , tokens
20003           , pending
20004           , i = 0;
20005     
20006         try {
20007           tokens = Lexer.lex(src, opt)
20008         } catch (e) {
20009           return callback(e);
20010         }
20011     
20012         pending = tokens.length;
20013          /**
20014          * eval:var:done
20015     */
20016         var done = function(err) {
20017           if (err) {
20018             opt.highlight = highlight;
20019             return callback(err);
20020           }
20021     
20022           var out;
20023     
20024           try {
20025             out = Parser.parse(tokens, opt);
20026           } catch (e) {
20027             err = e;
20028           }
20029     
20030           opt.highlight = highlight;
20031     
20032           return err
20033             ? callback(err)
20034             : callback(null, out);
20035         };
20036     
20037         if (!highlight || highlight.length < 3) {
20038           return done();
20039         }
20040     
20041         delete opt.highlight;
20042     
20043         if (!pending) { return done(); }
20044     
20045         for (; i < tokens.length; i++) {
20046           (function(token) {
20047             if (token.type !== 'code') {
20048               return --pending || done();
20049             }
20050             return highlight(token.text, token.lang, function(err, code) {
20051               if (err) { return done(err); }
20052               if (code == null || code === token.text) {
20053                 return --pending || done();
20054               }
20055               token.text = code;
20056               token.escaped = true;
20057               --pending || done();
20058             });
20059           })(tokens[i]);
20060         }
20061     
20062         return;
20063       }
20064       try {
20065         if (opt) { opt = merge({}, marked.defaults, opt); }
20066         return Parser.parse(Lexer.lex(src, opt), opt);
20067       } catch (e) {
20068         e.message += '\nPlease report this to https://github.com/chjj/marked.';
20069         if ((opt || marked.defaults).silent) {
20070           return '<p>An error occured:</p><pre>'
20071             + escape(e.message + '', true)
20072             + '</pre>';
20073         }
20074         throw e;
20075       }
20076     }
20077     
20078     /**
20079      * Options
20080      */
20081     
20082     marked.options =
20083     marked.setOptions = function(opt) {
20084       merge(marked.defaults, opt);
20085       return marked;
20086     };
20087     
20088     marked.defaults = {
20089       gfm: true,
20090       tables: true,
20091       breaks: false,
20092       pedantic: false,
20093       sanitize: false,
20094       sanitizer: null,
20095       mangle: true,
20096       smartLists: false,
20097       silent: false,
20098       highlight: null,
20099       langPrefix: 'lang-',
20100       smartypants: false,
20101       headerPrefix: '',
20102       renderer: new Renderer,
20103       xhtml: false
20104     };
20105     
20106     /**
20107      * Expose
20108      */
20109     
20110     marked.Parser = Parser;
20111     marked.parser = Parser.parse;
20112     
20113     marked.Renderer = Renderer;
20114     
20115     marked.Lexer = Lexer;
20116     marked.lexer = Lexer.lex;
20117     
20118     marked.InlineLexer = InlineLexer;
20119     marked.inlineLexer = InlineLexer.output;
20120     
20121     marked.parse = marked;
20122     
20123     Roo.Markdown.marked = marked;
20124
20125 })();/*
20126  * Based on:
20127  * Ext JS Library 1.1.1
20128  * Copyright(c) 2006-2007, Ext JS, LLC.
20129  *
20130  * Originally Released Under LGPL - original licence link has changed is not relivant.
20131  *
20132  * Fork - LGPL
20133  * <script type="text/javascript">
20134  */
20135
20136
20137
20138 /*
20139  * These classes are derivatives of the similarly named classes in the YUI Library.
20140  * The original license:
20141  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
20142  * Code licensed under the BSD License:
20143  * http://developer.yahoo.net/yui/license.txt
20144  */
20145
20146 (function() {
20147
20148 var Event=Roo.EventManager;
20149 var Dom=Roo.lib.Dom;
20150
20151 /**
20152  * @class Roo.dd.DragDrop
20153  * @extends Roo.util.Observable
20154  * Defines the interface and base operation of items that that can be
20155  * dragged or can be drop targets.  It was designed to be extended, overriding
20156  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
20157  * Up to three html elements can be associated with a DragDrop instance:
20158  * <ul>
20159  * <li>linked element: the element that is passed into the constructor.
20160  * This is the element which defines the boundaries for interaction with
20161  * other DragDrop objects.</li>
20162  * <li>handle element(s): The drag operation only occurs if the element that
20163  * was clicked matches a handle element.  By default this is the linked
20164  * element, but there are times that you will want only a portion of the
20165  * linked element to initiate the drag operation, and the setHandleElId()
20166  * method provides a way to define this.</li>
20167  * <li>drag element: this represents the element that would be moved along
20168  * with the cursor during a drag operation.  By default, this is the linked
20169  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
20170  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
20171  * </li>
20172  * </ul>
20173  * This class should not be instantiated until the onload event to ensure that
20174  * the associated elements are available.
20175  * The following would define a DragDrop obj that would interact with any
20176  * other DragDrop obj in the "group1" group:
20177  * <pre>
20178  *  dd = new Roo.dd.DragDrop("div1", "group1");
20179  * </pre>
20180  * Since none of the event handlers have been implemented, nothing would
20181  * actually happen if you were to run the code above.  Normally you would
20182  * override this class or one of the default implementations, but you can
20183  * also override the methods you want on an instance of the class...
20184  * <pre>
20185  *  dd.onDragDrop = function(e, id) {
20186  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
20187  *  }
20188  * </pre>
20189  * @constructor
20190  * @param {String} id of the element that is linked to this instance
20191  * @param {String} sGroup the group of related DragDrop objects
20192  * @param {object} config an object containing configurable attributes
20193  *                Valid properties for DragDrop:
20194  *                    padding, isTarget, maintainOffset, primaryButtonOnly
20195  */
20196 Roo.dd.DragDrop = function(id, sGroup, config) {
20197     if (id) {
20198         this.init(id, sGroup, config);
20199     }
20200     
20201 };
20202
20203 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
20204
20205     /**
20206      * The id of the element associated with this object.  This is what we
20207      * refer to as the "linked element" because the size and position of
20208      * this element is used to determine when the drag and drop objects have
20209      * interacted.
20210      * @property id
20211      * @type String
20212      */
20213     id: null,
20214
20215     /**
20216      * Configuration attributes passed into the constructor
20217      * @property config
20218      * @type object
20219      */
20220     config: null,
20221
20222     /**
20223      * The id of the element that will be dragged.  By default this is same
20224      * as the linked element , but could be changed to another element. Ex:
20225      * Roo.dd.DDProxy
20226      * @property dragElId
20227      * @type String
20228      * @private
20229      */
20230     dragElId: null,
20231
20232     /**
20233      * the id of the element that initiates the drag operation.  By default
20234      * this is the linked element, but could be changed to be a child of this
20235      * element.  This lets us do things like only starting the drag when the
20236      * header element within the linked html element is clicked.
20237      * @property handleElId
20238      * @type String
20239      * @private
20240      */
20241     handleElId: null,
20242
20243     /**
20244      * An associative array of HTML tags that will be ignored if clicked.
20245      * @property invalidHandleTypes
20246      * @type {string: string}
20247      */
20248     invalidHandleTypes: null,
20249
20250     /**
20251      * An associative array of ids for elements that will be ignored if clicked
20252      * @property invalidHandleIds
20253      * @type {string: string}
20254      */
20255     invalidHandleIds: null,
20256
20257     /**
20258      * An indexted array of css class names for elements that will be ignored
20259      * if clicked.
20260      * @property invalidHandleClasses
20261      * @type string[]
20262      */
20263     invalidHandleClasses: null,
20264
20265     /**
20266      * The linked element's absolute X position at the time the drag was
20267      * started
20268      * @property startPageX
20269      * @type int
20270      * @private
20271      */
20272     startPageX: 0,
20273
20274     /**
20275      * The linked element's absolute X position at the time the drag was
20276      * started
20277      * @property startPageY
20278      * @type int
20279      * @private
20280      */
20281     startPageY: 0,
20282
20283     /**
20284      * The group defines a logical collection of DragDrop objects that are
20285      * related.  Instances only get events when interacting with other
20286      * DragDrop object in the same group.  This lets us define multiple
20287      * groups using a single DragDrop subclass if we want.
20288      * @property groups
20289      * @type {string: string}
20290      */
20291     groups: null,
20292
20293     /**
20294      * Individual drag/drop instances can be locked.  This will prevent
20295      * onmousedown start drag.
20296      * @property locked
20297      * @type boolean
20298      * @private
20299      */
20300     locked: false,
20301
20302     /**
20303      * Lock this instance
20304      * @method lock
20305      */
20306     lock: function() { this.locked = true; },
20307
20308     /**
20309      * Unlock this instace
20310      * @method unlock
20311      */
20312     unlock: function() { this.locked = false; },
20313
20314     /**
20315      * By default, all insances can be a drop target.  This can be disabled by
20316      * setting isTarget to false.
20317      * @method isTarget
20318      * @type boolean
20319      */
20320     isTarget: true,
20321
20322     /**
20323      * The padding configured for this drag and drop object for calculating
20324      * the drop zone intersection with this object.
20325      * @method padding
20326      * @type int[]
20327      */
20328     padding: null,
20329
20330     /**
20331      * Cached reference to the linked element
20332      * @property _domRef
20333      * @private
20334      */
20335     _domRef: null,
20336
20337     /**
20338      * Internal typeof flag
20339      * @property __ygDragDrop
20340      * @private
20341      */
20342     __ygDragDrop: true,
20343
20344     /**
20345      * Set to true when horizontal contraints are applied
20346      * @property constrainX
20347      * @type boolean
20348      * @private
20349      */
20350     constrainX: false,
20351
20352     /**
20353      * Set to true when vertical contraints are applied
20354      * @property constrainY
20355      * @type boolean
20356      * @private
20357      */
20358     constrainY: false,
20359
20360     /**
20361      * The left constraint
20362      * @property minX
20363      * @type int
20364      * @private
20365      */
20366     minX: 0,
20367
20368     /**
20369      * The right constraint
20370      * @property maxX
20371      * @type int
20372      * @private
20373      */
20374     maxX: 0,
20375
20376     /**
20377      * The up constraint
20378      * @property minY
20379      * @type int
20380      * @type int
20381      * @private
20382      */
20383     minY: 0,
20384
20385     /**
20386      * The down constraint
20387      * @property maxY
20388      * @type int
20389      * @private
20390      */
20391     maxY: 0,
20392
20393     /**
20394      * Maintain offsets when we resetconstraints.  Set to true when you want
20395      * the position of the element relative to its parent to stay the same
20396      * when the page changes
20397      *
20398      * @property maintainOffset
20399      * @type boolean
20400      */
20401     maintainOffset: false,
20402
20403     /**
20404      * Array of pixel locations the element will snap to if we specified a
20405      * horizontal graduation/interval.  This array is generated automatically
20406      * when you define a tick interval.
20407      * @property xTicks
20408      * @type int[]
20409      */
20410     xTicks: null,
20411
20412     /**
20413      * Array of pixel locations the element will snap to if we specified a
20414      * vertical graduation/interval.  This array is generated automatically
20415      * when you define a tick interval.
20416      * @property yTicks
20417      * @type int[]
20418      */
20419     yTicks: null,
20420
20421     /**
20422      * By default the drag and drop instance will only respond to the primary
20423      * button click (left button for a right-handed mouse).  Set to true to
20424      * allow drag and drop to start with any mouse click that is propogated
20425      * by the browser
20426      * @property primaryButtonOnly
20427      * @type boolean
20428      */
20429     primaryButtonOnly: true,
20430
20431     /**
20432      * The availabe property is false until the linked dom element is accessible.
20433      * @property available
20434      * @type boolean
20435      */
20436     available: false,
20437
20438     /**
20439      * By default, drags can only be initiated if the mousedown occurs in the
20440      * region the linked element is.  This is done in part to work around a
20441      * bug in some browsers that mis-report the mousedown if the previous
20442      * mouseup happened outside of the window.  This property is set to true
20443      * if outer handles are defined.
20444      *
20445      * @property hasOuterHandles
20446      * @type boolean
20447      * @default false
20448      */
20449     hasOuterHandles: false,
20450
20451     /**
20452      * Code that executes immediately before the startDrag event
20453      * @method b4StartDrag
20454      * @private
20455      */
20456     b4StartDrag: function(x, y) { },
20457
20458     /**
20459      * Abstract method called after a drag/drop object is clicked
20460      * and the drag or mousedown time thresholds have beeen met.
20461      * @method startDrag
20462      * @param {int} X click location
20463      * @param {int} Y click location
20464      */
20465     startDrag: function(x, y) { /* override this */ },
20466
20467     /**
20468      * Code that executes immediately before the onDrag event
20469      * @method b4Drag
20470      * @private
20471      */
20472     b4Drag: function(e) { },
20473
20474     /**
20475      * Abstract method called during the onMouseMove event while dragging an
20476      * object.
20477      * @method onDrag
20478      * @param {Event} e the mousemove event
20479      */
20480     onDrag: function(e) { /* override this */ },
20481
20482     /**
20483      * Abstract method called when this element fist begins hovering over
20484      * another DragDrop obj
20485      * @method onDragEnter
20486      * @param {Event} e the mousemove event
20487      * @param {String|DragDrop[]} id In POINT mode, the element
20488      * id this is hovering over.  In INTERSECT mode, an array of one or more
20489      * dragdrop items being hovered over.
20490      */
20491     onDragEnter: function(e, id) { /* override this */ },
20492
20493     /**
20494      * Code that executes immediately before the onDragOver event
20495      * @method b4DragOver
20496      * @private
20497      */
20498     b4DragOver: function(e) { },
20499
20500     /**
20501      * Abstract method called when this element is hovering over another
20502      * DragDrop obj
20503      * @method onDragOver
20504      * @param {Event} e the mousemove event
20505      * @param {String|DragDrop[]} id In POINT mode, the element
20506      * id this is hovering over.  In INTERSECT mode, an array of dd items
20507      * being hovered over.
20508      */
20509     onDragOver: function(e, id) { /* override this */ },
20510
20511     /**
20512      * Code that executes immediately before the onDragOut event
20513      * @method b4DragOut
20514      * @private
20515      */
20516     b4DragOut: function(e) { },
20517
20518     /**
20519      * Abstract method called when we are no longer hovering over an element
20520      * @method onDragOut
20521      * @param {Event} e the mousemove event
20522      * @param {String|DragDrop[]} id In POINT mode, the element
20523      * id this was hovering over.  In INTERSECT mode, an array of dd items
20524      * that the mouse is no longer over.
20525      */
20526     onDragOut: function(e, id) { /* override this */ },
20527
20528     /**
20529      * Code that executes immediately before the onDragDrop event
20530      * @method b4DragDrop
20531      * @private
20532      */
20533     b4DragDrop: function(e) { },
20534
20535     /**
20536      * Abstract method called when this item is dropped on another DragDrop
20537      * obj
20538      * @method onDragDrop
20539      * @param {Event} e the mouseup event
20540      * @param {String|DragDrop[]} id In POINT mode, the element
20541      * id this was dropped on.  In INTERSECT mode, an array of dd items this
20542      * was dropped on.
20543      */
20544     onDragDrop: function(e, id) { /* override this */ },
20545
20546     /**
20547      * Abstract method called when this item is dropped on an area with no
20548      * drop target
20549      * @method onInvalidDrop
20550      * @param {Event} e the mouseup event
20551      */
20552     onInvalidDrop: function(e) { /* override this */ },
20553
20554     /**
20555      * Code that executes immediately before the endDrag event
20556      * @method b4EndDrag
20557      * @private
20558      */
20559     b4EndDrag: function(e) { },
20560
20561     /**
20562      * Fired when we are done dragging the object
20563      * @method endDrag
20564      * @param {Event} e the mouseup event
20565      */
20566     endDrag: function(e) { /* override this */ },
20567
20568     /**
20569      * Code executed immediately before the onMouseDown event
20570      * @method b4MouseDown
20571      * @param {Event} e the mousedown event
20572      * @private
20573      */
20574     b4MouseDown: function(e) {  },
20575
20576     /**
20577      * Event handler that fires when a drag/drop obj gets a mousedown
20578      * @method onMouseDown
20579      * @param {Event} e the mousedown event
20580      */
20581     onMouseDown: function(e) { /* override this */ },
20582
20583     /**
20584      * Event handler that fires when a drag/drop obj gets a mouseup
20585      * @method onMouseUp
20586      * @param {Event} e the mouseup event
20587      */
20588     onMouseUp: function(e) { /* override this */ },
20589
20590     /**
20591      * Override the onAvailable method to do what is needed after the initial
20592      * position was determined.
20593      * @method onAvailable
20594      */
20595     onAvailable: function () {
20596     },
20597
20598     /*
20599      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
20600      * @type Object
20601      */
20602     defaultPadding : {left:0, right:0, top:0, bottom:0},
20603
20604     /*
20605      * Initializes the drag drop object's constraints to restrict movement to a certain element.
20606  *
20607  * Usage:
20608  <pre><code>
20609  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
20610                 { dragElId: "existingProxyDiv" });
20611  dd.startDrag = function(){
20612      this.constrainTo("parent-id");
20613  };
20614  </code></pre>
20615  * Or you can initalize it using the {@link Roo.Element} object:
20616  <pre><code>
20617  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
20618      startDrag : function(){
20619          this.constrainTo("parent-id");
20620      }
20621  });
20622  </code></pre>
20623      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
20624      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
20625      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
20626      * an object containing the sides to pad. For example: {right:10, bottom:10}
20627      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
20628      */
20629     constrainTo : function(constrainTo, pad, inContent){
20630         if(typeof pad == "number"){
20631             pad = {left: pad, right:pad, top:pad, bottom:pad};
20632         }
20633         pad = pad || this.defaultPadding;
20634         var b = Roo.get(this.getEl()).getBox();
20635         var ce = Roo.get(constrainTo);
20636         var s = ce.getScroll();
20637         var c, cd = ce.dom;
20638         if(cd == document.body){
20639             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
20640         }else{
20641             xy = ce.getXY();
20642             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
20643         }
20644
20645
20646         var topSpace = b.y - c.y;
20647         var leftSpace = b.x - c.x;
20648
20649         this.resetConstraints();
20650         this.setXConstraint(leftSpace - (pad.left||0), // left
20651                 c.width - leftSpace - b.width - (pad.right||0) //right
20652         );
20653         this.setYConstraint(topSpace - (pad.top||0), //top
20654                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
20655         );
20656     },
20657
20658     /**
20659      * Returns a reference to the linked element
20660      * @method getEl
20661      * @return {HTMLElement} the html element
20662      */
20663     getEl: function() {
20664         if (!this._domRef) {
20665             this._domRef = Roo.getDom(this.id);
20666         }
20667
20668         return this._domRef;
20669     },
20670
20671     /**
20672      * Returns a reference to the actual element to drag.  By default this is
20673      * the same as the html element, but it can be assigned to another
20674      * element. An example of this can be found in Roo.dd.DDProxy
20675      * @method getDragEl
20676      * @return {HTMLElement} the html element
20677      */
20678     getDragEl: function() {
20679         return Roo.getDom(this.dragElId);
20680     },
20681
20682     /**
20683      * Sets up the DragDrop object.  Must be called in the constructor of any
20684      * Roo.dd.DragDrop subclass
20685      * @method init
20686      * @param id the id of the linked element
20687      * @param {String} sGroup the group of related items
20688      * @param {object} config configuration attributes
20689      */
20690     init: function(id, sGroup, config) {
20691         this.initTarget(id, sGroup, config);
20692         if (!Roo.isTouch) {
20693             Event.on(this.id, "mousedown", this.handleMouseDown, this);
20694         }
20695         Event.on(this.id, "touchstart", this.handleMouseDown, this);
20696         // Event.on(this.id, "selectstart", Event.preventDefault);
20697     },
20698
20699     /**
20700      * Initializes Targeting functionality only... the object does not
20701      * get a mousedown handler.
20702      * @method initTarget
20703      * @param id the id of the linked element
20704      * @param {String} sGroup the group of related items
20705      * @param {object} config configuration attributes
20706      */
20707     initTarget: function(id, sGroup, config) {
20708
20709         // configuration attributes
20710         this.config = config || {};
20711
20712         // create a local reference to the drag and drop manager
20713         this.DDM = Roo.dd.DDM;
20714         // initialize the groups array
20715         this.groups = {};
20716
20717         // assume that we have an element reference instead of an id if the
20718         // parameter is not a string
20719         if (typeof id !== "string") {
20720             id = Roo.id(id);
20721         }
20722
20723         // set the id
20724         this.id = id;
20725
20726         // add to an interaction group
20727         this.addToGroup((sGroup) ? sGroup : "default");
20728
20729         // We don't want to register this as the handle with the manager
20730         // so we just set the id rather than calling the setter.
20731         this.handleElId = id;
20732
20733         // the linked element is the element that gets dragged by default
20734         this.setDragElId(id);
20735
20736         // by default, clicked anchors will not start drag operations.
20737         this.invalidHandleTypes = { A: "A" };
20738         this.invalidHandleIds = {};
20739         this.invalidHandleClasses = [];
20740
20741         this.applyConfig();
20742
20743         this.handleOnAvailable();
20744     },
20745
20746     /**
20747      * Applies the configuration parameters that were passed into the constructor.
20748      * This is supposed to happen at each level through the inheritance chain.  So
20749      * a DDProxy implentation will execute apply config on DDProxy, DD, and
20750      * DragDrop in order to get all of the parameters that are available in
20751      * each object.
20752      * @method applyConfig
20753      */
20754     applyConfig: function() {
20755
20756         // configurable properties:
20757         //    padding, isTarget, maintainOffset, primaryButtonOnly
20758         this.padding           = this.config.padding || [0, 0, 0, 0];
20759         this.isTarget          = (this.config.isTarget !== false);
20760         this.maintainOffset    = (this.config.maintainOffset);
20761         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
20762
20763     },
20764
20765     /**
20766      * Executed when the linked element is available
20767      * @method handleOnAvailable
20768      * @private
20769      */
20770     handleOnAvailable: function() {
20771         this.available = true;
20772         this.resetConstraints();
20773         this.onAvailable();
20774     },
20775
20776      /**
20777      * Configures the padding for the target zone in px.  Effectively expands
20778      * (or reduces) the virtual object size for targeting calculations.
20779      * Supports css-style shorthand; if only one parameter is passed, all sides
20780      * will have that padding, and if only two are passed, the top and bottom
20781      * will have the first param, the left and right the second.
20782      * @method setPadding
20783      * @param {int} iTop    Top pad
20784      * @param {int} iRight  Right pad
20785      * @param {int} iBot    Bot pad
20786      * @param {int} iLeft   Left pad
20787      */
20788     setPadding: function(iTop, iRight, iBot, iLeft) {
20789         // this.padding = [iLeft, iRight, iTop, iBot];
20790         if (!iRight && 0 !== iRight) {
20791             this.padding = [iTop, iTop, iTop, iTop];
20792         } else if (!iBot && 0 !== iBot) {
20793             this.padding = [iTop, iRight, iTop, iRight];
20794         } else {
20795             this.padding = [iTop, iRight, iBot, iLeft];
20796         }
20797     },
20798
20799     /**
20800      * Stores the initial placement of the linked element.
20801      * @method setInitialPosition
20802      * @param {int} diffX   the X offset, default 0
20803      * @param {int} diffY   the Y offset, default 0
20804      */
20805     setInitPosition: function(diffX, diffY) {
20806         var el = this.getEl();
20807
20808         if (!this.DDM.verifyEl(el)) {
20809             return;
20810         }
20811
20812         var dx = diffX || 0;
20813         var dy = diffY || 0;
20814
20815         var p = Dom.getXY( el );
20816
20817         this.initPageX = p[0] - dx;
20818         this.initPageY = p[1] - dy;
20819
20820         this.lastPageX = p[0];
20821         this.lastPageY = p[1];
20822
20823
20824         this.setStartPosition(p);
20825     },
20826
20827     /**
20828      * Sets the start position of the element.  This is set when the obj
20829      * is initialized, the reset when a drag is started.
20830      * @method setStartPosition
20831      * @param pos current position (from previous lookup)
20832      * @private
20833      */
20834     setStartPosition: function(pos) {
20835         var p = pos || Dom.getXY( this.getEl() );
20836         this.deltaSetXY = null;
20837
20838         this.startPageX = p[0];
20839         this.startPageY = p[1];
20840     },
20841
20842     /**
20843      * Add this instance to a group of related drag/drop objects.  All
20844      * instances belong to at least one group, and can belong to as many
20845      * groups as needed.
20846      * @method addToGroup
20847      * @param sGroup {string} the name of the group
20848      */
20849     addToGroup: function(sGroup) {
20850         this.groups[sGroup] = true;
20851         this.DDM.regDragDrop(this, sGroup);
20852     },
20853
20854     /**
20855      * Remove's this instance from the supplied interaction group
20856      * @method removeFromGroup
20857      * @param {string}  sGroup  The group to drop
20858      */
20859     removeFromGroup: function(sGroup) {
20860         if (this.groups[sGroup]) {
20861             delete this.groups[sGroup];
20862         }
20863
20864         this.DDM.removeDDFromGroup(this, sGroup);
20865     },
20866
20867     /**
20868      * Allows you to specify that an element other than the linked element
20869      * will be moved with the cursor during a drag
20870      * @method setDragElId
20871      * @param id {string} the id of the element that will be used to initiate the drag
20872      */
20873     setDragElId: function(id) {
20874         this.dragElId = id;
20875     },
20876
20877     /**
20878      * Allows you to specify a child of the linked element that should be
20879      * used to initiate the drag operation.  An example of this would be if
20880      * you have a content div with text and links.  Clicking anywhere in the
20881      * content area would normally start the drag operation.  Use this method
20882      * to specify that an element inside of the content div is the element
20883      * that starts the drag operation.
20884      * @method setHandleElId
20885      * @param id {string} the id of the element that will be used to
20886      * initiate the drag.
20887      */
20888     setHandleElId: function(id) {
20889         if (typeof id !== "string") {
20890             id = Roo.id(id);
20891         }
20892         this.handleElId = id;
20893         this.DDM.regHandle(this.id, id);
20894     },
20895
20896     /**
20897      * Allows you to set an element outside of the linked element as a drag
20898      * handle
20899      * @method setOuterHandleElId
20900      * @param id the id of the element that will be used to initiate the drag
20901      */
20902     setOuterHandleElId: function(id) {
20903         if (typeof id !== "string") {
20904             id = Roo.id(id);
20905         }
20906         Event.on(id, "mousedown",
20907                 this.handleMouseDown, this);
20908         this.setHandleElId(id);
20909
20910         this.hasOuterHandles = true;
20911     },
20912
20913     /**
20914      * Remove all drag and drop hooks for this element
20915      * @method unreg
20916      */
20917     unreg: function() {
20918         Event.un(this.id, "mousedown",
20919                 this.handleMouseDown);
20920         Event.un(this.id, "touchstart",
20921                 this.handleMouseDown);
20922         this._domRef = null;
20923         this.DDM._remove(this);
20924     },
20925
20926     destroy : function(){
20927         this.unreg();
20928     },
20929
20930     /**
20931      * Returns true if this instance is locked, or the drag drop mgr is locked
20932      * (meaning that all drag/drop is disabled on the page.)
20933      * @method isLocked
20934      * @return {boolean} true if this obj or all drag/drop is locked, else
20935      * false
20936      */
20937     isLocked: function() {
20938         return (this.DDM.isLocked() || this.locked);
20939     },
20940
20941     /**
20942      * Fired when this object is clicked
20943      * @method handleMouseDown
20944      * @param {Event} e
20945      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
20946      * @private
20947      */
20948     handleMouseDown: function(e, oDD){
20949      
20950         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
20951             //Roo.log('not touch/ button !=0');
20952             return;
20953         }
20954         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
20955             return; // double touch..
20956         }
20957         
20958
20959         if (this.isLocked()) {
20960             //Roo.log('locked');
20961             return;
20962         }
20963
20964         this.DDM.refreshCache(this.groups);
20965 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
20966         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
20967         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
20968             //Roo.log('no outer handes or not over target');
20969                 // do nothing.
20970         } else {
20971 //            Roo.log('check validator');
20972             if (this.clickValidator(e)) {
20973 //                Roo.log('validate success');
20974                 // set the initial element position
20975                 this.setStartPosition();
20976
20977
20978                 this.b4MouseDown(e);
20979                 this.onMouseDown(e);
20980
20981                 this.DDM.handleMouseDown(e, this);
20982
20983                 this.DDM.stopEvent(e);
20984             } else {
20985
20986
20987             }
20988         }
20989     },
20990
20991     clickValidator: function(e) {
20992         var target = e.getTarget();
20993         return ( this.isValidHandleChild(target) &&
20994                     (this.id == this.handleElId ||
20995                         this.DDM.handleWasClicked(target, this.id)) );
20996     },
20997
20998     /**
20999      * Allows you to specify a tag name that should not start a drag operation
21000      * when clicked.  This is designed to facilitate embedding links within a
21001      * drag handle that do something other than start the drag.
21002      * @method addInvalidHandleType
21003      * @param {string} tagName the type of element to exclude
21004      */
21005     addInvalidHandleType: function(tagName) {
21006         var type = tagName.toUpperCase();
21007         this.invalidHandleTypes[type] = type;
21008     },
21009
21010     /**
21011      * Lets you to specify an element id for a child of a drag handle
21012      * that should not initiate a drag
21013      * @method addInvalidHandleId
21014      * @param {string} id the element id of the element you wish to ignore
21015      */
21016     addInvalidHandleId: function(id) {
21017         if (typeof id !== "string") {
21018             id = Roo.id(id);
21019         }
21020         this.invalidHandleIds[id] = id;
21021     },
21022
21023     /**
21024      * Lets you specify a css class of elements that will not initiate a drag
21025      * @method addInvalidHandleClass
21026      * @param {string} cssClass the class of the elements you wish to ignore
21027      */
21028     addInvalidHandleClass: function(cssClass) {
21029         this.invalidHandleClasses.push(cssClass);
21030     },
21031
21032     /**
21033      * Unsets an excluded tag name set by addInvalidHandleType
21034      * @method removeInvalidHandleType
21035      * @param {string} tagName the type of element to unexclude
21036      */
21037     removeInvalidHandleType: function(tagName) {
21038         var type = tagName.toUpperCase();
21039         // this.invalidHandleTypes[type] = null;
21040         delete this.invalidHandleTypes[type];
21041     },
21042
21043     /**
21044      * Unsets an invalid handle id
21045      * @method removeInvalidHandleId
21046      * @param {string} id the id of the element to re-enable
21047      */
21048     removeInvalidHandleId: function(id) {
21049         if (typeof id !== "string") {
21050             id = Roo.id(id);
21051         }
21052         delete this.invalidHandleIds[id];
21053     },
21054
21055     /**
21056      * Unsets an invalid css class
21057      * @method removeInvalidHandleClass
21058      * @param {string} cssClass the class of the element(s) you wish to
21059      * re-enable
21060      */
21061     removeInvalidHandleClass: function(cssClass) {
21062         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
21063             if (this.invalidHandleClasses[i] == cssClass) {
21064                 delete this.invalidHandleClasses[i];
21065             }
21066         }
21067     },
21068
21069     /**
21070      * Checks the tag exclusion list to see if this click should be ignored
21071      * @method isValidHandleChild
21072      * @param {HTMLElement} node the HTMLElement to evaluate
21073      * @return {boolean} true if this is a valid tag type, false if not
21074      */
21075     isValidHandleChild: function(node) {
21076
21077         var valid = true;
21078         // var n = (node.nodeName == "#text") ? node.parentNode : node;
21079         var nodeName;
21080         try {
21081             nodeName = node.nodeName.toUpperCase();
21082         } catch(e) {
21083             nodeName = node.nodeName;
21084         }
21085         valid = valid && !this.invalidHandleTypes[nodeName];
21086         valid = valid && !this.invalidHandleIds[node.id];
21087
21088         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
21089             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
21090         }
21091
21092
21093         return valid;
21094
21095     },
21096
21097     /**
21098      * Create the array of horizontal tick marks if an interval was specified
21099      * in setXConstraint().
21100      * @method setXTicks
21101      * @private
21102      */
21103     setXTicks: function(iStartX, iTickSize) {
21104         this.xTicks = [];
21105         this.xTickSize = iTickSize;
21106
21107         var tickMap = {};
21108
21109         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
21110             if (!tickMap[i]) {
21111                 this.xTicks[this.xTicks.length] = i;
21112                 tickMap[i] = true;
21113             }
21114         }
21115
21116         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
21117             if (!tickMap[i]) {
21118                 this.xTicks[this.xTicks.length] = i;
21119                 tickMap[i] = true;
21120             }
21121         }
21122
21123         this.xTicks.sort(this.DDM.numericSort) ;
21124     },
21125
21126     /**
21127      * Create the array of vertical tick marks if an interval was specified in
21128      * setYConstraint().
21129      * @method setYTicks
21130      * @private
21131      */
21132     setYTicks: function(iStartY, iTickSize) {
21133         this.yTicks = [];
21134         this.yTickSize = iTickSize;
21135
21136         var tickMap = {};
21137
21138         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
21139             if (!tickMap[i]) {
21140                 this.yTicks[this.yTicks.length] = i;
21141                 tickMap[i] = true;
21142             }
21143         }
21144
21145         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
21146             if (!tickMap[i]) {
21147                 this.yTicks[this.yTicks.length] = i;
21148                 tickMap[i] = true;
21149             }
21150         }
21151
21152         this.yTicks.sort(this.DDM.numericSort) ;
21153     },
21154
21155     /**
21156      * By default, the element can be dragged any place on the screen.  Use
21157      * this method to limit the horizontal travel of the element.  Pass in
21158      * 0,0 for the parameters if you want to lock the drag to the y axis.
21159      * @method setXConstraint
21160      * @param {int} iLeft the number of pixels the element can move to the left
21161      * @param {int} iRight the number of pixels the element can move to the
21162      * right
21163      * @param {int} iTickSize optional parameter for specifying that the
21164      * element
21165      * should move iTickSize pixels at a time.
21166      */
21167     setXConstraint: function(iLeft, iRight, iTickSize) {
21168         this.leftConstraint = iLeft;
21169         this.rightConstraint = iRight;
21170
21171         this.minX = this.initPageX - iLeft;
21172         this.maxX = this.initPageX + iRight;
21173         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
21174
21175         this.constrainX = true;
21176     },
21177
21178     /**
21179      * Clears any constraints applied to this instance.  Also clears ticks
21180      * since they can't exist independent of a constraint at this time.
21181      * @method clearConstraints
21182      */
21183     clearConstraints: function() {
21184         this.constrainX = false;
21185         this.constrainY = false;
21186         this.clearTicks();
21187     },
21188
21189     /**
21190      * Clears any tick interval defined for this instance
21191      * @method clearTicks
21192      */
21193     clearTicks: function() {
21194         this.xTicks = null;
21195         this.yTicks = null;
21196         this.xTickSize = 0;
21197         this.yTickSize = 0;
21198     },
21199
21200     /**
21201      * By default, the element can be dragged any place on the screen.  Set
21202      * this to limit the vertical travel of the element.  Pass in 0,0 for the
21203      * parameters if you want to lock the drag to the x axis.
21204      * @method setYConstraint
21205      * @param {int} iUp the number of pixels the element can move up
21206      * @param {int} iDown the number of pixels the element can move down
21207      * @param {int} iTickSize optional parameter for specifying that the
21208      * element should move iTickSize pixels at a time.
21209      */
21210     setYConstraint: function(iUp, iDown, iTickSize) {
21211         this.topConstraint = iUp;
21212         this.bottomConstraint = iDown;
21213
21214         this.minY = this.initPageY - iUp;
21215         this.maxY = this.initPageY + iDown;
21216         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
21217
21218         this.constrainY = true;
21219
21220     },
21221
21222     /**
21223      * resetConstraints must be called if you manually reposition a dd element.
21224      * @method resetConstraints
21225      * @param {boolean} maintainOffset
21226      */
21227     resetConstraints: function() {
21228
21229
21230         // Maintain offsets if necessary
21231         if (this.initPageX || this.initPageX === 0) {
21232             // figure out how much this thing has moved
21233             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
21234             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
21235
21236             this.setInitPosition(dx, dy);
21237
21238         // This is the first time we have detected the element's position
21239         } else {
21240             this.setInitPosition();
21241         }
21242
21243         if (this.constrainX) {
21244             this.setXConstraint( this.leftConstraint,
21245                                  this.rightConstraint,
21246                                  this.xTickSize        );
21247         }
21248
21249         if (this.constrainY) {
21250             this.setYConstraint( this.topConstraint,
21251                                  this.bottomConstraint,
21252                                  this.yTickSize         );
21253         }
21254     },
21255
21256     /**
21257      * Normally the drag element is moved pixel by pixel, but we can specify
21258      * that it move a number of pixels at a time.  This method resolves the
21259      * location when we have it set up like this.
21260      * @method getTick
21261      * @param {int} val where we want to place the object
21262      * @param {int[]} tickArray sorted array of valid points
21263      * @return {int} the closest tick
21264      * @private
21265      */
21266     getTick: function(val, tickArray) {
21267
21268         if (!tickArray) {
21269             // If tick interval is not defined, it is effectively 1 pixel,
21270             // so we return the value passed to us.
21271             return val;
21272         } else if (tickArray[0] >= val) {
21273             // The value is lower than the first tick, so we return the first
21274             // tick.
21275             return tickArray[0];
21276         } else {
21277             for (var i=0, len=tickArray.length; i<len; ++i) {
21278                 var next = i + 1;
21279                 if (tickArray[next] && tickArray[next] >= val) {
21280                     var diff1 = val - tickArray[i];
21281                     var diff2 = tickArray[next] - val;
21282                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
21283                 }
21284             }
21285
21286             // The value is larger than the last tick, so we return the last
21287             // tick.
21288             return tickArray[tickArray.length - 1];
21289         }
21290     },
21291
21292     /**
21293      * toString method
21294      * @method toString
21295      * @return {string} string representation of the dd obj
21296      */
21297     toString: function() {
21298         return ("DragDrop " + this.id);
21299     }
21300
21301 });
21302
21303 })();
21304 /*
21305  * Based on:
21306  * Ext JS Library 1.1.1
21307  * Copyright(c) 2006-2007, Ext JS, LLC.
21308  *
21309  * Originally Released Under LGPL - original licence link has changed is not relivant.
21310  *
21311  * Fork - LGPL
21312  * <script type="text/javascript">
21313  */
21314
21315
21316 /**
21317  * The drag and drop utility provides a framework for building drag and drop
21318  * applications.  In addition to enabling drag and drop for specific elements,
21319  * the drag and drop elements are tracked by the manager class, and the
21320  * interactions between the various elements are tracked during the drag and
21321  * the implementing code is notified about these important moments.
21322  */
21323
21324 // Only load the library once.  Rewriting the manager class would orphan
21325 // existing drag and drop instances.
21326 if (!Roo.dd.DragDropMgr) {
21327
21328 /**
21329  * @class Roo.dd.DragDropMgr
21330  * DragDropMgr is a singleton that tracks the element interaction for
21331  * all DragDrop items in the window.  Generally, you will not call
21332  * this class directly, but it does have helper methods that could
21333  * be useful in your DragDrop implementations.
21334  * @static
21335  */
21336 Roo.dd.DragDropMgr = function() {
21337
21338     var Event = Roo.EventManager;
21339
21340     return {
21341
21342         /**
21343          * Two dimensional Array of registered DragDrop objects.  The first
21344          * dimension is the DragDrop item group, the second the DragDrop
21345          * object.
21346          * @property ids
21347          * @type {string: string}
21348          * @private
21349          * @static
21350          */
21351         ids: {},
21352
21353         /**
21354          * Array of element ids defined as drag handles.  Used to determine
21355          * if the element that generated the mousedown event is actually the
21356          * handle and not the html element itself.
21357          * @property handleIds
21358          * @type {string: string}
21359          * @private
21360          * @static
21361          */
21362         handleIds: {},
21363
21364         /**
21365          * the DragDrop object that is currently being dragged
21366          * @property dragCurrent
21367          * @type DragDrop
21368          * @private
21369          * @static
21370          **/
21371         dragCurrent: null,
21372
21373         /**
21374          * the DragDrop object(s) that are being hovered over
21375          * @property dragOvers
21376          * @type Array
21377          * @private
21378          * @static
21379          */
21380         dragOvers: {},
21381
21382         /**
21383          * the X distance between the cursor and the object being dragged
21384          * @property deltaX
21385          * @type int
21386          * @private
21387          * @static
21388          */
21389         deltaX: 0,
21390
21391         /**
21392          * the Y distance between the cursor and the object being dragged
21393          * @property deltaY
21394          * @type int
21395          * @private
21396          * @static
21397          */
21398         deltaY: 0,
21399
21400         /**
21401          * Flag to determine if we should prevent the default behavior of the
21402          * events we define. By default this is true, but this can be set to
21403          * false if you need the default behavior (not recommended)
21404          * @property preventDefault
21405          * @type boolean
21406          * @static
21407          */
21408         preventDefault: true,
21409
21410         /**
21411          * Flag to determine if we should stop the propagation of the events
21412          * we generate. This is true by default but you may want to set it to
21413          * false if the html element contains other features that require the
21414          * mouse click.
21415          * @property stopPropagation
21416          * @type boolean
21417          * @static
21418          */
21419         stopPropagation: true,
21420
21421         /**
21422          * Internal flag that is set to true when drag and drop has been
21423          * intialized
21424          * @property initialized
21425          * @private
21426          * @static
21427          */
21428         initalized: false,
21429
21430         /**
21431          * All drag and drop can be disabled.
21432          * @property locked
21433          * @private
21434          * @static
21435          */
21436         locked: false,
21437
21438         /**
21439          * Called the first time an element is registered.
21440          * @method init
21441          * @private
21442          * @static
21443          */
21444         init: function() {
21445             this.initialized = true;
21446         },
21447
21448         /**
21449          * In point mode, drag and drop interaction is defined by the
21450          * location of the cursor during the drag/drop
21451          * @property POINT
21452          * @type int
21453          * @static
21454          */
21455         POINT: 0,
21456
21457         /**
21458          * In intersect mode, drag and drop interactio nis defined by the
21459          * overlap of two or more drag and drop objects.
21460          * @property INTERSECT
21461          * @type int
21462          * @static
21463          */
21464         INTERSECT: 1,
21465
21466         /**
21467          * The current drag and drop mode.  Default: POINT
21468          * @property mode
21469          * @type int
21470          * @static
21471          */
21472         mode: 0,
21473
21474         /**
21475          * Runs method on all drag and drop objects
21476          * @method _execOnAll
21477          * @private
21478          * @static
21479          */
21480         _execOnAll: function(sMethod, args) {
21481             for (var i in this.ids) {
21482                 for (var j in this.ids[i]) {
21483                     var oDD = this.ids[i][j];
21484                     if (! this.isTypeOfDD(oDD)) {
21485                         continue;
21486                     }
21487                     oDD[sMethod].apply(oDD, args);
21488                 }
21489             }
21490         },
21491
21492         /**
21493          * Drag and drop initialization.  Sets up the global event handlers
21494          * @method _onLoad
21495          * @private
21496          * @static
21497          */
21498         _onLoad: function() {
21499
21500             this.init();
21501
21502             if (!Roo.isTouch) {
21503                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
21504                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
21505             }
21506             Event.on(document, "touchend",   this.handleMouseUp, this, true);
21507             Event.on(document, "touchmove", this.handleMouseMove, this, true);
21508             
21509             Event.on(window,   "unload",    this._onUnload, this, true);
21510             Event.on(window,   "resize",    this._onResize, this, true);
21511             // Event.on(window,   "mouseout",    this._test);
21512
21513         },
21514
21515         /**
21516          * Reset constraints on all drag and drop objs
21517          * @method _onResize
21518          * @private
21519          * @static
21520          */
21521         _onResize: function(e) {
21522             this._execOnAll("resetConstraints", []);
21523         },
21524
21525         /**
21526          * Lock all drag and drop functionality
21527          * @method lock
21528          * @static
21529          */
21530         lock: function() { this.locked = true; },
21531
21532         /**
21533          * Unlock all drag and drop functionality
21534          * @method unlock
21535          * @static
21536          */
21537         unlock: function() { this.locked = false; },
21538
21539         /**
21540          * Is drag and drop locked?
21541          * @method isLocked
21542          * @return {boolean} True if drag and drop is locked, false otherwise.
21543          * @static
21544          */
21545         isLocked: function() { return this.locked; },
21546
21547         /**
21548          * Location cache that is set for all drag drop objects when a drag is
21549          * initiated, cleared when the drag is finished.
21550          * @property locationCache
21551          * @private
21552          * @static
21553          */
21554         locationCache: {},
21555
21556         /**
21557          * Set useCache to false if you want to force object the lookup of each
21558          * drag and drop linked element constantly during a drag.
21559          * @property useCache
21560          * @type boolean
21561          * @static
21562          */
21563         useCache: true,
21564
21565         /**
21566          * The number of pixels that the mouse needs to move after the
21567          * mousedown before the drag is initiated.  Default=3;
21568          * @property clickPixelThresh
21569          * @type int
21570          * @static
21571          */
21572         clickPixelThresh: 3,
21573
21574         /**
21575          * The number of milliseconds after the mousedown event to initiate the
21576          * drag if we don't get a mouseup event. Default=1000
21577          * @property clickTimeThresh
21578          * @type int
21579          * @static
21580          */
21581         clickTimeThresh: 350,
21582
21583         /**
21584          * Flag that indicates that either the drag pixel threshold or the
21585          * mousdown time threshold has been met
21586          * @property dragThreshMet
21587          * @type boolean
21588          * @private
21589          * @static
21590          */
21591         dragThreshMet: false,
21592
21593         /**
21594          * Timeout used for the click time threshold
21595          * @property clickTimeout
21596          * @type Object
21597          * @private
21598          * @static
21599          */
21600         clickTimeout: null,
21601
21602         /**
21603          * The X position of the mousedown event stored for later use when a
21604          * drag threshold is met.
21605          * @property startX
21606          * @type int
21607          * @private
21608          * @static
21609          */
21610         startX: 0,
21611
21612         /**
21613          * The Y position of the mousedown event stored for later use when a
21614          * drag threshold is met.
21615          * @property startY
21616          * @type int
21617          * @private
21618          * @static
21619          */
21620         startY: 0,
21621
21622         /**
21623          * Each DragDrop instance must be registered with the DragDropMgr.
21624          * This is executed in DragDrop.init()
21625          * @method regDragDrop
21626          * @param {DragDrop} oDD the DragDrop object to register
21627          * @param {String} sGroup the name of the group this element belongs to
21628          * @static
21629          */
21630         regDragDrop: function(oDD, sGroup) {
21631             if (!this.initialized) { this.init(); }
21632
21633             if (!this.ids[sGroup]) {
21634                 this.ids[sGroup] = {};
21635             }
21636             this.ids[sGroup][oDD.id] = oDD;
21637         },
21638
21639         /**
21640          * Removes the supplied dd instance from the supplied group. Executed
21641          * by DragDrop.removeFromGroup, so don't call this function directly.
21642          * @method removeDDFromGroup
21643          * @private
21644          * @static
21645          */
21646         removeDDFromGroup: function(oDD, sGroup) {
21647             if (!this.ids[sGroup]) {
21648                 this.ids[sGroup] = {};
21649             }
21650
21651             var obj = this.ids[sGroup];
21652             if (obj && obj[oDD.id]) {
21653                 delete obj[oDD.id];
21654             }
21655         },
21656
21657         /**
21658          * Unregisters a drag and drop item.  This is executed in
21659          * DragDrop.unreg, use that method instead of calling this directly.
21660          * @method _remove
21661          * @private
21662          * @static
21663          */
21664         _remove: function(oDD) {
21665             for (var g in oDD.groups) {
21666                 if (g && this.ids[g][oDD.id]) {
21667                     delete this.ids[g][oDD.id];
21668                 }
21669             }
21670             delete this.handleIds[oDD.id];
21671         },
21672
21673         /**
21674          * Each DragDrop handle element must be registered.  This is done
21675          * automatically when executing DragDrop.setHandleElId()
21676          * @method regHandle
21677          * @param {String} sDDId the DragDrop id this element is a handle for
21678          * @param {String} sHandleId the id of the element that is the drag
21679          * handle
21680          * @static
21681          */
21682         regHandle: function(sDDId, sHandleId) {
21683             if (!this.handleIds[sDDId]) {
21684                 this.handleIds[sDDId] = {};
21685             }
21686             this.handleIds[sDDId][sHandleId] = sHandleId;
21687         },
21688
21689         /**
21690          * Utility function to determine if a given element has been
21691          * registered as a drag drop item.
21692          * @method isDragDrop
21693          * @param {String} id the element id to check
21694          * @return {boolean} true if this element is a DragDrop item,
21695          * false otherwise
21696          * @static
21697          */
21698         isDragDrop: function(id) {
21699             return ( this.getDDById(id) ) ? true : false;
21700         },
21701
21702         /**
21703          * Returns the drag and drop instances that are in all groups the
21704          * passed in instance belongs to.
21705          * @method getRelated
21706          * @param {DragDrop} p_oDD the obj to get related data for
21707          * @param {boolean} bTargetsOnly if true, only return targetable objs
21708          * @return {DragDrop[]} the related instances
21709          * @static
21710          */
21711         getRelated: function(p_oDD, bTargetsOnly) {
21712             var oDDs = [];
21713             for (var i in p_oDD.groups) {
21714                 for (j in this.ids[i]) {
21715                     var dd = this.ids[i][j];
21716                     if (! this.isTypeOfDD(dd)) {
21717                         continue;
21718                     }
21719                     if (!bTargetsOnly || dd.isTarget) {
21720                         oDDs[oDDs.length] = dd;
21721                     }
21722                 }
21723             }
21724
21725             return oDDs;
21726         },
21727
21728         /**
21729          * Returns true if the specified dd target is a legal target for
21730          * the specifice drag obj
21731          * @method isLegalTarget
21732          * @param {DragDrop} the drag obj
21733          * @param {DragDrop} the target
21734          * @return {boolean} true if the target is a legal target for the
21735          * dd obj
21736          * @static
21737          */
21738         isLegalTarget: function (oDD, oTargetDD) {
21739             var targets = this.getRelated(oDD, true);
21740             for (var i=0, len=targets.length;i<len;++i) {
21741                 if (targets[i].id == oTargetDD.id) {
21742                     return true;
21743                 }
21744             }
21745
21746             return false;
21747         },
21748
21749         /**
21750          * My goal is to be able to transparently determine if an object is
21751          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
21752          * returns "object", oDD.constructor.toString() always returns
21753          * "DragDrop" and not the name of the subclass.  So for now it just
21754          * evaluates a well-known variable in DragDrop.
21755          * @method isTypeOfDD
21756          * @param {Object} the object to evaluate
21757          * @return {boolean} true if typeof oDD = DragDrop
21758          * @static
21759          */
21760         isTypeOfDD: function (oDD) {
21761             return (oDD && oDD.__ygDragDrop);
21762         },
21763
21764         /**
21765          * Utility function to determine if a given element has been
21766          * registered as a drag drop handle for the given Drag Drop object.
21767          * @method isHandle
21768          * @param {String} id the element id to check
21769          * @return {boolean} true if this element is a DragDrop handle, false
21770          * otherwise
21771          * @static
21772          */
21773         isHandle: function(sDDId, sHandleId) {
21774             return ( this.handleIds[sDDId] &&
21775                             this.handleIds[sDDId][sHandleId] );
21776         },
21777
21778         /**
21779          * Returns the DragDrop instance for a given id
21780          * @method getDDById
21781          * @param {String} id the id of the DragDrop object
21782          * @return {DragDrop} the drag drop object, null if it is not found
21783          * @static
21784          */
21785         getDDById: function(id) {
21786             for (var i in this.ids) {
21787                 if (this.ids[i][id]) {
21788                     return this.ids[i][id];
21789                 }
21790             }
21791             return null;
21792         },
21793
21794         /**
21795          * Fired after a registered DragDrop object gets the mousedown event.
21796          * Sets up the events required to track the object being dragged
21797          * @method handleMouseDown
21798          * @param {Event} e the event
21799          * @param oDD the DragDrop object being dragged
21800          * @private
21801          * @static
21802          */
21803         handleMouseDown: function(e, oDD) {
21804             if(Roo.QuickTips){
21805                 Roo.QuickTips.disable();
21806             }
21807             this.currentTarget = e.getTarget();
21808
21809             this.dragCurrent = oDD;
21810
21811             var el = oDD.getEl();
21812
21813             // track start position
21814             this.startX = e.getPageX();
21815             this.startY = e.getPageY();
21816
21817             this.deltaX = this.startX - el.offsetLeft;
21818             this.deltaY = this.startY - el.offsetTop;
21819
21820             this.dragThreshMet = false;
21821
21822             this.clickTimeout = setTimeout(
21823                     function() {
21824                         var DDM = Roo.dd.DDM;
21825                         DDM.startDrag(DDM.startX, DDM.startY);
21826                     },
21827                     this.clickTimeThresh );
21828         },
21829
21830         /**
21831          * Fired when either the drag pixel threshol or the mousedown hold
21832          * time threshold has been met.
21833          * @method startDrag
21834          * @param x {int} the X position of the original mousedown
21835          * @param y {int} the Y position of the original mousedown
21836          * @static
21837          */
21838         startDrag: function(x, y) {
21839             clearTimeout(this.clickTimeout);
21840             if (this.dragCurrent) {
21841                 this.dragCurrent.b4StartDrag(x, y);
21842                 this.dragCurrent.startDrag(x, y);
21843             }
21844             this.dragThreshMet = true;
21845         },
21846
21847         /**
21848          * Internal function to handle the mouseup event.  Will be invoked
21849          * from the context of the document.
21850          * @method handleMouseUp
21851          * @param {Event} e the event
21852          * @private
21853          * @static
21854          */
21855         handleMouseUp: function(e) {
21856
21857             if(Roo.QuickTips){
21858                 Roo.QuickTips.enable();
21859             }
21860             if (! this.dragCurrent) {
21861                 return;
21862             }
21863
21864             clearTimeout(this.clickTimeout);
21865
21866             if (this.dragThreshMet) {
21867                 this.fireEvents(e, true);
21868             } else {
21869             }
21870
21871             this.stopDrag(e);
21872
21873             this.stopEvent(e);
21874         },
21875
21876         /**
21877          * Utility to stop event propagation and event default, if these
21878          * features are turned on.
21879          * @method stopEvent
21880          * @param {Event} e the event as returned by this.getEvent()
21881          * @static
21882          */
21883         stopEvent: function(e){
21884             if(this.stopPropagation) {
21885                 e.stopPropagation();
21886             }
21887
21888             if (this.preventDefault) {
21889                 e.preventDefault();
21890             }
21891         },
21892
21893         /**
21894          * Internal function to clean up event handlers after the drag
21895          * operation is complete
21896          * @method stopDrag
21897          * @param {Event} e the event
21898          * @private
21899          * @static
21900          */
21901         stopDrag: function(e) {
21902             // Fire the drag end event for the item that was dragged
21903             if (this.dragCurrent) {
21904                 if (this.dragThreshMet) {
21905                     this.dragCurrent.b4EndDrag(e);
21906                     this.dragCurrent.endDrag(e);
21907                 }
21908
21909                 this.dragCurrent.onMouseUp(e);
21910             }
21911
21912             this.dragCurrent = null;
21913             this.dragOvers = {};
21914         },
21915
21916         /**
21917          * Internal function to handle the mousemove event.  Will be invoked
21918          * from the context of the html element.
21919          *
21920          * @TODO figure out what we can do about mouse events lost when the
21921          * user drags objects beyond the window boundary.  Currently we can
21922          * detect this in internet explorer by verifying that the mouse is
21923          * down during the mousemove event.  Firefox doesn't give us the
21924          * button state on the mousemove event.
21925          * @method handleMouseMove
21926          * @param {Event} e the event
21927          * @private
21928          * @static
21929          */
21930         handleMouseMove: function(e) {
21931             if (! this.dragCurrent) {
21932                 return true;
21933             }
21934
21935             // var button = e.which || e.button;
21936
21937             // check for IE mouseup outside of page boundary
21938             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
21939                 this.stopEvent(e);
21940                 return this.handleMouseUp(e);
21941             }
21942
21943             if (!this.dragThreshMet) {
21944                 var diffX = Math.abs(this.startX - e.getPageX());
21945                 var diffY = Math.abs(this.startY - e.getPageY());
21946                 if (diffX > this.clickPixelThresh ||
21947                             diffY > this.clickPixelThresh) {
21948                     this.startDrag(this.startX, this.startY);
21949                 }
21950             }
21951
21952             if (this.dragThreshMet) {
21953                 this.dragCurrent.b4Drag(e);
21954                 this.dragCurrent.onDrag(e);
21955                 if(!this.dragCurrent.moveOnly){
21956                     this.fireEvents(e, false);
21957                 }
21958             }
21959
21960             this.stopEvent(e);
21961
21962             return true;
21963         },
21964
21965         /**
21966          * Iterates over all of the DragDrop elements to find ones we are
21967          * hovering over or dropping on
21968          * @method fireEvents
21969          * @param {Event} e the event
21970          * @param {boolean} isDrop is this a drop op or a mouseover op?
21971          * @private
21972          * @static
21973          */
21974         fireEvents: function(e, isDrop) {
21975             var dc = this.dragCurrent;
21976
21977             // If the user did the mouse up outside of the window, we could
21978             // get here even though we have ended the drag.
21979             if (!dc || dc.isLocked()) {
21980                 return;
21981             }
21982
21983             var pt = e.getPoint();
21984
21985             // cache the previous dragOver array
21986             var oldOvers = [];
21987
21988             var outEvts   = [];
21989             var overEvts  = [];
21990             var dropEvts  = [];
21991             var enterEvts = [];
21992
21993             // Check to see if the object(s) we were hovering over is no longer
21994             // being hovered over so we can fire the onDragOut event
21995             for (var i in this.dragOvers) {
21996
21997                 var ddo = this.dragOvers[i];
21998
21999                 if (! this.isTypeOfDD(ddo)) {
22000                     continue;
22001                 }
22002
22003                 if (! this.isOverTarget(pt, ddo, this.mode)) {
22004                     outEvts.push( ddo );
22005                 }
22006
22007                 oldOvers[i] = true;
22008                 delete this.dragOvers[i];
22009             }
22010
22011             for (var sGroup in dc.groups) {
22012
22013                 if ("string" != typeof sGroup) {
22014                     continue;
22015                 }
22016
22017                 for (i in this.ids[sGroup]) {
22018                     var oDD = this.ids[sGroup][i];
22019                     if (! this.isTypeOfDD(oDD)) {
22020                         continue;
22021                     }
22022
22023                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
22024                         if (this.isOverTarget(pt, oDD, this.mode)) {
22025                             // look for drop interactions
22026                             if (isDrop) {
22027                                 dropEvts.push( oDD );
22028                             // look for drag enter and drag over interactions
22029                             } else {
22030
22031                                 // initial drag over: dragEnter fires
22032                                 if (!oldOvers[oDD.id]) {
22033                                     enterEvts.push( oDD );
22034                                 // subsequent drag overs: dragOver fires
22035                                 } else {
22036                                     overEvts.push( oDD );
22037                                 }
22038
22039                                 this.dragOvers[oDD.id] = oDD;
22040                             }
22041                         }
22042                     }
22043                 }
22044             }
22045
22046             if (this.mode) {
22047                 if (outEvts.length) {
22048                     dc.b4DragOut(e, outEvts);
22049                     dc.onDragOut(e, outEvts);
22050                 }
22051
22052                 if (enterEvts.length) {
22053                     dc.onDragEnter(e, enterEvts);
22054                 }
22055
22056                 if (overEvts.length) {
22057                     dc.b4DragOver(e, overEvts);
22058                     dc.onDragOver(e, overEvts);
22059                 }
22060
22061                 if (dropEvts.length) {
22062                     dc.b4DragDrop(e, dropEvts);
22063                     dc.onDragDrop(e, dropEvts);
22064                 }
22065
22066             } else {
22067                 // fire dragout events
22068                 var len = 0;
22069                 for (i=0, len=outEvts.length; i<len; ++i) {
22070                     dc.b4DragOut(e, outEvts[i].id);
22071                     dc.onDragOut(e, outEvts[i].id);
22072                 }
22073
22074                 // fire enter events
22075                 for (i=0,len=enterEvts.length; i<len; ++i) {
22076                     // dc.b4DragEnter(e, oDD.id);
22077                     dc.onDragEnter(e, enterEvts[i].id);
22078                 }
22079
22080                 // fire over events
22081                 for (i=0,len=overEvts.length; i<len; ++i) {
22082                     dc.b4DragOver(e, overEvts[i].id);
22083                     dc.onDragOver(e, overEvts[i].id);
22084                 }
22085
22086                 // fire drop events
22087                 for (i=0, len=dropEvts.length; i<len; ++i) {
22088                     dc.b4DragDrop(e, dropEvts[i].id);
22089                     dc.onDragDrop(e, dropEvts[i].id);
22090                 }
22091
22092             }
22093
22094             // notify about a drop that did not find a target
22095             if (isDrop && !dropEvts.length) {
22096                 dc.onInvalidDrop(e);
22097             }
22098
22099         },
22100
22101         /**
22102          * Helper function for getting the best match from the list of drag
22103          * and drop objects returned by the drag and drop events when we are
22104          * in INTERSECT mode.  It returns either the first object that the
22105          * cursor is over, or the object that has the greatest overlap with
22106          * the dragged element.
22107          * @method getBestMatch
22108          * @param  {DragDrop[]} dds The array of drag and drop objects
22109          * targeted
22110          * @return {DragDrop}       The best single match
22111          * @static
22112          */
22113         getBestMatch: function(dds) {
22114             var winner = null;
22115             // Return null if the input is not what we expect
22116             //if (!dds || !dds.length || dds.length == 0) {
22117                // winner = null;
22118             // If there is only one item, it wins
22119             //} else if (dds.length == 1) {
22120
22121             var len = dds.length;
22122
22123             if (len == 1) {
22124                 winner = dds[0];
22125             } else {
22126                 // Loop through the targeted items
22127                 for (var i=0; i<len; ++i) {
22128                     var dd = dds[i];
22129                     // If the cursor is over the object, it wins.  If the
22130                     // cursor is over multiple matches, the first one we come
22131                     // to wins.
22132                     if (dd.cursorIsOver) {
22133                         winner = dd;
22134                         break;
22135                     // Otherwise the object with the most overlap wins
22136                     } else {
22137                         if (!winner ||
22138                             winner.overlap.getArea() < dd.overlap.getArea()) {
22139                             winner = dd;
22140                         }
22141                     }
22142                 }
22143             }
22144
22145             return winner;
22146         },
22147
22148         /**
22149          * Refreshes the cache of the top-left and bottom-right points of the
22150          * drag and drop objects in the specified group(s).  This is in the
22151          * format that is stored in the drag and drop instance, so typical
22152          * usage is:
22153          * <code>
22154          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
22155          * </code>
22156          * Alternatively:
22157          * <code>
22158          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
22159          * </code>
22160          * @TODO this really should be an indexed array.  Alternatively this
22161          * method could accept both.
22162          * @method refreshCache
22163          * @param {Object} groups an associative array of groups to refresh
22164          * @static
22165          */
22166         refreshCache: function(groups) {
22167             for (var sGroup in groups) {
22168                 if ("string" != typeof sGroup) {
22169                     continue;
22170                 }
22171                 for (var i in this.ids[sGroup]) {
22172                     var oDD = this.ids[sGroup][i];
22173
22174                     if (this.isTypeOfDD(oDD)) {
22175                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
22176                         var loc = this.getLocation(oDD);
22177                         if (loc) {
22178                             this.locationCache[oDD.id] = loc;
22179                         } else {
22180                             delete this.locationCache[oDD.id];
22181                             // this will unregister the drag and drop object if
22182                             // the element is not in a usable state
22183                             // oDD.unreg();
22184                         }
22185                     }
22186                 }
22187             }
22188         },
22189
22190         /**
22191          * This checks to make sure an element exists and is in the DOM.  The
22192          * main purpose is to handle cases where innerHTML is used to remove
22193          * drag and drop objects from the DOM.  IE provides an 'unspecified
22194          * error' when trying to access the offsetParent of such an element
22195          * @method verifyEl
22196          * @param {HTMLElement} el the element to check
22197          * @return {boolean} true if the element looks usable
22198          * @static
22199          */
22200         verifyEl: function(el) {
22201             if (el) {
22202                 var parent;
22203                 if(Roo.isIE){
22204                     try{
22205                         parent = el.offsetParent;
22206                     }catch(e){}
22207                 }else{
22208                     parent = el.offsetParent;
22209                 }
22210                 if (parent) {
22211                     return true;
22212                 }
22213             }
22214
22215             return false;
22216         },
22217
22218         /**
22219          * Returns a Region object containing the drag and drop element's position
22220          * and size, including the padding configured for it
22221          * @method getLocation
22222          * @param {DragDrop} oDD the drag and drop object to get the
22223          *                       location for
22224          * @return {Roo.lib.Region} a Region object representing the total area
22225          *                             the element occupies, including any padding
22226          *                             the instance is configured for.
22227          * @static
22228          */
22229         getLocation: function(oDD) {
22230             if (! this.isTypeOfDD(oDD)) {
22231                 return null;
22232             }
22233
22234             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
22235
22236             try {
22237                 pos= Roo.lib.Dom.getXY(el);
22238             } catch (e) { }
22239
22240             if (!pos) {
22241                 return null;
22242             }
22243
22244             x1 = pos[0];
22245             x2 = x1 + el.offsetWidth;
22246             y1 = pos[1];
22247             y2 = y1 + el.offsetHeight;
22248
22249             t = y1 - oDD.padding[0];
22250             r = x2 + oDD.padding[1];
22251             b = y2 + oDD.padding[2];
22252             l = x1 - oDD.padding[3];
22253
22254             return new Roo.lib.Region( t, r, b, l );
22255         },
22256
22257         /**
22258          * Checks the cursor location to see if it over the target
22259          * @method isOverTarget
22260          * @param {Roo.lib.Point} pt The point to evaluate
22261          * @param {DragDrop} oTarget the DragDrop object we are inspecting
22262          * @return {boolean} true if the mouse is over the target
22263          * @private
22264          * @static
22265          */
22266         isOverTarget: function(pt, oTarget, intersect) {
22267             // use cache if available
22268             var loc = this.locationCache[oTarget.id];
22269             if (!loc || !this.useCache) {
22270                 loc = this.getLocation(oTarget);
22271                 this.locationCache[oTarget.id] = loc;
22272
22273             }
22274
22275             if (!loc) {
22276                 return false;
22277             }
22278
22279             oTarget.cursorIsOver = loc.contains( pt );
22280
22281             // DragDrop is using this as a sanity check for the initial mousedown
22282             // in this case we are done.  In POINT mode, if the drag obj has no
22283             // contraints, we are also done. Otherwise we need to evaluate the
22284             // location of the target as related to the actual location of the
22285             // dragged element.
22286             var dc = this.dragCurrent;
22287             if (!dc || !dc.getTargetCoord ||
22288                     (!intersect && !dc.constrainX && !dc.constrainY)) {
22289                 return oTarget.cursorIsOver;
22290             }
22291
22292             oTarget.overlap = null;
22293
22294             // Get the current location of the drag element, this is the
22295             // location of the mouse event less the delta that represents
22296             // where the original mousedown happened on the element.  We
22297             // need to consider constraints and ticks as well.
22298             var pos = dc.getTargetCoord(pt.x, pt.y);
22299
22300             var el = dc.getDragEl();
22301             var curRegion = new Roo.lib.Region( pos.y,
22302                                                    pos.x + el.offsetWidth,
22303                                                    pos.y + el.offsetHeight,
22304                                                    pos.x );
22305
22306             var overlap = curRegion.intersect(loc);
22307
22308             if (overlap) {
22309                 oTarget.overlap = overlap;
22310                 return (intersect) ? true : oTarget.cursorIsOver;
22311             } else {
22312                 return false;
22313             }
22314         },
22315
22316         /**
22317          * unload event handler
22318          * @method _onUnload
22319          * @private
22320          * @static
22321          */
22322         _onUnload: function(e, me) {
22323             Roo.dd.DragDropMgr.unregAll();
22324         },
22325
22326         /**
22327          * Cleans up the drag and drop events and objects.
22328          * @method unregAll
22329          * @private
22330          * @static
22331          */
22332         unregAll: function() {
22333
22334             if (this.dragCurrent) {
22335                 this.stopDrag();
22336                 this.dragCurrent = null;
22337             }
22338
22339             this._execOnAll("unreg", []);
22340
22341             for (i in this.elementCache) {
22342                 delete this.elementCache[i];
22343             }
22344
22345             this.elementCache = {};
22346             this.ids = {};
22347         },
22348
22349         /**
22350          * A cache of DOM elements
22351          * @property elementCache
22352          * @private
22353          * @static
22354          */
22355         elementCache: {},
22356
22357         /**
22358          * Get the wrapper for the DOM element specified
22359          * @method getElWrapper
22360          * @param {String} id the id of the element to get
22361          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
22362          * @private
22363          * @deprecated This wrapper isn't that useful
22364          * @static
22365          */
22366         getElWrapper: function(id) {
22367             var oWrapper = this.elementCache[id];
22368             if (!oWrapper || !oWrapper.el) {
22369                 oWrapper = this.elementCache[id] =
22370                     new this.ElementWrapper(Roo.getDom(id));
22371             }
22372             return oWrapper;
22373         },
22374
22375         /**
22376          * Returns the actual DOM element
22377          * @method getElement
22378          * @param {String} id the id of the elment to get
22379          * @return {Object} The element
22380          * @deprecated use Roo.getDom instead
22381          * @static
22382          */
22383         getElement: function(id) {
22384             return Roo.getDom(id);
22385         },
22386
22387         /**
22388          * Returns the style property for the DOM element (i.e.,
22389          * document.getElById(id).style)
22390          * @method getCss
22391          * @param {String} id the id of the elment to get
22392          * @return {Object} The style property of the element
22393          * @deprecated use Roo.getDom instead
22394          * @static
22395          */
22396         getCss: function(id) {
22397             var el = Roo.getDom(id);
22398             return (el) ? el.style : null;
22399         },
22400
22401         /**
22402          * Inner class for cached elements
22403          * @class DragDropMgr.ElementWrapper
22404          * @for DragDropMgr
22405          * @private
22406          * @deprecated
22407          */
22408         ElementWrapper: function(el) {
22409                 /**
22410                  * The element
22411                  * @property el
22412                  */
22413                 this.el = el || null;
22414                 /**
22415                  * The element id
22416                  * @property id
22417                  */
22418                 this.id = this.el && el.id;
22419                 /**
22420                  * A reference to the style property
22421                  * @property css
22422                  */
22423                 this.css = this.el && el.style;
22424             },
22425
22426         /**
22427          * Returns the X position of an html element
22428          * @method getPosX
22429          * @param el the element for which to get the position
22430          * @return {int} the X coordinate
22431          * @for DragDropMgr
22432          * @deprecated use Roo.lib.Dom.getX instead
22433          * @static
22434          */
22435         getPosX: function(el) {
22436             return Roo.lib.Dom.getX(el);
22437         },
22438
22439         /**
22440          * Returns the Y position of an html element
22441          * @method getPosY
22442          * @param el the element for which to get the position
22443          * @return {int} the Y coordinate
22444          * @deprecated use Roo.lib.Dom.getY instead
22445          * @static
22446          */
22447         getPosY: function(el) {
22448             return Roo.lib.Dom.getY(el);
22449         },
22450
22451         /**
22452          * Swap two nodes.  In IE, we use the native method, for others we
22453          * emulate the IE behavior
22454          * @method swapNode
22455          * @param n1 the first node to swap
22456          * @param n2 the other node to swap
22457          * @static
22458          */
22459         swapNode: function(n1, n2) {
22460             if (n1.swapNode) {
22461                 n1.swapNode(n2);
22462             } else {
22463                 var p = n2.parentNode;
22464                 var s = n2.nextSibling;
22465
22466                 if (s == n1) {
22467                     p.insertBefore(n1, n2);
22468                 } else if (n2 == n1.nextSibling) {
22469                     p.insertBefore(n2, n1);
22470                 } else {
22471                     n1.parentNode.replaceChild(n2, n1);
22472                     p.insertBefore(n1, s);
22473                 }
22474             }
22475         },
22476
22477         /**
22478          * Returns the current scroll position
22479          * @method getScroll
22480          * @private
22481          * @static
22482          */
22483         getScroll: function () {
22484             var t, l, dde=document.documentElement, db=document.body;
22485             if (dde && (dde.scrollTop || dde.scrollLeft)) {
22486                 t = dde.scrollTop;
22487                 l = dde.scrollLeft;
22488             } else if (db) {
22489                 t = db.scrollTop;
22490                 l = db.scrollLeft;
22491             } else {
22492
22493             }
22494             return { top: t, left: l };
22495         },
22496
22497         /**
22498          * Returns the specified element style property
22499          * @method getStyle
22500          * @param {HTMLElement} el          the element
22501          * @param {string}      styleProp   the style property
22502          * @return {string} The value of the style property
22503          * @deprecated use Roo.lib.Dom.getStyle
22504          * @static
22505          */
22506         getStyle: function(el, styleProp) {
22507             return Roo.fly(el).getStyle(styleProp);
22508         },
22509
22510         /**
22511          * Gets the scrollTop
22512          * @method getScrollTop
22513          * @return {int} the document's scrollTop
22514          * @static
22515          */
22516         getScrollTop: function () { return this.getScroll().top; },
22517
22518         /**
22519          * Gets the scrollLeft
22520          * @method getScrollLeft
22521          * @return {int} the document's scrollTop
22522          * @static
22523          */
22524         getScrollLeft: function () { return this.getScroll().left; },
22525
22526         /**
22527          * Sets the x/y position of an element to the location of the
22528          * target element.
22529          * @method moveToEl
22530          * @param {HTMLElement} moveEl      The element to move
22531          * @param {HTMLElement} targetEl    The position reference element
22532          * @static
22533          */
22534         moveToEl: function (moveEl, targetEl) {
22535             var aCoord = Roo.lib.Dom.getXY(targetEl);
22536             Roo.lib.Dom.setXY(moveEl, aCoord);
22537         },
22538
22539         /**
22540          * Numeric array sort function
22541          * @method numericSort
22542          * @static
22543          */
22544         numericSort: function(a, b) { return (a - b); },
22545
22546         /**
22547          * Internal counter
22548          * @property _timeoutCount
22549          * @private
22550          * @static
22551          */
22552         _timeoutCount: 0,
22553
22554         /**
22555          * Trying to make the load order less important.  Without this we get
22556          * an error if this file is loaded before the Event Utility.
22557          * @method _addListeners
22558          * @private
22559          * @static
22560          */
22561         _addListeners: function() {
22562             var DDM = Roo.dd.DDM;
22563             if ( Roo.lib.Event && document ) {
22564                 DDM._onLoad();
22565             } else {
22566                 if (DDM._timeoutCount > 2000) {
22567                 } else {
22568                     setTimeout(DDM._addListeners, 10);
22569                     if (document && document.body) {
22570                         DDM._timeoutCount += 1;
22571                     }
22572                 }
22573             }
22574         },
22575
22576         /**
22577          * Recursively searches the immediate parent and all child nodes for
22578          * the handle element in order to determine wheter or not it was
22579          * clicked.
22580          * @method handleWasClicked
22581          * @param node the html element to inspect
22582          * @static
22583          */
22584         handleWasClicked: function(node, id) {
22585             if (this.isHandle(id, node.id)) {
22586                 return true;
22587             } else {
22588                 // check to see if this is a text node child of the one we want
22589                 var p = node.parentNode;
22590
22591                 while (p) {
22592                     if (this.isHandle(id, p.id)) {
22593                         return true;
22594                     } else {
22595                         p = p.parentNode;
22596                     }
22597                 }
22598             }
22599
22600             return false;
22601         }
22602
22603     };
22604
22605 }();
22606
22607 // shorter alias, save a few bytes
22608 Roo.dd.DDM = Roo.dd.DragDropMgr;
22609 Roo.dd.DDM._addListeners();
22610
22611 }/*
22612  * Based on:
22613  * Ext JS Library 1.1.1
22614  * Copyright(c) 2006-2007, Ext JS, LLC.
22615  *
22616  * Originally Released Under LGPL - original licence link has changed is not relivant.
22617  *
22618  * Fork - LGPL
22619  * <script type="text/javascript">
22620  */
22621
22622 /**
22623  * @class Roo.dd.DD
22624  * A DragDrop implementation where the linked element follows the
22625  * mouse cursor during a drag.
22626  * @extends Roo.dd.DragDrop
22627  * @constructor
22628  * @param {String} id the id of the linked element
22629  * @param {String} sGroup the group of related DragDrop items
22630  * @param {object} config an object containing configurable attributes
22631  *                Valid properties for DD:
22632  *                    scroll
22633  */
22634 Roo.dd.DD = function(id, sGroup, config) {
22635     if (id) {
22636         this.init(id, sGroup, config);
22637     }
22638 };
22639
22640 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
22641
22642     /**
22643      * When set to true, the utility automatically tries to scroll the browser
22644      * window wehn a drag and drop element is dragged near the viewport boundary.
22645      * Defaults to true.
22646      * @property scroll
22647      * @type boolean
22648      */
22649     scroll: true,
22650
22651     /**
22652      * Sets the pointer offset to the distance between the linked element's top
22653      * left corner and the location the element was clicked
22654      * @method autoOffset
22655      * @param {int} iPageX the X coordinate of the click
22656      * @param {int} iPageY the Y coordinate of the click
22657      */
22658     autoOffset: function(iPageX, iPageY) {
22659         var x = iPageX - this.startPageX;
22660         var y = iPageY - this.startPageY;
22661         this.setDelta(x, y);
22662     },
22663
22664     /**
22665      * Sets the pointer offset.  You can call this directly to force the
22666      * offset to be in a particular location (e.g., pass in 0,0 to set it
22667      * to the center of the object)
22668      * @method setDelta
22669      * @param {int} iDeltaX the distance from the left
22670      * @param {int} iDeltaY the distance from the top
22671      */
22672     setDelta: function(iDeltaX, iDeltaY) {
22673         this.deltaX = iDeltaX;
22674         this.deltaY = iDeltaY;
22675     },
22676
22677     /**
22678      * Sets the drag element to the location of the mousedown or click event,
22679      * maintaining the cursor location relative to the location on the element
22680      * that was clicked.  Override this if you want to place the element in a
22681      * location other than where the cursor is.
22682      * @method setDragElPos
22683      * @param {int} iPageX the X coordinate of the mousedown or drag event
22684      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22685      */
22686     setDragElPos: function(iPageX, iPageY) {
22687         // the first time we do this, we are going to check to make sure
22688         // the element has css positioning
22689
22690         var el = this.getDragEl();
22691         this.alignElWithMouse(el, iPageX, iPageY);
22692     },
22693
22694     /**
22695      * Sets the element to the location of the mousedown or click event,
22696      * maintaining the cursor location relative to the location on the element
22697      * that was clicked.  Override this if you want to place the element in a
22698      * location other than where the cursor is.
22699      * @method alignElWithMouse
22700      * @param {HTMLElement} el the element to move
22701      * @param {int} iPageX the X coordinate of the mousedown or drag event
22702      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22703      */
22704     alignElWithMouse: function(el, iPageX, iPageY) {
22705         var oCoord = this.getTargetCoord(iPageX, iPageY);
22706         var fly = el.dom ? el : Roo.fly(el);
22707         if (!this.deltaSetXY) {
22708             var aCoord = [oCoord.x, oCoord.y];
22709             fly.setXY(aCoord);
22710             var newLeft = fly.getLeft(true);
22711             var newTop  = fly.getTop(true);
22712             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
22713         } else {
22714             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
22715         }
22716
22717         this.cachePosition(oCoord.x, oCoord.y);
22718         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
22719         return oCoord;
22720     },
22721
22722     /**
22723      * Saves the most recent position so that we can reset the constraints and
22724      * tick marks on-demand.  We need to know this so that we can calculate the
22725      * number of pixels the element is offset from its original position.
22726      * @method cachePosition
22727      * @param iPageX the current x position (optional, this just makes it so we
22728      * don't have to look it up again)
22729      * @param iPageY the current y position (optional, this just makes it so we
22730      * don't have to look it up again)
22731      */
22732     cachePosition: function(iPageX, iPageY) {
22733         if (iPageX) {
22734             this.lastPageX = iPageX;
22735             this.lastPageY = iPageY;
22736         } else {
22737             var aCoord = Roo.lib.Dom.getXY(this.getEl());
22738             this.lastPageX = aCoord[0];
22739             this.lastPageY = aCoord[1];
22740         }
22741     },
22742
22743     /**
22744      * Auto-scroll the window if the dragged object has been moved beyond the
22745      * visible window boundary.
22746      * @method autoScroll
22747      * @param {int} x the drag element's x position
22748      * @param {int} y the drag element's y position
22749      * @param {int} h the height of the drag element
22750      * @param {int} w the width of the drag element
22751      * @private
22752      */
22753     autoScroll: function(x, y, h, w) {
22754
22755         if (this.scroll) {
22756             // The client height
22757             var clientH = Roo.lib.Dom.getViewWidth();
22758
22759             // The client width
22760             var clientW = Roo.lib.Dom.getViewHeight();
22761
22762             // The amt scrolled down
22763             var st = this.DDM.getScrollTop();
22764
22765             // The amt scrolled right
22766             var sl = this.DDM.getScrollLeft();
22767
22768             // Location of the bottom of the element
22769             var bot = h + y;
22770
22771             // Location of the right of the element
22772             var right = w + x;
22773
22774             // The distance from the cursor to the bottom of the visible area,
22775             // adjusted so that we don't scroll if the cursor is beyond the
22776             // element drag constraints
22777             var toBot = (clientH + st - y - this.deltaY);
22778
22779             // The distance from the cursor to the right of the visible area
22780             var toRight = (clientW + sl - x - this.deltaX);
22781
22782
22783             // How close to the edge the cursor must be before we scroll
22784             // var thresh = (document.all) ? 100 : 40;
22785             var thresh = 40;
22786
22787             // How many pixels to scroll per autoscroll op.  This helps to reduce
22788             // clunky scrolling. IE is more sensitive about this ... it needs this
22789             // value to be higher.
22790             var scrAmt = (document.all) ? 80 : 30;
22791
22792             // Scroll down if we are near the bottom of the visible page and the
22793             // obj extends below the crease
22794             if ( bot > clientH && toBot < thresh ) {
22795                 window.scrollTo(sl, st + scrAmt);
22796             }
22797
22798             // Scroll up if the window is scrolled down and the top of the object
22799             // goes above the top border
22800             if ( y < st && st > 0 && y - st < thresh ) {
22801                 window.scrollTo(sl, st - scrAmt);
22802             }
22803
22804             // Scroll right if the obj is beyond the right border and the cursor is
22805             // near the border.
22806             if ( right > clientW && toRight < thresh ) {
22807                 window.scrollTo(sl + scrAmt, st);
22808             }
22809
22810             // Scroll left if the window has been scrolled to the right and the obj
22811             // extends past the left border
22812             if ( x < sl && sl > 0 && x - sl < thresh ) {
22813                 window.scrollTo(sl - scrAmt, st);
22814             }
22815         }
22816     },
22817
22818     /**
22819      * Finds the location the element should be placed if we want to move
22820      * it to where the mouse location less the click offset would place us.
22821      * @method getTargetCoord
22822      * @param {int} iPageX the X coordinate of the click
22823      * @param {int} iPageY the Y coordinate of the click
22824      * @return an object that contains the coordinates (Object.x and Object.y)
22825      * @private
22826      */
22827     getTargetCoord: function(iPageX, iPageY) {
22828
22829
22830         var x = iPageX - this.deltaX;
22831         var y = iPageY - this.deltaY;
22832
22833         if (this.constrainX) {
22834             if (x < this.minX) { x = this.minX; }
22835             if (x > this.maxX) { x = this.maxX; }
22836         }
22837
22838         if (this.constrainY) {
22839             if (y < this.minY) { y = this.minY; }
22840             if (y > this.maxY) { y = this.maxY; }
22841         }
22842
22843         x = this.getTick(x, this.xTicks);
22844         y = this.getTick(y, this.yTicks);
22845
22846
22847         return {x:x, y:y};
22848     },
22849
22850     /*
22851      * Sets up config options specific to this class. Overrides
22852      * Roo.dd.DragDrop, but all versions of this method through the
22853      * inheritance chain are called
22854      */
22855     applyConfig: function() {
22856         Roo.dd.DD.superclass.applyConfig.call(this);
22857         this.scroll = (this.config.scroll !== false);
22858     },
22859
22860     /*
22861      * Event that fires prior to the onMouseDown event.  Overrides
22862      * Roo.dd.DragDrop.
22863      */
22864     b4MouseDown: function(e) {
22865         // this.resetConstraints();
22866         this.autoOffset(e.getPageX(),
22867                             e.getPageY());
22868     },
22869
22870     /*
22871      * Event that fires prior to the onDrag event.  Overrides
22872      * Roo.dd.DragDrop.
22873      */
22874     b4Drag: function(e) {
22875         this.setDragElPos(e.getPageX(),
22876                             e.getPageY());
22877     },
22878
22879     toString: function() {
22880         return ("DD " + this.id);
22881     }
22882
22883     //////////////////////////////////////////////////////////////////////////
22884     // Debugging ygDragDrop events that can be overridden
22885     //////////////////////////////////////////////////////////////////////////
22886     /*
22887     startDrag: function(x, y) {
22888     },
22889
22890     onDrag: function(e) {
22891     },
22892
22893     onDragEnter: function(e, id) {
22894     },
22895
22896     onDragOver: function(e, id) {
22897     },
22898
22899     onDragOut: function(e, id) {
22900     },
22901
22902     onDragDrop: function(e, id) {
22903     },
22904
22905     endDrag: function(e) {
22906     }
22907
22908     */
22909
22910 });/*
22911  * Based on:
22912  * Ext JS Library 1.1.1
22913  * Copyright(c) 2006-2007, Ext JS, LLC.
22914  *
22915  * Originally Released Under LGPL - original licence link has changed is not relivant.
22916  *
22917  * Fork - LGPL
22918  * <script type="text/javascript">
22919  */
22920
22921 /**
22922  * @class Roo.dd.DDProxy
22923  * A DragDrop implementation that inserts an empty, bordered div into
22924  * the document that follows the cursor during drag operations.  At the time of
22925  * the click, the frame div is resized to the dimensions of the linked html
22926  * element, and moved to the exact location of the linked element.
22927  *
22928  * References to the "frame" element refer to the single proxy element that
22929  * was created to be dragged in place of all DDProxy elements on the
22930  * page.
22931  *
22932  * @extends Roo.dd.DD
22933  * @constructor
22934  * @param {String} id the id of the linked html element
22935  * @param {String} sGroup the group of related DragDrop objects
22936  * @param {object} config an object containing configurable attributes
22937  *                Valid properties for DDProxy in addition to those in DragDrop:
22938  *                   resizeFrame, centerFrame, dragElId
22939  */
22940 Roo.dd.DDProxy = function(id, sGroup, config) {
22941     if (id) {
22942         this.init(id, sGroup, config);
22943         this.initFrame();
22944     }
22945 };
22946
22947 /**
22948  * The default drag frame div id
22949  * @property Roo.dd.DDProxy.dragElId
22950  * @type String
22951  * @static
22952  */
22953 Roo.dd.DDProxy.dragElId = "ygddfdiv";
22954
22955 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
22956
22957     /**
22958      * By default we resize the drag frame to be the same size as the element
22959      * we want to drag (this is to get the frame effect).  We can turn it off
22960      * if we want a different behavior.
22961      * @property resizeFrame
22962      * @type boolean
22963      */
22964     resizeFrame: true,
22965
22966     /**
22967      * By default the frame is positioned exactly where the drag element is, so
22968      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
22969      * you do not have constraints on the obj is to have the drag frame centered
22970      * around the cursor.  Set centerFrame to true for this effect.
22971      * @property centerFrame
22972      * @type boolean
22973      */
22974     centerFrame: false,
22975
22976     /**
22977      * Creates the proxy element if it does not yet exist
22978      * @method createFrame
22979      */
22980     createFrame: function() {
22981         var self = this;
22982         var body = document.body;
22983
22984         if (!body || !body.firstChild) {
22985             setTimeout( function() { self.createFrame(); }, 50 );
22986             return;
22987         }
22988
22989         var div = this.getDragEl();
22990
22991         if (!div) {
22992             div    = document.createElement("div");
22993             div.id = this.dragElId;
22994             var s  = div.style;
22995
22996             s.position   = "absolute";
22997             s.visibility = "hidden";
22998             s.cursor     = "move";
22999             s.border     = "2px solid #aaa";
23000             s.zIndex     = 999;
23001
23002             // appendChild can blow up IE if invoked prior to the window load event
23003             // while rendering a table.  It is possible there are other scenarios
23004             // that would cause this to happen as well.
23005             body.insertBefore(div, body.firstChild);
23006         }
23007     },
23008
23009     /**
23010      * Initialization for the drag frame element.  Must be called in the
23011      * constructor of all subclasses
23012      * @method initFrame
23013      */
23014     initFrame: function() {
23015         this.createFrame();
23016     },
23017
23018     applyConfig: function() {
23019         Roo.dd.DDProxy.superclass.applyConfig.call(this);
23020
23021         this.resizeFrame = (this.config.resizeFrame !== false);
23022         this.centerFrame = (this.config.centerFrame);
23023         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
23024     },
23025
23026     /**
23027      * Resizes the drag frame to the dimensions of the clicked object, positions
23028      * it over the object, and finally displays it
23029      * @method showFrame
23030      * @param {int} iPageX X click position
23031      * @param {int} iPageY Y click position
23032      * @private
23033      */
23034     showFrame: function(iPageX, iPageY) {
23035         var el = this.getEl();
23036         var dragEl = this.getDragEl();
23037         var s = dragEl.style;
23038
23039         this._resizeProxy();
23040
23041         if (this.centerFrame) {
23042             this.setDelta( Math.round(parseInt(s.width,  10)/2),
23043                            Math.round(parseInt(s.height, 10)/2) );
23044         }
23045
23046         this.setDragElPos(iPageX, iPageY);
23047
23048         Roo.fly(dragEl).show();
23049     },
23050
23051     /**
23052      * The proxy is automatically resized to the dimensions of the linked
23053      * element when a drag is initiated, unless resizeFrame is set to false
23054      * @method _resizeProxy
23055      * @private
23056      */
23057     _resizeProxy: function() {
23058         if (this.resizeFrame) {
23059             var el = this.getEl();
23060             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
23061         }
23062     },
23063
23064     // overrides Roo.dd.DragDrop
23065     b4MouseDown: function(e) {
23066         var x = e.getPageX();
23067         var y = e.getPageY();
23068         this.autoOffset(x, y);
23069         this.setDragElPos(x, y);
23070     },
23071
23072     // overrides Roo.dd.DragDrop
23073     b4StartDrag: function(x, y) {
23074         // show the drag frame
23075         this.showFrame(x, y);
23076     },
23077
23078     // overrides Roo.dd.DragDrop
23079     b4EndDrag: function(e) {
23080         Roo.fly(this.getDragEl()).hide();
23081     },
23082
23083     // overrides Roo.dd.DragDrop
23084     // By default we try to move the element to the last location of the frame.
23085     // This is so that the default behavior mirrors that of Roo.dd.DD.
23086     endDrag: function(e) {
23087
23088         var lel = this.getEl();
23089         var del = this.getDragEl();
23090
23091         // Show the drag frame briefly so we can get its position
23092         del.style.visibility = "";
23093
23094         this.beforeMove();
23095         // Hide the linked element before the move to get around a Safari
23096         // rendering bug.
23097         lel.style.visibility = "hidden";
23098         Roo.dd.DDM.moveToEl(lel, del);
23099         del.style.visibility = "hidden";
23100         lel.style.visibility = "";
23101
23102         this.afterDrag();
23103     },
23104
23105     beforeMove : function(){
23106
23107     },
23108
23109     afterDrag : function(){
23110
23111     },
23112
23113     toString: function() {
23114         return ("DDProxy " + this.id);
23115     }
23116
23117 });
23118 /*
23119  * Based on:
23120  * Ext JS Library 1.1.1
23121  * Copyright(c) 2006-2007, Ext JS, LLC.
23122  *
23123  * Originally Released Under LGPL - original licence link has changed is not relivant.
23124  *
23125  * Fork - LGPL
23126  * <script type="text/javascript">
23127  */
23128
23129  /**
23130  * @class Roo.dd.DDTarget
23131  * A DragDrop implementation that does not move, but can be a drop
23132  * target.  You would get the same result by simply omitting implementation
23133  * for the event callbacks, but this way we reduce the processing cost of the
23134  * event listener and the callbacks.
23135  * @extends Roo.dd.DragDrop
23136  * @constructor
23137  * @param {String} id the id of the element that is a drop target
23138  * @param {String} sGroup the group of related DragDrop objects
23139  * @param {object} config an object containing configurable attributes
23140  *                 Valid properties for DDTarget in addition to those in
23141  *                 DragDrop:
23142  *                    none
23143  */
23144 Roo.dd.DDTarget = function(id, sGroup, config) {
23145     if (id) {
23146         this.initTarget(id, sGroup, config);
23147     }
23148     if (config && (config.listeners || config.events)) { 
23149         Roo.dd.DragDrop.superclass.constructor.call(this,  { 
23150             listeners : config.listeners || {}, 
23151             events : config.events || {} 
23152         });    
23153     }
23154 };
23155
23156 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
23157 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
23158     toString: function() {
23159         return ("DDTarget " + this.id);
23160     }
23161 });
23162 /*
23163  * Based on:
23164  * Ext JS Library 1.1.1
23165  * Copyright(c) 2006-2007, Ext JS, LLC.
23166  *
23167  * Originally Released Under LGPL - original licence link has changed is not relivant.
23168  *
23169  * Fork - LGPL
23170  * <script type="text/javascript">
23171  */
23172  
23173
23174 /**
23175  * @class Roo.dd.ScrollManager
23176  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
23177  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
23178  * @static
23179  */
23180 Roo.dd.ScrollManager = function(){
23181     var ddm = Roo.dd.DragDropMgr;
23182     var els = {};
23183     var dragEl = null;
23184     var proc = {};
23185     
23186     
23187     
23188     var onStop = function(e){
23189         dragEl = null;
23190         clearProc();
23191     };
23192     
23193     var triggerRefresh = function(){
23194         if(ddm.dragCurrent){
23195              ddm.refreshCache(ddm.dragCurrent.groups);
23196         }
23197     };
23198     
23199     var doScroll = function(){
23200         if(ddm.dragCurrent){
23201             var dds = Roo.dd.ScrollManager;
23202             if(!dds.animate){
23203                 if(proc.el.scroll(proc.dir, dds.increment)){
23204                     triggerRefresh();
23205                 }
23206             }else{
23207                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
23208             }
23209         }
23210     };
23211     
23212     var clearProc = function(){
23213         if(proc.id){
23214             clearInterval(proc.id);
23215         }
23216         proc.id = 0;
23217         proc.el = null;
23218         proc.dir = "";
23219     };
23220     
23221     var startProc = function(el, dir){
23222          Roo.log('scroll startproc');
23223         clearProc();
23224         proc.el = el;
23225         proc.dir = dir;
23226         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
23227     };
23228     
23229     var onFire = function(e, isDrop){
23230        
23231         if(isDrop || !ddm.dragCurrent){ return; }
23232         var dds = Roo.dd.ScrollManager;
23233         if(!dragEl || dragEl != ddm.dragCurrent){
23234             dragEl = ddm.dragCurrent;
23235             // refresh regions on drag start
23236             dds.refreshCache();
23237         }
23238         
23239         var xy = Roo.lib.Event.getXY(e);
23240         var pt = new Roo.lib.Point(xy[0], xy[1]);
23241         for(var id in els){
23242             var el = els[id], r = el._region;
23243             if(r && r.contains(pt) && el.isScrollable()){
23244                 if(r.bottom - pt.y <= dds.thresh){
23245                     if(proc.el != el){
23246                         startProc(el, "down");
23247                     }
23248                     return;
23249                 }else if(r.right - pt.x <= dds.thresh){
23250                     if(proc.el != el){
23251                         startProc(el, "left");
23252                     }
23253                     return;
23254                 }else if(pt.y - r.top <= dds.thresh){
23255                     if(proc.el != el){
23256                         startProc(el, "up");
23257                     }
23258                     return;
23259                 }else if(pt.x - r.left <= dds.thresh){
23260                     if(proc.el != el){
23261                         startProc(el, "right");
23262                     }
23263                     return;
23264                 }
23265             }
23266         }
23267         clearProc();
23268     };
23269     
23270     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
23271     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
23272     
23273     return {
23274         /**
23275          * Registers new overflow element(s) to auto scroll
23276          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
23277          */
23278         register : function(el){
23279             if(el instanceof Array){
23280                 for(var i = 0, len = el.length; i < len; i++) {
23281                         this.register(el[i]);
23282                 }
23283             }else{
23284                 el = Roo.get(el);
23285                 els[el.id] = el;
23286             }
23287             Roo.dd.ScrollManager.els = els;
23288         },
23289         
23290         /**
23291          * Unregisters overflow element(s) so they are no longer scrolled
23292          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
23293          */
23294         unregister : function(el){
23295             if(el instanceof Array){
23296                 for(var i = 0, len = el.length; i < len; i++) {
23297                         this.unregister(el[i]);
23298                 }
23299             }else{
23300                 el = Roo.get(el);
23301                 delete els[el.id];
23302             }
23303         },
23304         
23305         /**
23306          * The number of pixels from the edge of a container the pointer needs to be to 
23307          * trigger scrolling (defaults to 25)
23308          * @type Number
23309          */
23310         thresh : 25,
23311         
23312         /**
23313          * The number of pixels to scroll in each scroll increment (defaults to 50)
23314          * @type Number
23315          */
23316         increment : 100,
23317         
23318         /**
23319          * The frequency of scrolls in milliseconds (defaults to 500)
23320          * @type Number
23321          */
23322         frequency : 500,
23323         
23324         /**
23325          * True to animate the scroll (defaults to true)
23326          * @type Boolean
23327          */
23328         animate: true,
23329         
23330         /**
23331          * The animation duration in seconds - 
23332          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
23333          * @type Number
23334          */
23335         animDuration: .4,
23336         
23337         /**
23338          * Manually trigger a cache refresh.
23339          */
23340         refreshCache : function(){
23341             for(var id in els){
23342                 if(typeof els[id] == 'object'){ // for people extending the object prototype
23343                     els[id]._region = els[id].getRegion();
23344                 }
23345             }
23346         }
23347     };
23348 }();/*
23349  * Based on:
23350  * Ext JS Library 1.1.1
23351  * Copyright(c) 2006-2007, Ext JS, LLC.
23352  *
23353  * Originally Released Under LGPL - original licence link has changed is not relivant.
23354  *
23355  * Fork - LGPL
23356  * <script type="text/javascript">
23357  */
23358  
23359
23360 /**
23361  * @class Roo.dd.Registry
23362  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
23363  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
23364  * @static
23365  */
23366 Roo.dd.Registry = function(){
23367     var elements = {}; 
23368     var handles = {}; 
23369     var autoIdSeed = 0;
23370
23371     var getId = function(el, autogen){
23372         if(typeof el == "string"){
23373             return el;
23374         }
23375         var id = el.id;
23376         if(!id && autogen !== false){
23377             id = "roodd-" + (++autoIdSeed);
23378             el.id = id;
23379         }
23380         return id;
23381     };
23382     
23383     return {
23384     /**
23385      * Register a drag drop element
23386      * @param {String|HTMLElement} element The id or DOM node to register
23387      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
23388      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
23389      * knows how to interpret, plus there are some specific properties known to the Registry that should be
23390      * populated in the data object (if applicable):
23391      * <pre>
23392 Value      Description<br />
23393 ---------  ------------------------------------------<br />
23394 handles    Array of DOM nodes that trigger dragging<br />
23395            for the element being registered<br />
23396 isHandle   True if the element passed in triggers<br />
23397            dragging itself, else false
23398 </pre>
23399      */
23400         register : function(el, data){
23401             data = data || {};
23402             if(typeof el == "string"){
23403                 el = document.getElementById(el);
23404             }
23405             data.ddel = el;
23406             elements[getId(el)] = data;
23407             if(data.isHandle !== false){
23408                 handles[data.ddel.id] = data;
23409             }
23410             if(data.handles){
23411                 var hs = data.handles;
23412                 for(var i = 0, len = hs.length; i < len; i++){
23413                         handles[getId(hs[i])] = data;
23414                 }
23415             }
23416         },
23417
23418     /**
23419      * Unregister a drag drop element
23420      * @param {String|HTMLElement}  element The id or DOM node to unregister
23421      */
23422         unregister : function(el){
23423             var id = getId(el, false);
23424             var data = elements[id];
23425             if(data){
23426                 delete elements[id];
23427                 if(data.handles){
23428                     var hs = data.handles;
23429                     for(var i = 0, len = hs.length; i < len; i++){
23430                         delete handles[getId(hs[i], false)];
23431                     }
23432                 }
23433             }
23434         },
23435
23436     /**
23437      * Returns the handle registered for a DOM Node by id
23438      * @param {String|HTMLElement} id The DOM node or id to look up
23439      * @return {Object} handle The custom handle data
23440      */
23441         getHandle : function(id){
23442             if(typeof id != "string"){ // must be element?
23443                 id = id.id;
23444             }
23445             return handles[id];
23446         },
23447
23448     /**
23449      * Returns the handle that is registered for the DOM node that is the target of the event
23450      * @param {Event} e The event
23451      * @return {Object} handle The custom handle data
23452      */
23453         getHandleFromEvent : function(e){
23454             var t = Roo.lib.Event.getTarget(e);
23455             return t ? handles[t.id] : null;
23456         },
23457
23458     /**
23459      * Returns a custom data object that is registered for a DOM node by id
23460      * @param {String|HTMLElement} id The DOM node or id to look up
23461      * @return {Object} data The custom data
23462      */
23463         getTarget : function(id){
23464             if(typeof id != "string"){ // must be element?
23465                 id = id.id;
23466             }
23467             return elements[id];
23468         },
23469
23470     /**
23471      * Returns a custom data object that is registered for the DOM node that is the target of the event
23472      * @param {Event} e The event
23473      * @return {Object} data The custom data
23474      */
23475         getTargetFromEvent : function(e){
23476             var t = Roo.lib.Event.getTarget(e);
23477             return t ? elements[t.id] || handles[t.id] : null;
23478         }
23479     };
23480 }();/*
23481  * Based on:
23482  * Ext JS Library 1.1.1
23483  * Copyright(c) 2006-2007, Ext JS, LLC.
23484  *
23485  * Originally Released Under LGPL - original licence link has changed is not relivant.
23486  *
23487  * Fork - LGPL
23488  * <script type="text/javascript">
23489  */
23490  
23491
23492 /**
23493  * @class Roo.dd.StatusProxy
23494  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
23495  * default drag proxy used by all Roo.dd components.
23496  * @constructor
23497  * @param {Object} config
23498  */
23499 Roo.dd.StatusProxy = function(config){
23500     Roo.apply(this, config);
23501     this.id = this.id || Roo.id();
23502     this.el = new Roo.Layer({
23503         dh: {
23504             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
23505                 {tag: "div", cls: "x-dd-drop-icon"},
23506                 {tag: "div", cls: "x-dd-drag-ghost"}
23507             ]
23508         }, 
23509         shadow: !config || config.shadow !== false
23510     });
23511     this.ghost = Roo.get(this.el.dom.childNodes[1]);
23512     this.dropStatus = this.dropNotAllowed;
23513 };
23514
23515 Roo.dd.StatusProxy.prototype = {
23516     /**
23517      * @cfg {String} dropAllowed
23518      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
23519      */
23520     dropAllowed : "x-dd-drop-ok",
23521     /**
23522      * @cfg {String} dropNotAllowed
23523      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
23524      */
23525     dropNotAllowed : "x-dd-drop-nodrop",
23526
23527     /**
23528      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
23529      * over the current target element.
23530      * @param {String} cssClass The css class for the new drop status indicator image
23531      */
23532     setStatus : function(cssClass){
23533         cssClass = cssClass || this.dropNotAllowed;
23534         if(this.dropStatus != cssClass){
23535             this.el.replaceClass(this.dropStatus, cssClass);
23536             this.dropStatus = cssClass;
23537         }
23538     },
23539
23540     /**
23541      * Resets the status indicator to the default dropNotAllowed value
23542      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
23543      */
23544     reset : function(clearGhost){
23545         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
23546         this.dropStatus = this.dropNotAllowed;
23547         if(clearGhost){
23548             this.ghost.update("");
23549         }
23550     },
23551
23552     /**
23553      * Updates the contents of the ghost element
23554      * @param {String} html The html that will replace the current innerHTML of the ghost element
23555      */
23556     update : function(html){
23557         if(typeof html == "string"){
23558             this.ghost.update(html);
23559         }else{
23560             this.ghost.update("");
23561             html.style.margin = "0";
23562             this.ghost.dom.appendChild(html);
23563         }
23564         // ensure float = none set?? cant remember why though.
23565         var el = this.ghost.dom.firstChild;
23566                 if(el){
23567                         Roo.fly(el).setStyle('float', 'none');
23568                 }
23569     },
23570     
23571     /**
23572      * Returns the underlying proxy {@link Roo.Layer}
23573      * @return {Roo.Layer} el
23574     */
23575     getEl : function(){
23576         return this.el;
23577     },
23578
23579     /**
23580      * Returns the ghost element
23581      * @return {Roo.Element} el
23582      */
23583     getGhost : function(){
23584         return this.ghost;
23585     },
23586
23587     /**
23588      * Hides the proxy
23589      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
23590      */
23591     hide : function(clear){
23592         this.el.hide();
23593         if(clear){
23594             this.reset(true);
23595         }
23596     },
23597
23598     /**
23599      * Stops the repair animation if it's currently running
23600      */
23601     stop : function(){
23602         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
23603             this.anim.stop();
23604         }
23605     },
23606
23607     /**
23608      * Displays this proxy
23609      */
23610     show : function(){
23611         this.el.show();
23612     },
23613
23614     /**
23615      * Force the Layer to sync its shadow and shim positions to the element
23616      */
23617     sync : function(){
23618         this.el.sync();
23619     },
23620
23621     /**
23622      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
23623      * invalid drop operation by the item being dragged.
23624      * @param {Array} xy The XY position of the element ([x, y])
23625      * @param {Function} callback The function to call after the repair is complete
23626      * @param {Object} scope The scope in which to execute the callback
23627      */
23628     repair : function(xy, callback, scope){
23629         this.callback = callback;
23630         this.scope = scope;
23631         if(xy && this.animRepair !== false){
23632             this.el.addClass("x-dd-drag-repair");
23633             this.el.hideUnders(true);
23634             this.anim = this.el.shift({
23635                 duration: this.repairDuration || .5,
23636                 easing: 'easeOut',
23637                 xy: xy,
23638                 stopFx: true,
23639                 callback: this.afterRepair,
23640                 scope: this
23641             });
23642         }else{
23643             this.afterRepair();
23644         }
23645     },
23646
23647     // private
23648     afterRepair : function(){
23649         this.hide(true);
23650         if(typeof this.callback == "function"){
23651             this.callback.call(this.scope || this);
23652         }
23653         this.callback = null;
23654         this.scope = null;
23655     }
23656 };/*
23657  * Based on:
23658  * Ext JS Library 1.1.1
23659  * Copyright(c) 2006-2007, Ext JS, LLC.
23660  *
23661  * Originally Released Under LGPL - original licence link has changed is not relivant.
23662  *
23663  * Fork - LGPL
23664  * <script type="text/javascript">
23665  */
23666
23667 /**
23668  * @class Roo.dd.DragSource
23669  * @extends Roo.dd.DDProxy
23670  * A simple class that provides the basic implementation needed to make any element draggable.
23671  * @constructor
23672  * @param {String/HTMLElement/Element} el The container element
23673  * @param {Object} config
23674  */
23675 Roo.dd.DragSource = function(el, config){
23676     this.el = Roo.get(el);
23677     this.dragData = {};
23678     
23679     Roo.apply(this, config);
23680     
23681     if(!this.proxy){
23682         this.proxy = new Roo.dd.StatusProxy();
23683     }
23684
23685     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
23686           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
23687     
23688     this.dragging = false;
23689 };
23690
23691 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
23692     /**
23693      * @cfg {String} dropAllowed
23694      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23695      */
23696     dropAllowed : "x-dd-drop-ok",
23697     /**
23698      * @cfg {String} dropNotAllowed
23699      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23700      */
23701     dropNotAllowed : "x-dd-drop-nodrop",
23702
23703     /**
23704      * Returns the data object associated with this drag source
23705      * @return {Object} data An object containing arbitrary data
23706      */
23707     getDragData : function(e){
23708         return this.dragData;
23709     },
23710
23711     // private
23712     onDragEnter : function(e, id){
23713         var target = Roo.dd.DragDropMgr.getDDById(id);
23714         this.cachedTarget = target;
23715         if(this.beforeDragEnter(target, e, id) !== false){
23716             if(target.isNotifyTarget){
23717                 var status = target.notifyEnter(this, e, this.dragData);
23718                 this.proxy.setStatus(status);
23719             }else{
23720                 this.proxy.setStatus(this.dropAllowed);
23721             }
23722             
23723             if(this.afterDragEnter){
23724                 /**
23725                  * An empty function by default, but provided so that you can perform a custom action
23726                  * when the dragged item enters the drop target by providing an implementation.
23727                  * @param {Roo.dd.DragDrop} target The drop target
23728                  * @param {Event} e The event object
23729                  * @param {String} id The id of the dragged element
23730                  * @method afterDragEnter
23731                  */
23732                 this.afterDragEnter(target, e, id);
23733             }
23734         }
23735     },
23736
23737     /**
23738      * An empty function by default, but provided so that you can perform a custom action
23739      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
23740      * @param {Roo.dd.DragDrop} target The drop target
23741      * @param {Event} e The event object
23742      * @param {String} id The id of the dragged element
23743      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23744      */
23745     beforeDragEnter : function(target, e, id){
23746         return true;
23747     },
23748
23749     // private
23750     alignElWithMouse: function() {
23751         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
23752         this.proxy.sync();
23753     },
23754
23755     // private
23756     onDragOver : function(e, id){
23757         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23758         if(this.beforeDragOver(target, e, id) !== false){
23759             if(target.isNotifyTarget){
23760                 var status = target.notifyOver(this, e, this.dragData);
23761                 this.proxy.setStatus(status);
23762             }
23763
23764             if(this.afterDragOver){
23765                 /**
23766                  * An empty function by default, but provided so that you can perform a custom action
23767                  * while the dragged item is over the drop target by providing an implementation.
23768                  * @param {Roo.dd.DragDrop} target The drop target
23769                  * @param {Event} e The event object
23770                  * @param {String} id The id of the dragged element
23771                  * @method afterDragOver
23772                  */
23773                 this.afterDragOver(target, e, id);
23774             }
23775         }
23776     },
23777
23778     /**
23779      * An empty function by default, but provided so that you can perform a custom action
23780      * while the dragged item is over the drop target and optionally cancel the onDragOver.
23781      * @param {Roo.dd.DragDrop} target The drop target
23782      * @param {Event} e The event object
23783      * @param {String} id The id of the dragged element
23784      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23785      */
23786     beforeDragOver : function(target, e, id){
23787         return true;
23788     },
23789
23790     // private
23791     onDragOut : function(e, id){
23792         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23793         if(this.beforeDragOut(target, e, id) !== false){
23794             if(target.isNotifyTarget){
23795                 target.notifyOut(this, e, this.dragData);
23796             }
23797             this.proxy.reset();
23798             if(this.afterDragOut){
23799                 /**
23800                  * An empty function by default, but provided so that you can perform a custom action
23801                  * after the dragged item is dragged out of the target without dropping.
23802                  * @param {Roo.dd.DragDrop} target The drop target
23803                  * @param {Event} e The event object
23804                  * @param {String} id The id of the dragged element
23805                  * @method afterDragOut
23806                  */
23807                 this.afterDragOut(target, e, id);
23808             }
23809         }
23810         this.cachedTarget = null;
23811     },
23812
23813     /**
23814      * An empty function by default, but provided so that you can perform a custom action before the dragged
23815      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
23816      * @param {Roo.dd.DragDrop} target The drop target
23817      * @param {Event} e The event object
23818      * @param {String} id The id of the dragged element
23819      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23820      */
23821     beforeDragOut : function(target, e, id){
23822         return true;
23823     },
23824     
23825     // private
23826     onDragDrop : function(e, id){
23827         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23828         if(this.beforeDragDrop(target, e, id) !== false){
23829             if(target.isNotifyTarget){
23830                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
23831                     this.onValidDrop(target, e, id);
23832                 }else{
23833                     this.onInvalidDrop(target, e, id);
23834                 }
23835             }else{
23836                 this.onValidDrop(target, e, id);
23837             }
23838             
23839             if(this.afterDragDrop){
23840                 /**
23841                  * An empty function by default, but provided so that you can perform a custom action
23842                  * after a valid drag drop has occurred by providing an implementation.
23843                  * @param {Roo.dd.DragDrop} target The drop target
23844                  * @param {Event} e The event object
23845                  * @param {String} id The id of the dropped element
23846                  * @method afterDragDrop
23847                  */
23848                 this.afterDragDrop(target, e, id);
23849             }
23850         }
23851         delete this.cachedTarget;
23852     },
23853
23854     /**
23855      * An empty function by default, but provided so that you can perform a custom action before the dragged
23856      * item is dropped onto the target and optionally cancel the onDragDrop.
23857      * @param {Roo.dd.DragDrop} target The drop target
23858      * @param {Event} e The event object
23859      * @param {String} id The id of the dragged element
23860      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
23861      */
23862     beforeDragDrop : function(target, e, id){
23863         return true;
23864     },
23865
23866     // private
23867     onValidDrop : function(target, e, id){
23868         this.hideProxy();
23869         if(this.afterValidDrop){
23870             /**
23871              * An empty function by default, but provided so that you can perform a custom action
23872              * after a valid drop has occurred by providing an implementation.
23873              * @param {Object} target The target DD 
23874              * @param {Event} e The event object
23875              * @param {String} id The id of the dropped element
23876              * @method afterInvalidDrop
23877              */
23878             this.afterValidDrop(target, e, id);
23879         }
23880     },
23881
23882     // private
23883     getRepairXY : function(e, data){
23884         return this.el.getXY();  
23885     },
23886
23887     // private
23888     onInvalidDrop : function(target, e, id){
23889         this.beforeInvalidDrop(target, e, id);
23890         if(this.cachedTarget){
23891             if(this.cachedTarget.isNotifyTarget){
23892                 this.cachedTarget.notifyOut(this, e, this.dragData);
23893             }
23894             this.cacheTarget = null;
23895         }
23896         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
23897
23898         if(this.afterInvalidDrop){
23899             /**
23900              * An empty function by default, but provided so that you can perform a custom action
23901              * after an invalid drop has occurred by providing an implementation.
23902              * @param {Event} e The event object
23903              * @param {String} id The id of the dropped element
23904              * @method afterInvalidDrop
23905              */
23906             this.afterInvalidDrop(e, id);
23907         }
23908     },
23909
23910     // private
23911     afterRepair : function(){
23912         if(Roo.enableFx){
23913             this.el.highlight(this.hlColor || "c3daf9");
23914         }
23915         this.dragging = false;
23916     },
23917
23918     /**
23919      * An empty function by default, but provided so that you can perform a custom action after an invalid
23920      * drop has occurred.
23921      * @param {Roo.dd.DragDrop} target The drop target
23922      * @param {Event} e The event object
23923      * @param {String} id The id of the dragged element
23924      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
23925      */
23926     beforeInvalidDrop : function(target, e, id){
23927         return true;
23928     },
23929
23930     // private
23931     handleMouseDown : function(e){
23932         if(this.dragging) {
23933             return;
23934         }
23935         var data = this.getDragData(e);
23936         if(data && this.onBeforeDrag(data, e) !== false){
23937             this.dragData = data;
23938             this.proxy.stop();
23939             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
23940         } 
23941     },
23942
23943     /**
23944      * An empty function by default, but provided so that you can perform a custom action before the initial
23945      * drag event begins and optionally cancel it.
23946      * @param {Object} data An object containing arbitrary data to be shared with drop targets
23947      * @param {Event} e The event object
23948      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23949      */
23950     onBeforeDrag : function(data, e){
23951         return true;
23952     },
23953
23954     /**
23955      * An empty function by default, but provided so that you can perform a custom action once the initial
23956      * drag event has begun.  The drag cannot be canceled from this function.
23957      * @param {Number} x The x position of the click on the dragged object
23958      * @param {Number} y The y position of the click on the dragged object
23959      */
23960     onStartDrag : Roo.emptyFn,
23961
23962     // private - YUI override
23963     startDrag : function(x, y){
23964         this.proxy.reset();
23965         this.dragging = true;
23966         this.proxy.update("");
23967         this.onInitDrag(x, y);
23968         this.proxy.show();
23969     },
23970
23971     // private
23972     onInitDrag : function(x, y){
23973         var clone = this.el.dom.cloneNode(true);
23974         clone.id = Roo.id(); // prevent duplicate ids
23975         this.proxy.update(clone);
23976         this.onStartDrag(x, y);
23977         return true;
23978     },
23979
23980     /**
23981      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
23982      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
23983      */
23984     getProxy : function(){
23985         return this.proxy;  
23986     },
23987
23988     /**
23989      * Hides the drag source's {@link Roo.dd.StatusProxy}
23990      */
23991     hideProxy : function(){
23992         this.proxy.hide();  
23993         this.proxy.reset(true);
23994         this.dragging = false;
23995     },
23996
23997     // private
23998     triggerCacheRefresh : function(){
23999         Roo.dd.DDM.refreshCache(this.groups);
24000     },
24001
24002     // private - override to prevent hiding
24003     b4EndDrag: function(e) {
24004     },
24005
24006     // private - override to prevent moving
24007     endDrag : function(e){
24008         this.onEndDrag(this.dragData, e);
24009     },
24010
24011     // private
24012     onEndDrag : function(data, e){
24013     },
24014     
24015     // private - pin to cursor
24016     autoOffset : function(x, y) {
24017         this.setDelta(-12, -20);
24018     }    
24019 });/*
24020  * Based on:
24021  * Ext JS Library 1.1.1
24022  * Copyright(c) 2006-2007, Ext JS, LLC.
24023  *
24024  * Originally Released Under LGPL - original licence link has changed is not relivant.
24025  *
24026  * Fork - LGPL
24027  * <script type="text/javascript">
24028  */
24029
24030
24031 /**
24032  * @class Roo.dd.DropTarget
24033  * @extends Roo.dd.DDTarget
24034  * A simple class that provides the basic implementation needed to make any element a drop target that can have
24035  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
24036  * @constructor
24037  * @param {String/HTMLElement/Element} el The container element
24038  * @param {Object} config
24039  */
24040 Roo.dd.DropTarget = function(el, config){
24041     this.el = Roo.get(el);
24042     
24043     var listeners = false; ;
24044     if (config && config.listeners) {
24045         listeners= config.listeners;
24046         delete config.listeners;
24047     }
24048     Roo.apply(this, config);
24049     
24050     if(this.containerScroll){
24051         Roo.dd.ScrollManager.register(this.el);
24052     }
24053     this.addEvents( {
24054          /**
24055          * @scope Roo.dd.DropTarget
24056          */
24057          
24058          /**
24059          * @event enter
24060          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
24061          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
24062          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
24063          * 
24064          * IMPORTANT : it should set  this.valid to true|false
24065          * 
24066          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24067          * @param {Event} e The event
24068          * @param {Object} data An object containing arbitrary data supplied by the drag source
24069          */
24070         "enter" : true,
24071         
24072          /**
24073          * @event over
24074          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
24075          * This method will be called on every mouse movement while the drag source is over the drop target.
24076          * This default implementation simply returns the dropAllowed config value.
24077          * 
24078          * IMPORTANT : it should set  this.valid to true|false
24079          * 
24080          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24081          * @param {Event} e The event
24082          * @param {Object} data An object containing arbitrary data supplied by the drag source
24083          
24084          */
24085         "over" : true,
24086         /**
24087          * @event out
24088          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
24089          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
24090          * overClass (if any) from the drop element.
24091          * 
24092          * 
24093          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24094          * @param {Event} e The event
24095          * @param {Object} data An object containing arbitrary data supplied by the drag source
24096          */
24097          "out" : true,
24098          
24099         /**
24100          * @event drop
24101          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
24102          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
24103          * implementation that does something to process the drop event and returns true so that the drag source's
24104          * repair action does not run.
24105          * 
24106          * IMPORTANT : it should set this.success
24107          * 
24108          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24109          * @param {Event} e The event
24110          * @param {Object} data An object containing arbitrary data supplied by the drag source
24111         */
24112          "drop" : true
24113     });
24114             
24115      
24116     Roo.dd.DropTarget.superclass.constructor.call(  this, 
24117         this.el.dom, 
24118         this.ddGroup || this.group,
24119         {
24120             isTarget: true,
24121             listeners : listeners || {} 
24122            
24123         
24124         }
24125     );
24126
24127 };
24128
24129 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
24130     /**
24131      * @cfg {String} overClass
24132      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
24133      */
24134      /**
24135      * @cfg {String} ddGroup
24136      * The drag drop group to handle drop events for
24137      */
24138      
24139     /**
24140      * @cfg {String} dropAllowed
24141      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
24142      */
24143     dropAllowed : "x-dd-drop-ok",
24144     /**
24145      * @cfg {String} dropNotAllowed
24146      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
24147      */
24148     dropNotAllowed : "x-dd-drop-nodrop",
24149     /**
24150      * @cfg {boolean} success
24151      * set this after drop listener.. 
24152      */
24153     success : false,
24154     /**
24155      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
24156      * if the drop point is valid for over/enter..
24157      */
24158     valid : false,
24159     // private
24160     isTarget : true,
24161
24162     // private
24163     isNotifyTarget : true,
24164     
24165     /**
24166      * @hide
24167      */
24168     notifyEnter : function(dd, e, data)
24169     {
24170         this.valid = true;
24171         this.fireEvent('enter', dd, e, data);
24172         if(this.overClass){
24173             this.el.addClass(this.overClass);
24174         }
24175         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
24176             this.valid ? this.dropAllowed : this.dropNotAllowed
24177         );
24178     },
24179
24180     /**
24181      * @hide
24182      */
24183     notifyOver : function(dd, e, data)
24184     {
24185         this.valid = true;
24186         this.fireEvent('over', dd, e, data);
24187         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
24188             this.valid ? this.dropAllowed : this.dropNotAllowed
24189         );
24190     },
24191
24192     /**
24193      * @hide
24194      */
24195     notifyOut : function(dd, e, data)
24196     {
24197         this.fireEvent('out', dd, e, data);
24198         if(this.overClass){
24199             this.el.removeClass(this.overClass);
24200         }
24201     },
24202
24203     /**
24204      * @hide
24205      */
24206     notifyDrop : function(dd, e, data)
24207     {
24208         this.success = false;
24209         this.fireEvent('drop', dd, e, data);
24210         return this.success;
24211     }
24212 });/*
24213  * Based on:
24214  * Ext JS Library 1.1.1
24215  * Copyright(c) 2006-2007, Ext JS, LLC.
24216  *
24217  * Originally Released Under LGPL - original licence link has changed is not relivant.
24218  *
24219  * Fork - LGPL
24220  * <script type="text/javascript">
24221  */
24222
24223
24224 /**
24225  * @class Roo.dd.DragZone
24226  * @extends Roo.dd.DragSource
24227  * This class provides a container DD instance that proxies for multiple child node sources.<br />
24228  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
24229  * @constructor
24230  * @param {String/HTMLElement/Element} el The container element
24231  * @param {Object} config
24232  */
24233 Roo.dd.DragZone = function(el, config){
24234     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
24235     if(this.containerScroll){
24236         Roo.dd.ScrollManager.register(this.el);
24237     }
24238 };
24239
24240 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
24241     /**
24242      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
24243      * for auto scrolling during drag operations.
24244      */
24245     /**
24246      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
24247      * method after a failed drop (defaults to "c3daf9" - light blue)
24248      */
24249
24250     /**
24251      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
24252      * for a valid target to drag based on the mouse down. Override this method
24253      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
24254      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
24255      * @param {EventObject} e The mouse down event
24256      * @return {Object} The dragData
24257      */
24258     getDragData : function(e){
24259         return Roo.dd.Registry.getHandleFromEvent(e);
24260     },
24261     
24262     /**
24263      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
24264      * this.dragData.ddel
24265      * @param {Number} x The x position of the click on the dragged object
24266      * @param {Number} y The y position of the click on the dragged object
24267      * @return {Boolean} true to continue the drag, false to cancel
24268      */
24269     onInitDrag : function(x, y){
24270         this.proxy.update(this.dragData.ddel.cloneNode(true));
24271         this.onStartDrag(x, y);
24272         return true;
24273     },
24274     
24275     /**
24276      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
24277      */
24278     afterRepair : function(){
24279         if(Roo.enableFx){
24280             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
24281         }
24282         this.dragging = false;
24283     },
24284
24285     /**
24286      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
24287      * the XY of this.dragData.ddel
24288      * @param {EventObject} e The mouse up event
24289      * @return {Array} The xy location (e.g. [100, 200])
24290      */
24291     getRepairXY : function(e){
24292         return Roo.Element.fly(this.dragData.ddel).getXY();  
24293     }
24294 });/*
24295  * Based on:
24296  * Ext JS Library 1.1.1
24297  * Copyright(c) 2006-2007, Ext JS, LLC.
24298  *
24299  * Originally Released Under LGPL - original licence link has changed is not relivant.
24300  *
24301  * Fork - LGPL
24302  * <script type="text/javascript">
24303  */
24304 /**
24305  * @class Roo.dd.DropZone
24306  * @extends Roo.dd.DropTarget
24307  * This class provides a container DD instance that proxies for multiple child node targets.<br />
24308  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
24309  * @constructor
24310  * @param {String/HTMLElement/Element} el The container element
24311  * @param {Object} config
24312  */
24313 Roo.dd.DropZone = function(el, config){
24314     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
24315 };
24316
24317 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
24318     /**
24319      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
24320      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
24321      * provide your own custom lookup.
24322      * @param {Event} e The event
24323      * @return {Object} data The custom data
24324      */
24325     getTargetFromEvent : function(e){
24326         return Roo.dd.Registry.getTargetFromEvent(e);
24327     },
24328
24329     /**
24330      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
24331      * that it has registered.  This method has no default implementation and should be overridden to provide
24332      * node-specific processing if necessary.
24333      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
24334      * {@link #getTargetFromEvent} for this node)
24335      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24336      * @param {Event} e The event
24337      * @param {Object} data An object containing arbitrary data supplied by the drag source
24338      */
24339     onNodeEnter : function(n, dd, e, data){
24340         
24341     },
24342
24343     /**
24344      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
24345      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
24346      * overridden to provide the proper feedback.
24347      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24348      * {@link #getTargetFromEvent} for this node)
24349      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24350      * @param {Event} e The event
24351      * @param {Object} data An object containing arbitrary data supplied by the drag source
24352      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24353      * underlying {@link Roo.dd.StatusProxy} can be updated
24354      */
24355     onNodeOver : function(n, dd, e, data){
24356         return this.dropAllowed;
24357     },
24358
24359     /**
24360      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
24361      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
24362      * node-specific processing if necessary.
24363      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24364      * {@link #getTargetFromEvent} for this node)
24365      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24366      * @param {Event} e The event
24367      * @param {Object} data An object containing arbitrary data supplied by the drag source
24368      */
24369     onNodeOut : function(n, dd, e, data){
24370         
24371     },
24372
24373     /**
24374      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
24375      * the drop node.  The default implementation returns false, so it should be overridden to provide the
24376      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
24377      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24378      * {@link #getTargetFromEvent} for this node)
24379      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24380      * @param {Event} e The event
24381      * @param {Object} data An object containing arbitrary data supplied by the drag source
24382      * @return {Boolean} True if the drop was valid, else false
24383      */
24384     onNodeDrop : function(n, dd, e, data){
24385         return false;
24386     },
24387
24388     /**
24389      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
24390      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
24391      * it should be overridden to provide the proper feedback if necessary.
24392      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24393      * @param {Event} e The event
24394      * @param {Object} data An object containing arbitrary data supplied by the drag source
24395      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24396      * underlying {@link Roo.dd.StatusProxy} can be updated
24397      */
24398     onContainerOver : function(dd, e, data){
24399         return this.dropNotAllowed;
24400     },
24401
24402     /**
24403      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
24404      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
24405      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
24406      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
24407      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24408      * @param {Event} e The event
24409      * @param {Object} data An object containing arbitrary data supplied by the drag source
24410      * @return {Boolean} True if the drop was valid, else false
24411      */
24412     onContainerDrop : function(dd, e, data){
24413         return false;
24414     },
24415
24416     /**
24417      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
24418      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
24419      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
24420      * you should override this method and provide a custom implementation.
24421      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24422      * @param {Event} e The event
24423      * @param {Object} data An object containing arbitrary data supplied by the drag source
24424      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24425      * underlying {@link Roo.dd.StatusProxy} can be updated
24426      */
24427     notifyEnter : function(dd, e, data){
24428         return this.dropNotAllowed;
24429     },
24430
24431     /**
24432      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
24433      * This method will be called on every mouse movement while the drag source is over the drop zone.
24434      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
24435      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
24436      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
24437      * registered node, it will call {@link #onContainerOver}.
24438      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24439      * @param {Event} e The event
24440      * @param {Object} data An object containing arbitrary data supplied by the drag source
24441      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24442      * underlying {@link Roo.dd.StatusProxy} can be updated
24443      */
24444     notifyOver : function(dd, e, data){
24445         var n = this.getTargetFromEvent(e);
24446         if(!n){ // not over valid drop target
24447             if(this.lastOverNode){
24448                 this.onNodeOut(this.lastOverNode, dd, e, data);
24449                 this.lastOverNode = null;
24450             }
24451             return this.onContainerOver(dd, e, data);
24452         }
24453         if(this.lastOverNode != n){
24454             if(this.lastOverNode){
24455                 this.onNodeOut(this.lastOverNode, dd, e, data);
24456             }
24457             this.onNodeEnter(n, dd, e, data);
24458             this.lastOverNode = n;
24459         }
24460         return this.onNodeOver(n, dd, e, data);
24461     },
24462
24463     /**
24464      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
24465      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
24466      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
24467      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24468      * @param {Event} e The event
24469      * @param {Object} data An object containing arbitrary data supplied by the drag zone
24470      */
24471     notifyOut : function(dd, e, data){
24472         if(this.lastOverNode){
24473             this.onNodeOut(this.lastOverNode, dd, e, data);
24474             this.lastOverNode = null;
24475         }
24476     },
24477
24478     /**
24479      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
24480      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
24481      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
24482      * otherwise it will call {@link #onContainerDrop}.
24483      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24484      * @param {Event} e The event
24485      * @param {Object} data An object containing arbitrary data supplied by the drag source
24486      * @return {Boolean} True if the drop was valid, else false
24487      */
24488     notifyDrop : function(dd, e, data){
24489         if(this.lastOverNode){
24490             this.onNodeOut(this.lastOverNode, dd, e, data);
24491             this.lastOverNode = null;
24492         }
24493         var n = this.getTargetFromEvent(e);
24494         return n ?
24495             this.onNodeDrop(n, dd, e, data) :
24496             this.onContainerDrop(dd, e, data);
24497     },
24498
24499     // private
24500     triggerCacheRefresh : function(){
24501         Roo.dd.DDM.refreshCache(this.groups);
24502     }  
24503 });/*
24504  * Based on:
24505  * Ext JS Library 1.1.1
24506  * Copyright(c) 2006-2007, Ext JS, LLC.
24507  *
24508  * Originally Released Under LGPL - original licence link has changed is not relivant.
24509  *
24510  * Fork - LGPL
24511  * <script type="text/javascript">
24512  */
24513
24514
24515 /**
24516  * @class Roo.data.SortTypes
24517  * @static
24518  * Defines the default sorting (casting?) comparison functions used when sorting data.
24519  */
24520 Roo.data.SortTypes = {
24521     /**
24522      * Default sort that does nothing
24523      * @param {Mixed} s The value being converted
24524      * @return {Mixed} The comparison value
24525      */
24526     none : function(s){
24527         return s;
24528     },
24529     
24530     /**
24531      * The regular expression used to strip tags
24532      * @type {RegExp}
24533      * @property
24534      */
24535     stripTagsRE : /<\/?[^>]+>/gi,
24536     
24537     /**
24538      * Strips all HTML tags to sort on text only
24539      * @param {Mixed} s The value being converted
24540      * @return {String} The comparison value
24541      */
24542     asText : function(s){
24543         return String(s).replace(this.stripTagsRE, "");
24544     },
24545     
24546     /**
24547      * Strips all HTML tags to sort on text only - Case insensitive
24548      * @param {Mixed} s The value being converted
24549      * @return {String} The comparison value
24550      */
24551     asUCText : function(s){
24552         return String(s).toUpperCase().replace(this.stripTagsRE, "");
24553     },
24554     
24555     /**
24556      * Case insensitive string
24557      * @param {Mixed} s The value being converted
24558      * @return {String} The comparison value
24559      */
24560     asUCString : function(s) {
24561         return String(s).toUpperCase();
24562     },
24563     
24564     /**
24565      * Date sorting
24566      * @param {Mixed} s The value being converted
24567      * @return {Number} The comparison value
24568      */
24569     asDate : function(s) {
24570         if(!s){
24571             return 0;
24572         }
24573         if(s instanceof Date){
24574             return s.getTime();
24575         }
24576         return Date.parse(String(s));
24577     },
24578     
24579     /**
24580      * Float sorting
24581      * @param {Mixed} s The value being converted
24582      * @return {Float} The comparison value
24583      */
24584     asFloat : function(s) {
24585         var val = parseFloat(String(s).replace(/,/g, ""));
24586         if(isNaN(val)) {
24587             val = 0;
24588         }
24589         return val;
24590     },
24591     
24592     /**
24593      * Integer sorting
24594      * @param {Mixed} s The value being converted
24595      * @return {Number} The comparison value
24596      */
24597     asInt : function(s) {
24598         var val = parseInt(String(s).replace(/,/g, ""));
24599         if(isNaN(val)) {
24600             val = 0;
24601         }
24602         return val;
24603     }
24604 };/*
24605  * Based on:
24606  * Ext JS Library 1.1.1
24607  * Copyright(c) 2006-2007, Ext JS, LLC.
24608  *
24609  * Originally Released Under LGPL - original licence link has changed is not relivant.
24610  *
24611  * Fork - LGPL
24612  * <script type="text/javascript">
24613  */
24614
24615 /**
24616 * @class Roo.data.Record
24617  * Instances of this class encapsulate both record <em>definition</em> information, and record
24618  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
24619  * to access Records cached in an {@link Roo.data.Store} object.<br>
24620  * <p>
24621  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
24622  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
24623  * objects.<br>
24624  * <p>
24625  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
24626  * @constructor
24627  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
24628  * {@link #create}. The parameters are the same.
24629  * @param {Array} data An associative Array of data values keyed by the field name.
24630  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
24631  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
24632  * not specified an integer id is generated.
24633  */
24634 Roo.data.Record = function(data, id){
24635     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
24636     this.data = data;
24637 };
24638
24639 /**
24640  * Generate a constructor for a specific record layout.
24641  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
24642  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
24643  * Each field definition object may contain the following properties: <ul>
24644  * <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,
24645  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
24646  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
24647  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
24648  * is being used, then this is a string containing the javascript expression to reference the data relative to 
24649  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
24650  * to the data item relative to the record element. If the mapping expression is the same as the field name,
24651  * this may be omitted.</p></li>
24652  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
24653  * <ul><li>auto (Default, implies no conversion)</li>
24654  * <li>string</li>
24655  * <li>int</li>
24656  * <li>float</li>
24657  * <li>boolean</li>
24658  * <li>date</li></ul></p></li>
24659  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
24660  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
24661  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
24662  * by the Reader into an object that will be stored in the Record. It is passed the
24663  * following parameters:<ul>
24664  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
24665  * </ul></p></li>
24666  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
24667  * </ul>
24668  * <br>usage:<br><pre><code>
24669 var TopicRecord = Roo.data.Record.create(
24670     {name: 'title', mapping: 'topic_title'},
24671     {name: 'author', mapping: 'username'},
24672     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
24673     {name: 'lastPost', mapping: 'post_time', type: 'date'},
24674     {name: 'lastPoster', mapping: 'user2'},
24675     {name: 'excerpt', mapping: 'post_text'}
24676 );
24677
24678 var myNewRecord = new TopicRecord({
24679     title: 'Do my job please',
24680     author: 'noobie',
24681     totalPosts: 1,
24682     lastPost: new Date(),
24683     lastPoster: 'Animal',
24684     excerpt: 'No way dude!'
24685 });
24686 myStore.add(myNewRecord);
24687 </code></pre>
24688  * @method create
24689  * @static
24690  */
24691 Roo.data.Record.create = function(o){
24692     var f = function(){
24693         f.superclass.constructor.apply(this, arguments);
24694     };
24695     Roo.extend(f, Roo.data.Record);
24696     var p = f.prototype;
24697     p.fields = new Roo.util.MixedCollection(false, function(field){
24698         return field.name;
24699     });
24700     for(var i = 0, len = o.length; i < len; i++){
24701         p.fields.add(new Roo.data.Field(o[i]));
24702     }
24703     f.getField = function(name){
24704         return p.fields.get(name);  
24705     };
24706     return f;
24707 };
24708
24709 Roo.data.Record.AUTO_ID = 1000;
24710 Roo.data.Record.EDIT = 'edit';
24711 Roo.data.Record.REJECT = 'reject';
24712 Roo.data.Record.COMMIT = 'commit';
24713
24714 Roo.data.Record.prototype = {
24715     /**
24716      * Readonly flag - true if this record has been modified.
24717      * @type Boolean
24718      */
24719     dirty : false,
24720     editing : false,
24721     error: null,
24722     modified: null,
24723
24724     // private
24725     join : function(store){
24726         this.store = store;
24727     },
24728
24729     /**
24730      * Set the named field to the specified value.
24731      * @param {String} name The name of the field to set.
24732      * @param {Object} value The value to set the field to.
24733      */
24734     set : function(name, value){
24735         if(this.data[name] == value){
24736             return;
24737         }
24738         this.dirty = true;
24739         if(!this.modified){
24740             this.modified = {};
24741         }
24742         if(typeof this.modified[name] == 'undefined'){
24743             this.modified[name] = this.data[name];
24744         }
24745         this.data[name] = value;
24746         if(!this.editing && this.store){
24747             this.store.afterEdit(this);
24748         }       
24749     },
24750
24751     /**
24752      * Get the value of the named field.
24753      * @param {String} name The name of the field to get the value of.
24754      * @return {Object} The value of the field.
24755      */
24756     get : function(name){
24757         return this.data[name]; 
24758     },
24759
24760     // private
24761     beginEdit : function(){
24762         this.editing = true;
24763         this.modified = {}; 
24764     },
24765
24766     // private
24767     cancelEdit : function(){
24768         this.editing = false;
24769         delete this.modified;
24770     },
24771
24772     // private
24773     endEdit : function(){
24774         this.editing = false;
24775         if(this.dirty && this.store){
24776             this.store.afterEdit(this);
24777         }
24778     },
24779
24780     /**
24781      * Usually called by the {@link Roo.data.Store} which owns the Record.
24782      * Rejects all changes made to the Record since either creation, or the last commit operation.
24783      * Modified fields are reverted to their original values.
24784      * <p>
24785      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24786      * of reject operations.
24787      */
24788     reject : function(){
24789         var m = this.modified;
24790         for(var n in m){
24791             if(typeof m[n] != "function"){
24792                 this.data[n] = m[n];
24793             }
24794         }
24795         this.dirty = false;
24796         delete this.modified;
24797         this.editing = false;
24798         if(this.store){
24799             this.store.afterReject(this);
24800         }
24801     },
24802
24803     /**
24804      * Usually called by the {@link Roo.data.Store} which owns the Record.
24805      * Commits all changes made to the Record since either creation, or the last commit operation.
24806      * <p>
24807      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24808      * of commit operations.
24809      */
24810     commit : function(){
24811         this.dirty = false;
24812         delete this.modified;
24813         this.editing = false;
24814         if(this.store){
24815             this.store.afterCommit(this);
24816         }
24817     },
24818
24819     // private
24820     hasError : function(){
24821         return this.error != null;
24822     },
24823
24824     // private
24825     clearError : function(){
24826         this.error = null;
24827     },
24828
24829     /**
24830      * Creates a copy of this record.
24831      * @param {String} id (optional) A new record id if you don't want to use this record's id
24832      * @return {Record}
24833      */
24834     copy : function(newId) {
24835         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
24836     }
24837 };/*
24838  * Based on:
24839  * Ext JS Library 1.1.1
24840  * Copyright(c) 2006-2007, Ext JS, LLC.
24841  *
24842  * Originally Released Under LGPL - original licence link has changed is not relivant.
24843  *
24844  * Fork - LGPL
24845  * <script type="text/javascript">
24846  */
24847
24848
24849
24850 /**
24851  * @class Roo.data.Store
24852  * @extends Roo.util.Observable
24853  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
24854  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
24855  * <p>
24856  * 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
24857  * has no knowledge of the format of the data returned by the Proxy.<br>
24858  * <p>
24859  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
24860  * instances from the data object. These records are cached and made available through accessor functions.
24861  * @constructor
24862  * Creates a new Store.
24863  * @param {Object} config A config object containing the objects needed for the Store to access data,
24864  * and read the data into Records.
24865  */
24866 Roo.data.Store = function(config){
24867     this.data = new Roo.util.MixedCollection(false);
24868     this.data.getKey = function(o){
24869         return o.id;
24870     };
24871     this.baseParams = {};
24872     // private
24873     this.paramNames = {
24874         "start" : "start",
24875         "limit" : "limit",
24876         "sort" : "sort",
24877         "dir" : "dir",
24878         "multisort" : "_multisort"
24879     };
24880
24881     if(config && config.data){
24882         this.inlineData = config.data;
24883         delete config.data;
24884     }
24885
24886     Roo.apply(this, config);
24887     
24888     if(this.reader){ // reader passed
24889         this.reader = Roo.factory(this.reader, Roo.data);
24890         this.reader.xmodule = this.xmodule || false;
24891         if(!this.recordType){
24892             this.recordType = this.reader.recordType;
24893         }
24894         if(this.reader.onMetaChange){
24895             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
24896         }
24897     }
24898
24899     if(this.recordType){
24900         this.fields = this.recordType.prototype.fields;
24901     }
24902     this.modified = [];
24903
24904     this.addEvents({
24905         /**
24906          * @event datachanged
24907          * Fires when the data cache has changed, and a widget which is using this Store
24908          * as a Record cache should refresh its view.
24909          * @param {Store} this
24910          */
24911         datachanged : true,
24912         /**
24913          * @event metachange
24914          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
24915          * @param {Store} this
24916          * @param {Object} meta The JSON metadata
24917          */
24918         metachange : true,
24919         /**
24920          * @event add
24921          * Fires when Records have been added to the Store
24922          * @param {Store} this
24923          * @param {Roo.data.Record[]} records The array of Records added
24924          * @param {Number} index The index at which the record(s) were added
24925          */
24926         add : true,
24927         /**
24928          * @event remove
24929          * Fires when a Record has been removed from the Store
24930          * @param {Store} this
24931          * @param {Roo.data.Record} record The Record that was removed
24932          * @param {Number} index The index at which the record was removed
24933          */
24934         remove : true,
24935         /**
24936          * @event update
24937          * Fires when a Record has been updated
24938          * @param {Store} this
24939          * @param {Roo.data.Record} record The Record that was updated
24940          * @param {String} operation The update operation being performed.  Value may be one of:
24941          * <pre><code>
24942  Roo.data.Record.EDIT
24943  Roo.data.Record.REJECT
24944  Roo.data.Record.COMMIT
24945          * </code></pre>
24946          */
24947         update : true,
24948         /**
24949          * @event clear
24950          * Fires when the data cache has been cleared.
24951          * @param {Store} this
24952          */
24953         clear : true,
24954         /**
24955          * @event beforeload
24956          * Fires before a request is made for a new data object.  If the beforeload handler returns false
24957          * the load action will be canceled.
24958          * @param {Store} this
24959          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24960          */
24961         beforeload : true,
24962         /**
24963          * @event beforeloadadd
24964          * Fires after a new set of Records has been loaded.
24965          * @param {Store} this
24966          * @param {Roo.data.Record[]} records The Records that were loaded
24967          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24968          */
24969         beforeloadadd : true,
24970         /**
24971          * @event load
24972          * Fires after a new set of Records has been loaded, before they are added to the store.
24973          * @param {Store} this
24974          * @param {Roo.data.Record[]} records The Records that were loaded
24975          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24976          * @params {Object} return from reader
24977          */
24978         load : true,
24979         /**
24980          * @event loadexception
24981          * Fires if an exception occurs in the Proxy during loading.
24982          * Called with the signature of the Proxy's "loadexception" event.
24983          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
24984          * 
24985          * @param {Proxy} 
24986          * @param {Object} ret return data from JsonData.reader() - success, totalRecords, records
24987          * @param {Object} opts - load Options
24988          * @param {Object} jsonData from your request (normally this contains the Exception)
24989          */
24990         loadexception : true
24991     });
24992     
24993     if(this.proxy){
24994         this.proxy = Roo.factory(this.proxy, Roo.data);
24995         this.proxy.xmodule = this.xmodule || false;
24996         this.relayEvents(this.proxy,  ["loadexception"]);
24997     }
24998     this.sortToggle = {};
24999     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
25000
25001     Roo.data.Store.superclass.constructor.call(this);
25002
25003     if(this.inlineData){
25004         this.loadData(this.inlineData);
25005         delete this.inlineData;
25006     }
25007 };
25008
25009 Roo.extend(Roo.data.Store, Roo.util.Observable, {
25010      /**
25011     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
25012     * without a remote query - used by combo/forms at present.
25013     */
25014     
25015     /**
25016     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
25017     */
25018     /**
25019     * @cfg {Array} data Inline data to be loaded when the store is initialized.
25020     */
25021     /**
25022     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
25023     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
25024     */
25025     /**
25026     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
25027     * on any HTTP request
25028     */
25029     /**
25030     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
25031     */
25032     /**
25033     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
25034     */
25035     multiSort: false,
25036     /**
25037     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
25038     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
25039     */
25040     remoteSort : false,
25041
25042     /**
25043     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
25044      * loaded or when a record is removed. (defaults to false).
25045     */
25046     pruneModifiedRecords : false,
25047
25048     // private
25049     lastOptions : null,
25050
25051     /**
25052      * Add Records to the Store and fires the add event.
25053      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
25054      */
25055     add : function(records){
25056         records = [].concat(records);
25057         for(var i = 0, len = records.length; i < len; i++){
25058             records[i].join(this);
25059         }
25060         var index = this.data.length;
25061         this.data.addAll(records);
25062         this.fireEvent("add", this, records, index);
25063     },
25064
25065     /**
25066      * Remove a Record from the Store and fires the remove event.
25067      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
25068      */
25069     remove : function(record){
25070         var index = this.data.indexOf(record);
25071         this.data.removeAt(index);
25072  
25073         if(this.pruneModifiedRecords){
25074             this.modified.remove(record);
25075         }
25076         this.fireEvent("remove", this, record, index);
25077     },
25078
25079     /**
25080      * Remove all Records from the Store and fires the clear event.
25081      */
25082     removeAll : function(){
25083         this.data.clear();
25084         if(this.pruneModifiedRecords){
25085             this.modified = [];
25086         }
25087         this.fireEvent("clear", this);
25088     },
25089
25090     /**
25091      * Inserts Records to the Store at the given index and fires the add event.
25092      * @param {Number} index The start index at which to insert the passed Records.
25093      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
25094      */
25095     insert : function(index, records){
25096         records = [].concat(records);
25097         for(var i = 0, len = records.length; i < len; i++){
25098             this.data.insert(index, records[i]);
25099             records[i].join(this);
25100         }
25101         this.fireEvent("add", this, records, index);
25102     },
25103
25104     /**
25105      * Get the index within the cache of the passed Record.
25106      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
25107      * @return {Number} The index of the passed Record. Returns -1 if not found.
25108      */
25109     indexOf : function(record){
25110         return this.data.indexOf(record);
25111     },
25112
25113     /**
25114      * Get the index within the cache of the Record with the passed id.
25115      * @param {String} id The id of the Record to find.
25116      * @return {Number} The index of the Record. Returns -1 if not found.
25117      */
25118     indexOfId : function(id){
25119         return this.data.indexOfKey(id);
25120     },
25121
25122     /**
25123      * Get the Record with the specified id.
25124      * @param {String} id The id of the Record to find.
25125      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
25126      */
25127     getById : function(id){
25128         return this.data.key(id);
25129     },
25130
25131     /**
25132      * Get the Record at the specified index.
25133      * @param {Number} index The index of the Record to find.
25134      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
25135      */
25136     getAt : function(index){
25137         return this.data.itemAt(index);
25138     },
25139
25140     /**
25141      * Returns a range of Records between specified indices.
25142      * @param {Number} startIndex (optional) The starting index (defaults to 0)
25143      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
25144      * @return {Roo.data.Record[]} An array of Records
25145      */
25146     getRange : function(start, end){
25147         return this.data.getRange(start, end);
25148     },
25149
25150     // private
25151     storeOptions : function(o){
25152         o = Roo.apply({}, o);
25153         delete o.callback;
25154         delete o.scope;
25155         this.lastOptions = o;
25156     },
25157
25158     /**
25159      * Loads the Record cache from the configured Proxy using the configured Reader.
25160      * <p>
25161      * If using remote paging, then the first load call must specify the <em>start</em>
25162      * and <em>limit</em> properties in the options.params property to establish the initial
25163      * position within the dataset, and the number of Records to cache on each read from the Proxy.
25164      * <p>
25165      * <strong>It is important to note that for remote data sources, loading is asynchronous,
25166      * and this call will return before the new data has been loaded. Perform any post-processing
25167      * in a callback function, or in a "load" event handler.</strong>
25168      * <p>
25169      * @param {Object} options An object containing properties which control loading options:<ul>
25170      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
25171      * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
25172      * <pre>
25173                 {
25174                     data : data,  // array of key=>value data like JsonReader
25175                     total : data.length,
25176                     success : true
25177                     
25178                 }
25179         </pre>
25180             }.</li>
25181      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
25182      * passed the following arguments:<ul>
25183      * <li>r : Roo.data.Record[]</li>
25184      * <li>options: Options object from the load call</li>
25185      * <li>success: Boolean success indicator</li></ul></li>
25186      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
25187      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
25188      * </ul>
25189      */
25190     load : function(options){
25191         options = options || {};
25192         if(this.fireEvent("beforeload", this, options) !== false){
25193             this.storeOptions(options);
25194             var p = Roo.apply(options.params || {}, this.baseParams);
25195             // if meta was not loaded from remote source.. try requesting it.
25196             if (!this.reader.metaFromRemote) {
25197                 p._requestMeta = 1;
25198             }
25199             if(this.sortInfo && this.remoteSort){
25200                 var pn = this.paramNames;
25201                 p[pn["sort"]] = this.sortInfo.field;
25202                 p[pn["dir"]] = this.sortInfo.direction;
25203             }
25204             if (this.multiSort) {
25205                 var pn = this.paramNames;
25206                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
25207             }
25208             
25209             this.proxy.load(p, this.reader, this.loadRecords, this, options);
25210         }
25211     },
25212
25213     /**
25214      * Reloads the Record cache from the configured Proxy using the configured Reader and
25215      * the options from the last load operation performed.
25216      * @param {Object} options (optional) An object containing properties which may override the options
25217      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
25218      * the most recently used options are reused).
25219      */
25220     reload : function(options){
25221         this.load(Roo.applyIf(options||{}, this.lastOptions));
25222     },
25223
25224     // private
25225     // Called as a callback by the Reader during a load operation.
25226     loadRecords : function(o, options, success){
25227          
25228         if(!o){
25229             if(success !== false){
25230                 this.fireEvent("load", this, [], options, o);
25231             }
25232             if(options.callback){
25233                 options.callback.call(options.scope || this, [], options, false);
25234             }
25235             return;
25236         }
25237         // if data returned failure - throw an exception.
25238         if (o.success === false) {
25239             // show a message if no listener is registered.
25240             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
25241                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
25242             }
25243             // loadmask wil be hooked into this..
25244             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
25245             return;
25246         }
25247         var r = o.records, t = o.totalRecords || r.length;
25248         
25249         this.fireEvent("beforeloadadd", this, r, options, o);
25250         
25251         if(!options || options.add !== true){
25252             if(this.pruneModifiedRecords){
25253                 this.modified = [];
25254             }
25255             for(var i = 0, len = r.length; i < len; i++){
25256                 r[i].join(this);
25257             }
25258             if(this.snapshot){
25259                 this.data = this.snapshot;
25260                 delete this.snapshot;
25261             }
25262             this.data.clear();
25263             this.data.addAll(r);
25264             this.totalLength = t;
25265             this.applySort();
25266             this.fireEvent("datachanged", this);
25267         }else{
25268             this.totalLength = Math.max(t, this.data.length+r.length);
25269             this.add(r);
25270         }
25271         
25272         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
25273                 
25274             var e = new Roo.data.Record({});
25275
25276             e.set(this.parent.displayField, this.parent.emptyTitle);
25277             e.set(this.parent.valueField, '');
25278
25279             this.insert(0, e);
25280         }
25281             
25282         this.fireEvent("load", this, r, options, o);
25283         if(options.callback){
25284             options.callback.call(options.scope || this, r, options, true);
25285         }
25286     },
25287
25288
25289     /**
25290      * Loads data from a passed data block. A Reader which understands the format of the data
25291      * must have been configured in the constructor.
25292      * @param {Object} data The data block from which to read the Records.  The format of the data expected
25293      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
25294      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
25295      */
25296     loadData : function(o, append){
25297         var r = this.reader.readRecords(o);
25298         this.loadRecords(r, {add: append}, true);
25299     },
25300     
25301      /**
25302      * using 'cn' the nested child reader read the child array into it's child stores.
25303      * @param {Object} rec The record with a 'children array
25304      */
25305     loadDataFromChildren : function(rec)
25306     {
25307         this.loadData(this.reader.toLoadData(rec));
25308     },
25309     
25310
25311     /**
25312      * Gets the number of cached records.
25313      * <p>
25314      * <em>If using paging, this may not be the total size of the dataset. If the data object
25315      * used by the Reader contains the dataset size, then the getTotalCount() function returns
25316      * the data set size</em>
25317      */
25318     getCount : function(){
25319         return this.data.length || 0;
25320     },
25321
25322     /**
25323      * Gets the total number of records in the dataset as returned by the server.
25324      * <p>
25325      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
25326      * the dataset size</em>
25327      */
25328     getTotalCount : function(){
25329         return this.totalLength || 0;
25330     },
25331
25332     /**
25333      * Returns the sort state of the Store as an object with two properties:
25334      * <pre><code>
25335  field {String} The name of the field by which the Records are sorted
25336  direction {String} The sort order, "ASC" or "DESC"
25337      * </code></pre>
25338      */
25339     getSortState : function(){
25340         return this.sortInfo;
25341     },
25342
25343     // private
25344     applySort : function(){
25345         if(this.sortInfo && !this.remoteSort){
25346             var s = this.sortInfo, f = s.field;
25347             var st = this.fields.get(f).sortType;
25348             var fn = function(r1, r2){
25349                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
25350                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
25351             };
25352             this.data.sort(s.direction, fn);
25353             if(this.snapshot && this.snapshot != this.data){
25354                 this.snapshot.sort(s.direction, fn);
25355             }
25356         }
25357     },
25358
25359     /**
25360      * Sets the default sort column and order to be used by the next load operation.
25361      * @param {String} fieldName The name of the field to sort by.
25362      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
25363      */
25364     setDefaultSort : function(field, dir){
25365         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
25366     },
25367
25368     /**
25369      * Sort the Records.
25370      * If remote sorting is used, the sort is performed on the server, and the cache is
25371      * reloaded. If local sorting is used, the cache is sorted internally.
25372      * @param {String} fieldName The name of the field to sort by.
25373      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
25374      */
25375     sort : function(fieldName, dir){
25376         var f = this.fields.get(fieldName);
25377         if(!dir){
25378             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
25379             
25380             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
25381                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
25382             }else{
25383                 dir = f.sortDir;
25384             }
25385         }
25386         this.sortToggle[f.name] = dir;
25387         this.sortInfo = {field: f.name, direction: dir};
25388         if(!this.remoteSort){
25389             this.applySort();
25390             this.fireEvent("datachanged", this);
25391         }else{
25392             this.load(this.lastOptions);
25393         }
25394     },
25395
25396     /**
25397      * Calls the specified function for each of the Records in the cache.
25398      * @param {Function} fn The function to call. The Record is passed as the first parameter.
25399      * Returning <em>false</em> aborts and exits the iteration.
25400      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
25401      */
25402     each : function(fn, scope){
25403         this.data.each(fn, scope);
25404     },
25405
25406     /**
25407      * Gets all records modified since the last commit.  Modified records are persisted across load operations
25408      * (e.g., during paging).
25409      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
25410      */
25411     getModifiedRecords : function(){
25412         return this.modified;
25413     },
25414
25415     // private
25416     createFilterFn : function(property, value, anyMatch){
25417         if(!value.exec){ // not a regex
25418             value = String(value);
25419             if(value.length == 0){
25420                 return false;
25421             }
25422             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
25423         }
25424         return function(r){
25425             return value.test(r.data[property]);
25426         };
25427     },
25428
25429     /**
25430      * Sums the value of <i>property</i> for each record between start and end and returns the result.
25431      * @param {String} property A field on your records
25432      * @param {Number} start The record index to start at (defaults to 0)
25433      * @param {Number} end The last record index to include (defaults to length - 1)
25434      * @return {Number} The sum
25435      */
25436     sum : function(property, start, end){
25437         var rs = this.data.items, v = 0;
25438         start = start || 0;
25439         end = (end || end === 0) ? end : rs.length-1;
25440
25441         for(var i = start; i <= end; i++){
25442             v += (rs[i].data[property] || 0);
25443         }
25444         return v;
25445     },
25446
25447     /**
25448      * Filter the records by a specified property.
25449      * @param {String} field A field on your records
25450      * @param {String/RegExp} value Either a string that the field
25451      * should start with or a RegExp to test against the field
25452      * @param {Boolean} anyMatch True to match any part not just the beginning
25453      */
25454     filter : function(property, value, anyMatch){
25455         var fn = this.createFilterFn(property, value, anyMatch);
25456         return fn ? this.filterBy(fn) : this.clearFilter();
25457     },
25458
25459     /**
25460      * Filter by a function. The specified function will be called with each
25461      * record in this data source. If the function returns true the record is included,
25462      * otherwise it is filtered.
25463      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
25464      * @param {Object} scope (optional) The scope of the function (defaults to this)
25465      */
25466     filterBy : function(fn, scope){
25467         this.snapshot = this.snapshot || this.data;
25468         this.data = this.queryBy(fn, scope||this);
25469         this.fireEvent("datachanged", this);
25470     },
25471
25472     /**
25473      * Query the records by a specified property.
25474      * @param {String} field A field on your records
25475      * @param {String/RegExp} value Either a string that the field
25476      * should start with or a RegExp to test against the field
25477      * @param {Boolean} anyMatch True to match any part not just the beginning
25478      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
25479      */
25480     query : function(property, value, anyMatch){
25481         var fn = this.createFilterFn(property, value, anyMatch);
25482         return fn ? this.queryBy(fn) : this.data.clone();
25483     },
25484
25485     /**
25486      * Query by a function. The specified function will be called with each
25487      * record in this data source. If the function returns true the record is included
25488      * in the results.
25489      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
25490      * @param {Object} scope (optional) The scope of the function (defaults to this)
25491       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
25492      **/
25493     queryBy : function(fn, scope){
25494         var data = this.snapshot || this.data;
25495         return data.filterBy(fn, scope||this);
25496     },
25497
25498     /**
25499      * Collects unique values for a particular dataIndex from this store.
25500      * @param {String} dataIndex The property to collect
25501      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
25502      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
25503      * @return {Array} An array of the unique values
25504      **/
25505     collect : function(dataIndex, allowNull, bypassFilter){
25506         var d = (bypassFilter === true && this.snapshot) ?
25507                 this.snapshot.items : this.data.items;
25508         var v, sv, r = [], l = {};
25509         for(var i = 0, len = d.length; i < len; i++){
25510             v = d[i].data[dataIndex];
25511             sv = String(v);
25512             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
25513                 l[sv] = true;
25514                 r[r.length] = v;
25515             }
25516         }
25517         return r;
25518     },
25519
25520     /**
25521      * Revert to a view of the Record cache with no filtering applied.
25522      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
25523      */
25524     clearFilter : function(suppressEvent){
25525         if(this.snapshot && this.snapshot != this.data){
25526             this.data = this.snapshot;
25527             delete this.snapshot;
25528             if(suppressEvent !== true){
25529                 this.fireEvent("datachanged", this);
25530             }
25531         }
25532     },
25533
25534     // private
25535     afterEdit : function(record){
25536         if(this.modified.indexOf(record) == -1){
25537             this.modified.push(record);
25538         }
25539         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
25540     },
25541     
25542     // private
25543     afterReject : function(record){
25544         this.modified.remove(record);
25545         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
25546     },
25547
25548     // private
25549     afterCommit : function(record){
25550         this.modified.remove(record);
25551         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
25552     },
25553
25554     /**
25555      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
25556      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
25557      */
25558     commitChanges : function(){
25559         var m = this.modified.slice(0);
25560         this.modified = [];
25561         for(var i = 0, len = m.length; i < len; i++){
25562             m[i].commit();
25563         }
25564     },
25565
25566     /**
25567      * Cancel outstanding changes on all changed records.
25568      */
25569     rejectChanges : function(){
25570         var m = this.modified.slice(0);
25571         this.modified = [];
25572         for(var i = 0, len = m.length; i < len; i++){
25573             m[i].reject();
25574         }
25575     },
25576
25577     onMetaChange : function(meta, rtype, o){
25578         this.recordType = rtype;
25579         this.fields = rtype.prototype.fields;
25580         delete this.snapshot;
25581         this.sortInfo = meta.sortInfo || this.sortInfo;
25582         this.modified = [];
25583         this.fireEvent('metachange', this, this.reader.meta);
25584     },
25585     
25586     moveIndex : function(data, type)
25587     {
25588         var index = this.indexOf(data);
25589         
25590         var newIndex = index + type;
25591         
25592         this.remove(data);
25593         
25594         this.insert(newIndex, data);
25595         
25596     }
25597 });/*
25598  * Based on:
25599  * Ext JS Library 1.1.1
25600  * Copyright(c) 2006-2007, Ext JS, LLC.
25601  *
25602  * Originally Released Under LGPL - original licence link has changed is not relivant.
25603  *
25604  * Fork - LGPL
25605  * <script type="text/javascript">
25606  */
25607
25608 /**
25609  * @class Roo.data.SimpleStore
25610  * @extends Roo.data.Store
25611  * Small helper class to make creating Stores from Array data easier.
25612  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
25613  * @cfg {Array} fields An array of field definition objects, or field name strings.
25614  * @cfg {Object} an existing reader (eg. copied from another store)
25615  * @cfg {Array} data The multi-dimensional array of data
25616  * @cfg {Roo.data.DataProxy} proxy [not-required]  
25617  * @cfg {Roo.data.Reader} reader  [not-required] 
25618  * @constructor
25619  * @param {Object} config
25620  */
25621 Roo.data.SimpleStore = function(config)
25622 {
25623     Roo.data.SimpleStore.superclass.constructor.call(this, {
25624         isLocal : true,
25625         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
25626                 id: config.id
25627             },
25628             Roo.data.Record.create(config.fields)
25629         ),
25630         proxy : new Roo.data.MemoryProxy(config.data)
25631     });
25632     this.load();
25633 };
25634 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
25635  * Based on:
25636  * Ext JS Library 1.1.1
25637  * Copyright(c) 2006-2007, Ext JS, LLC.
25638  *
25639  * Originally Released Under LGPL - original licence link has changed is not relivant.
25640  *
25641  * Fork - LGPL
25642  * <script type="text/javascript">
25643  */
25644
25645 /**
25646 /**
25647  * @extends Roo.data.Store
25648  * @class Roo.data.JsonStore
25649  * Small helper class to make creating Stores for JSON data easier. <br/>
25650 <pre><code>
25651 var store = new Roo.data.JsonStore({
25652     url: 'get-images.php',
25653     root: 'images',
25654     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
25655 });
25656 </code></pre>
25657  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
25658  * JsonReader and HttpProxy (unless inline data is provided).</b>
25659  * @cfg {Array} fields An array of field definition objects, or field name strings.
25660  * @constructor
25661  * @param {Object} config
25662  */
25663 Roo.data.JsonStore = function(c){
25664     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
25665         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
25666         reader: new Roo.data.JsonReader(c, c.fields)
25667     }));
25668 };
25669 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
25670  * Based on:
25671  * Ext JS Library 1.1.1
25672  * Copyright(c) 2006-2007, Ext JS, LLC.
25673  *
25674  * Originally Released Under LGPL - original licence link has changed is not relivant.
25675  *
25676  * Fork - LGPL
25677  * <script type="text/javascript">
25678  */
25679
25680  
25681 Roo.data.Field = function(config){
25682     if(typeof config == "string"){
25683         config = {name: config};
25684     }
25685     Roo.apply(this, config);
25686     
25687     if(!this.type){
25688         this.type = "auto";
25689     }
25690     
25691     var st = Roo.data.SortTypes;
25692     // named sortTypes are supported, here we look them up
25693     if(typeof this.sortType == "string"){
25694         this.sortType = st[this.sortType];
25695     }
25696     
25697     // set default sortType for strings and dates
25698     if(!this.sortType){
25699         switch(this.type){
25700             case "string":
25701                 this.sortType = st.asUCString;
25702                 break;
25703             case "date":
25704                 this.sortType = st.asDate;
25705                 break;
25706             default:
25707                 this.sortType = st.none;
25708         }
25709     }
25710
25711     // define once
25712     var stripRe = /[\$,%]/g;
25713
25714     // prebuilt conversion function for this field, instead of
25715     // switching every time we're reading a value
25716     if(!this.convert){
25717         var cv, dateFormat = this.dateFormat;
25718         switch(this.type){
25719             case "":
25720             case "auto":
25721             case undefined:
25722                 cv = function(v){ return v; };
25723                 break;
25724             case "string":
25725                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
25726                 break;
25727             case "int":
25728                 cv = function(v){
25729                     return v !== undefined && v !== null && v !== '' ?
25730                            parseInt(String(v).replace(stripRe, ""), 10) : '';
25731                     };
25732                 break;
25733             case "float":
25734                 cv = function(v){
25735                     return v !== undefined && v !== null && v !== '' ?
25736                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
25737                     };
25738                 break;
25739             case "bool":
25740             case "boolean":
25741                 cv = function(v){ return v === true || v === "true" || v == 1; };
25742                 break;
25743             case "date":
25744                 cv = function(v){
25745                     if(!v){
25746                         return '';
25747                     }
25748                     if(v instanceof Date){
25749                         return v;
25750                     }
25751                     if(dateFormat){
25752                         if(dateFormat == "timestamp"){
25753                             return new Date(v*1000);
25754                         }
25755                         return Date.parseDate(v, dateFormat);
25756                     }
25757                     var parsed = Date.parse(v);
25758                     return parsed ? new Date(parsed) : null;
25759                 };
25760              break;
25761             
25762         }
25763         this.convert = cv;
25764     }
25765 };
25766
25767 Roo.data.Field.prototype = {
25768     dateFormat: null,
25769     defaultValue: "",
25770     mapping: null,
25771     sortType : null,
25772     sortDir : "ASC"
25773 };/*
25774  * Based on:
25775  * Ext JS Library 1.1.1
25776  * Copyright(c) 2006-2007, Ext JS, LLC.
25777  *
25778  * Originally Released Under LGPL - original licence link has changed is not relivant.
25779  *
25780  * Fork - LGPL
25781  * <script type="text/javascript">
25782  */
25783  
25784 // Base class for reading structured data from a data source.  This class is intended to be
25785 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
25786
25787 /**
25788  * @class Roo.data.DataReader
25789  * @abstract
25790  * Base class for reading structured data from a data source.  This class is intended to be
25791  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
25792  */
25793
25794 Roo.data.DataReader = function(meta, recordType){
25795     
25796     this.meta = meta;
25797     
25798     this.recordType = recordType instanceof Array ? 
25799         Roo.data.Record.create(recordType) : recordType;
25800 };
25801
25802 Roo.data.DataReader.prototype = {
25803     
25804     
25805     readerType : 'Data',
25806      /**
25807      * Create an empty record
25808      * @param {Object} data (optional) - overlay some values
25809      * @return {Roo.data.Record} record created.
25810      */
25811     newRow :  function(d) {
25812         var da =  {};
25813         this.recordType.prototype.fields.each(function(c) {
25814             switch( c.type) {
25815                 case 'int' : da[c.name] = 0; break;
25816                 case 'date' : da[c.name] = new Date(); break;
25817                 case 'float' : da[c.name] = 0.0; break;
25818                 case 'boolean' : da[c.name] = false; break;
25819                 default : da[c.name] = ""; break;
25820             }
25821             
25822         });
25823         return new this.recordType(Roo.apply(da, d));
25824     }
25825     
25826     
25827 };/*
25828  * Based on:
25829  * Ext JS Library 1.1.1
25830  * Copyright(c) 2006-2007, Ext JS, LLC.
25831  *
25832  * Originally Released Under LGPL - original licence link has changed is not relivant.
25833  *
25834  * Fork - LGPL
25835  * <script type="text/javascript">
25836  */
25837
25838 /**
25839  * @class Roo.data.DataProxy
25840  * @extends Roo.util.Observable
25841  * @abstract
25842  * This class is an abstract base class for implementations which provide retrieval of
25843  * unformatted data objects.<br>
25844  * <p>
25845  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
25846  * (of the appropriate type which knows how to parse the data object) to provide a block of
25847  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
25848  * <p>
25849  * Custom implementations must implement the load method as described in
25850  * {@link Roo.data.HttpProxy#load}.
25851  */
25852 Roo.data.DataProxy = function(){
25853     this.addEvents({
25854         /**
25855          * @event beforeload
25856          * Fires before a network request is made to retrieve a data object.
25857          * @param {Object} This DataProxy object.
25858          * @param {Object} params The params parameter to the load function.
25859          */
25860         beforeload : true,
25861         /**
25862          * @event load
25863          * Fires before the load method's callback is called.
25864          * @param {Object} This DataProxy object.
25865          * @param {Object} o The data object.
25866          * @param {Object} arg The callback argument object passed to the load function.
25867          */
25868         load : true,
25869         /**
25870          * @event loadexception
25871          * Fires if an Exception occurs during data retrieval.
25872          * @param {Object} This DataProxy object.
25873          * @param {Object} o The data object.
25874          * @param {Object} arg The callback argument object passed to the load function.
25875          * @param {Object} e The Exception.
25876          */
25877         loadexception : true
25878     });
25879     Roo.data.DataProxy.superclass.constructor.call(this);
25880 };
25881
25882 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
25883
25884     /**
25885      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
25886      */
25887 /*
25888  * Based on:
25889  * Ext JS Library 1.1.1
25890  * Copyright(c) 2006-2007, Ext JS, LLC.
25891  *
25892  * Originally Released Under LGPL - original licence link has changed is not relivant.
25893  *
25894  * Fork - LGPL
25895  * <script type="text/javascript">
25896  */
25897 /**
25898  * @class Roo.data.MemoryProxy
25899  * @extends Roo.data.DataProxy
25900  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
25901  * to the Reader when its load method is called.
25902  * @constructor
25903  * @param {Object} config  A config object containing the objects needed for the Store to access data,
25904  */
25905 Roo.data.MemoryProxy = function(config){
25906     var data = config;
25907     if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
25908         data = config.data;
25909     }
25910     Roo.data.MemoryProxy.superclass.constructor.call(this);
25911     this.data = data;
25912 };
25913
25914 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
25915     
25916     /**
25917      *  @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
25918      */
25919     /**
25920      * Load data from the requested source (in this case an in-memory
25921      * data object passed to the constructor), read the data object into
25922      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25923      * process that block using the passed callback.
25924      * @param {Object} params This parameter is not used by the MemoryProxy class.
25925      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25926      * object into a block of Roo.data.Records.
25927      * @param {Function} callback The function into which to pass the block of Roo.data.records.
25928      * The function must be passed <ul>
25929      * <li>The Record block object</li>
25930      * <li>The "arg" argument from the load function</li>
25931      * <li>A boolean success indicator</li>
25932      * </ul>
25933      * @param {Object} scope The scope in which to call the callback
25934      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25935      */
25936     load : function(params, reader, callback, scope, arg){
25937         params = params || {};
25938         var result;
25939         try {
25940             result = reader.readRecords(params.data ? params.data :this.data);
25941         }catch(e){
25942             this.fireEvent("loadexception", this, arg, null, e);
25943             callback.call(scope, null, arg, false);
25944             return;
25945         }
25946         callback.call(scope, result, arg, true);
25947     },
25948     
25949     // private
25950     update : function(params, records){
25951         
25952     }
25953 });/*
25954  * Based on:
25955  * Ext JS Library 1.1.1
25956  * Copyright(c) 2006-2007, Ext JS, LLC.
25957  *
25958  * Originally Released Under LGPL - original licence link has changed is not relivant.
25959  *
25960  * Fork - LGPL
25961  * <script type="text/javascript">
25962  */
25963 /**
25964  * @class Roo.data.HttpProxy
25965  * @extends Roo.data.DataProxy
25966  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
25967  * configured to reference a certain URL.<br><br>
25968  * <p>
25969  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
25970  * from which the running page was served.<br><br>
25971  * <p>
25972  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
25973  * <p>
25974  * Be aware that to enable the browser to parse an XML document, the server must set
25975  * the Content-Type header in the HTTP response to "text/xml".
25976  * @constructor
25977  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
25978  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
25979  * will be used to make the request.
25980  */
25981 Roo.data.HttpProxy = function(conn){
25982     Roo.data.HttpProxy.superclass.constructor.call(this);
25983     // is conn a conn config or a real conn?
25984     this.conn = conn;
25985     this.useAjax = !conn || !conn.events;
25986   
25987 };
25988
25989 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
25990     // thse are take from connection...
25991     
25992     /**
25993      * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
25994      */
25995     /**
25996      * @cfg {Object} extraParams  An object containing properties which are used as
25997      * extra parameters to each request made by this object. (defaults to undefined)
25998      */
25999     /**
26000      * @cfg {Object} defaultHeaders   An object containing request headers which are added
26001      *  to each request made by this object. (defaults to undefined)
26002      */
26003     /**
26004      * @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)
26005      */
26006     /**
26007      * @cfg {Number} timeout The timeout in milliseconds to be used for requests. (defaults to 30000)
26008      */
26009      /**
26010      * @cfg {Boolean} autoAbort Whether this request should abort any pending requests. (defaults to false)
26011      * @type Boolean
26012      */
26013   
26014
26015     /**
26016      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
26017      * @type Boolean
26018      */
26019     /**
26020      * Return the {@link Roo.data.Connection} object being used by this Proxy.
26021      * @return {Connection} The Connection object. This object may be used to subscribe to events on
26022      * a finer-grained basis than the DataProxy events.
26023      */
26024     getConnection : function(){
26025         return this.useAjax ? Roo.Ajax : this.conn;
26026     },
26027
26028     /**
26029      * Load data from the configured {@link Roo.data.Connection}, read the data object into
26030      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
26031      * process that block using the passed callback.
26032      * @param {Object} params An object containing properties which are to be used as HTTP parameters
26033      * for the request to the remote server.
26034      * @param {Roo.data.DataReader} reader The Reader object which converts the data
26035      * object into a block of Roo.data.Records.
26036      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
26037      * The function must be passed <ul>
26038      * <li>The Record block object</li>
26039      * <li>The "arg" argument from the load function</li>
26040      * <li>A boolean success indicator</li>
26041      * </ul>
26042      * @param {Object} scope The scope in which to call the callback
26043      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
26044      */
26045     load : function(params, reader, callback, scope, arg){
26046         if(this.fireEvent("beforeload", this, params) !== false){
26047             var  o = {
26048                 params : params || {},
26049                 request: {
26050                     callback : callback,
26051                     scope : scope,
26052                     arg : arg
26053                 },
26054                 reader: reader,
26055                 callback : this.loadResponse,
26056                 scope: this
26057             };
26058             if(this.useAjax){
26059                 Roo.applyIf(o, this.conn);
26060                 if(this.activeRequest){
26061                     Roo.Ajax.abort(this.activeRequest);
26062                 }
26063                 this.activeRequest = Roo.Ajax.request(o);
26064             }else{
26065                 this.conn.request(o);
26066             }
26067         }else{
26068             callback.call(scope||this, null, arg, false);
26069         }
26070     },
26071
26072     // private
26073     loadResponse : function(o, success, response){
26074         delete this.activeRequest;
26075         if(!success){
26076             this.fireEvent("loadexception", this, o, response);
26077             o.request.callback.call(o.request.scope, null, o.request.arg, false);
26078             return;
26079         }
26080         var result;
26081         try {
26082             result = o.reader.read(response);
26083         }catch(e){
26084             o.success = false;
26085             o.raw = { errorMsg : response.responseText };
26086             this.fireEvent("loadexception", this, o, response, e);
26087             o.request.callback.call(o.request.scope, o, o.request.arg, false);
26088             return;
26089         }
26090         
26091         this.fireEvent("load", this, o, o.request.arg);
26092         o.request.callback.call(o.request.scope, result, o.request.arg, true);
26093     },
26094
26095     // private
26096     update : function(dataSet){
26097
26098     },
26099
26100     // private
26101     updateResponse : function(dataSet){
26102
26103     }
26104 });/*
26105  * Based on:
26106  * Ext JS Library 1.1.1
26107  * Copyright(c) 2006-2007, Ext JS, LLC.
26108  *
26109  * Originally Released Under LGPL - original licence link has changed is not relivant.
26110  *
26111  * Fork - LGPL
26112  * <script type="text/javascript">
26113  */
26114
26115 /**
26116  * @class Roo.data.ScriptTagProxy
26117  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
26118  * other than the originating domain of the running page.<br><br>
26119  * <p>
26120  * <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
26121  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
26122  * <p>
26123  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
26124  * source code that is used as the source inside a &lt;script> tag.<br><br>
26125  * <p>
26126  * In order for the browser to process the returned data, the server must wrap the data object
26127  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
26128  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
26129  * depending on whether the callback name was passed:
26130  * <p>
26131  * <pre><code>
26132 boolean scriptTag = false;
26133 String cb = request.getParameter("callback");
26134 if (cb != null) {
26135     scriptTag = true;
26136     response.setContentType("text/javascript");
26137 } else {
26138     response.setContentType("application/x-json");
26139 }
26140 Writer out = response.getWriter();
26141 if (scriptTag) {
26142     out.write(cb + "(");
26143 }
26144 out.print(dataBlock.toJsonString());
26145 if (scriptTag) {
26146     out.write(");");
26147 }
26148 </pre></code>
26149  *
26150  * @constructor
26151  * @param {Object} config A configuration object.
26152  */
26153 Roo.data.ScriptTagProxy = function(config){
26154     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
26155     Roo.apply(this, config);
26156     this.head = document.getElementsByTagName("head")[0];
26157 };
26158
26159 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
26160
26161 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
26162     /**
26163      * @cfg {String} url The URL from which to request the data object.
26164      */
26165     /**
26166      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
26167      */
26168     timeout : 30000,
26169     /**
26170      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
26171      * the server the name of the callback function set up by the load call to process the returned data object.
26172      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
26173      * javascript output which calls this named function passing the data object as its only parameter.
26174      */
26175     callbackParam : "callback",
26176     /**
26177      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
26178      * name to the request.
26179      */
26180     nocache : true,
26181
26182     /**
26183      * Load data from the configured URL, read the data object into
26184      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
26185      * process that block using the passed callback.
26186      * @param {Object} params An object containing properties which are to be used as HTTP parameters
26187      * for the request to the remote server.
26188      * @param {Roo.data.DataReader} reader The Reader object which converts the data
26189      * object into a block of Roo.data.Records.
26190      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
26191      * The function must be passed <ul>
26192      * <li>The Record block object</li>
26193      * <li>The "arg" argument from the load function</li>
26194      * <li>A boolean success indicator</li>
26195      * </ul>
26196      * @param {Object} scope The scope in which to call the callback
26197      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
26198      */
26199     load : function(params, reader, callback, scope, arg){
26200         if(this.fireEvent("beforeload", this, params) !== false){
26201
26202             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
26203
26204             var url = this.url;
26205             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
26206             if(this.nocache){
26207                 url += "&_dc=" + (new Date().getTime());
26208             }
26209             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
26210             var trans = {
26211                 id : transId,
26212                 cb : "stcCallback"+transId,
26213                 scriptId : "stcScript"+transId,
26214                 params : params,
26215                 arg : arg,
26216                 url : url,
26217                 callback : callback,
26218                 scope : scope,
26219                 reader : reader
26220             };
26221             var conn = this;
26222
26223             window[trans.cb] = function(o){
26224                 conn.handleResponse(o, trans);
26225             };
26226
26227             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
26228
26229             if(this.autoAbort !== false){
26230                 this.abort();
26231             }
26232
26233             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
26234
26235             var script = document.createElement("script");
26236             script.setAttribute("src", url);
26237             script.setAttribute("type", "text/javascript");
26238             script.setAttribute("id", trans.scriptId);
26239             this.head.appendChild(script);
26240
26241             this.trans = trans;
26242         }else{
26243             callback.call(scope||this, null, arg, false);
26244         }
26245     },
26246
26247     // private
26248     isLoading : function(){
26249         return this.trans ? true : false;
26250     },
26251
26252     /**
26253      * Abort the current server request.
26254      */
26255     abort : function(){
26256         if(this.isLoading()){
26257             this.destroyTrans(this.trans);
26258         }
26259     },
26260
26261     // private
26262     destroyTrans : function(trans, isLoaded){
26263         this.head.removeChild(document.getElementById(trans.scriptId));
26264         clearTimeout(trans.timeoutId);
26265         if(isLoaded){
26266             window[trans.cb] = undefined;
26267             try{
26268                 delete window[trans.cb];
26269             }catch(e){}
26270         }else{
26271             // if hasn't been loaded, wait for load to remove it to prevent script error
26272             window[trans.cb] = function(){
26273                 window[trans.cb] = undefined;
26274                 try{
26275                     delete window[trans.cb];
26276                 }catch(e){}
26277             };
26278         }
26279     },
26280
26281     // private
26282     handleResponse : function(o, trans){
26283         this.trans = false;
26284         this.destroyTrans(trans, true);
26285         var result;
26286         try {
26287             result = trans.reader.readRecords(o);
26288         }catch(e){
26289             this.fireEvent("loadexception", this, o, trans.arg, e);
26290             trans.callback.call(trans.scope||window, null, trans.arg, false);
26291             return;
26292         }
26293         this.fireEvent("load", this, o, trans.arg);
26294         trans.callback.call(trans.scope||window, result, trans.arg, true);
26295     },
26296
26297     // private
26298     handleFailure : function(trans){
26299         this.trans = false;
26300         this.destroyTrans(trans, false);
26301         this.fireEvent("loadexception", this, null, trans.arg);
26302         trans.callback.call(trans.scope||window, null, trans.arg, false);
26303     }
26304 });/*
26305  * Based on:
26306  * Ext JS Library 1.1.1
26307  * Copyright(c) 2006-2007, Ext JS, LLC.
26308  *
26309  * Originally Released Under LGPL - original licence link has changed is not relivant.
26310  *
26311  * Fork - LGPL
26312  * <script type="text/javascript">
26313  */
26314
26315 /**
26316  * @class Roo.data.JsonReader
26317  * @extends Roo.data.DataReader
26318  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
26319  * based on mappings in a provided Roo.data.Record constructor.
26320  * 
26321  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
26322  * in the reply previously. 
26323  * 
26324  * <p>
26325  * Example code:
26326  * <pre><code>
26327 var RecordDef = Roo.data.Record.create([
26328     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
26329     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
26330 ]);
26331 var myReader = new Roo.data.JsonReader({
26332     totalProperty: "results",    // The property which contains the total dataset size (optional)
26333     root: "rows",                // The property which contains an Array of row objects
26334     id: "id"                     // The property within each row object that provides an ID for the record (optional)
26335 }, RecordDef);
26336 </code></pre>
26337  * <p>
26338  * This would consume a JSON file like this:
26339  * <pre><code>
26340 { 'results': 2, 'rows': [
26341     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
26342     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
26343 }
26344 </code></pre>
26345  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
26346  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
26347  * paged from the remote server.
26348  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
26349  * @cfg {String} root name of the property which contains the Array of row objects.
26350  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26351  * @cfg {Array} fields Array of field definition objects
26352  * @constructor
26353  * Create a new JsonReader
26354  * @param {Object} meta Metadata configuration options
26355  * @param {Object} recordType Either an Array of field definition objects,
26356  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
26357  */
26358 Roo.data.JsonReader = function(meta, recordType){
26359     
26360     meta = meta || {};
26361     // set some defaults:
26362     Roo.applyIf(meta, {
26363         totalProperty: 'total',
26364         successProperty : 'success',
26365         root : 'data',
26366         id : 'id'
26367     });
26368     
26369     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26370 };
26371 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
26372     
26373     readerType : 'Json',
26374     
26375     /**
26376      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
26377      * Used by Store query builder to append _requestMeta to params.
26378      * 
26379      */
26380     metaFromRemote : false,
26381     /**
26382      * This method is only used by a DataProxy which has retrieved data from a remote server.
26383      * @param {Object} response The XHR object which contains the JSON data in its responseText.
26384      * @return {Object} data A data block which is used by an Roo.data.Store object as
26385      * a cache of Roo.data.Records.
26386      */
26387     read : function(response){
26388         var json = response.responseText;
26389        
26390         var o = /* eval:var:o */ eval("("+json+")");
26391         if(!o) {
26392             throw {message: "JsonReader.read: Json object not found"};
26393         }
26394         
26395         if(o.metaData){
26396             
26397             delete this.ef;
26398             this.metaFromRemote = true;
26399             this.meta = o.metaData;
26400             this.recordType = Roo.data.Record.create(o.metaData.fields);
26401             this.onMetaChange(this.meta, this.recordType, o);
26402         }
26403         return this.readRecords(o);
26404     },
26405
26406     // private function a store will implement
26407     onMetaChange : function(meta, recordType, o){
26408
26409     },
26410
26411     /**
26412          * @ignore
26413          */
26414     simpleAccess: function(obj, subsc) {
26415         return obj[subsc];
26416     },
26417
26418         /**
26419          * @ignore
26420          */
26421     getJsonAccessor: function(){
26422         var re = /[\[\.]/;
26423         return function(expr) {
26424             try {
26425                 return(re.test(expr))
26426                     ? new Function("obj", "return obj." + expr)
26427                     : function(obj){
26428                         return obj[expr];
26429                     };
26430             } catch(e){}
26431             return Roo.emptyFn;
26432         };
26433     }(),
26434
26435     /**
26436      * Create a data block containing Roo.data.Records from an XML document.
26437      * @param {Object} o An object which contains an Array of row objects in the property specified
26438      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
26439      * which contains the total size of the dataset.
26440      * @return {Object} data A data block which is used by an Roo.data.Store object as
26441      * a cache of Roo.data.Records.
26442      */
26443     readRecords : function(o){
26444         /**
26445          * After any data loads, the raw JSON data is available for further custom processing.
26446          * @type Object
26447          */
26448         this.o = o;
26449         var s = this.meta, Record = this.recordType,
26450             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
26451
26452 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
26453         if (!this.ef) {
26454             if(s.totalProperty) {
26455                     this.getTotal = this.getJsonAccessor(s.totalProperty);
26456                 }
26457                 if(s.successProperty) {
26458                     this.getSuccess = this.getJsonAccessor(s.successProperty);
26459                 }
26460                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
26461                 if (s.id) {
26462                         var g = this.getJsonAccessor(s.id);
26463                         this.getId = function(rec) {
26464                                 var r = g(rec);  
26465                                 return (r === undefined || r === "") ? null : r;
26466                         };
26467                 } else {
26468                         this.getId = function(){return null;};
26469                 }
26470             this.ef = [];
26471             for(var jj = 0; jj < fl; jj++){
26472                 f = fi[jj];
26473                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
26474                 this.ef[jj] = this.getJsonAccessor(map);
26475             }
26476         }
26477
26478         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
26479         if(s.totalProperty){
26480             var vt = parseInt(this.getTotal(o), 10);
26481             if(!isNaN(vt)){
26482                 totalRecords = vt;
26483             }
26484         }
26485         if(s.successProperty){
26486             var vs = this.getSuccess(o);
26487             if(vs === false || vs === 'false'){
26488                 success = false;
26489             }
26490         }
26491         var records = [];
26492         for(var i = 0; i < c; i++){
26493             var n = root[i];
26494             var values = {};
26495             var id = this.getId(n);
26496             for(var j = 0; j < fl; j++){
26497                 f = fi[j];
26498                                 var v = this.ef[j](n);
26499                                 if (!f.convert) {
26500                                         Roo.log('missing convert for ' + f.name);
26501                                         Roo.log(f);
26502                                         continue;
26503                                 }
26504                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
26505             }
26506                         if (!Record) {
26507                                 return {
26508                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
26509                                         success : false,
26510                                         records : [],
26511                                         totalRecords : 0
26512                                 };
26513                         }
26514             var record = new Record(values, id);
26515             record.json = n;
26516             records[i] = record;
26517         }
26518         return {
26519             raw : o,
26520             success : success,
26521             records : records,
26522             totalRecords : totalRecords
26523         };
26524     },
26525     // used when loading children.. @see loadDataFromChildren
26526     toLoadData: function(rec)
26527     {
26528         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26529         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26530         return { data : data, total : data.length };
26531         
26532     }
26533 });/*
26534  * Based on:
26535  * Ext JS Library 1.1.1
26536  * Copyright(c) 2006-2007, Ext JS, LLC.
26537  *
26538  * Originally Released Under LGPL - original licence link has changed is not relivant.
26539  *
26540  * Fork - LGPL
26541  * <script type="text/javascript">
26542  */
26543
26544 /**
26545  * @class Roo.data.XmlReader
26546  * @extends Roo.data.DataReader
26547  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
26548  * based on mappings in a provided Roo.data.Record constructor.<br><br>
26549  * <p>
26550  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
26551  * header in the HTTP response must be set to "text/xml".</em>
26552  * <p>
26553  * Example code:
26554  * <pre><code>
26555 var RecordDef = Roo.data.Record.create([
26556    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
26557    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
26558 ]);
26559 var myReader = new Roo.data.XmlReader({
26560    totalRecords: "results", // The element which contains the total dataset size (optional)
26561    record: "row",           // The repeated element which contains row information
26562    id: "id"                 // The element within the row that provides an ID for the record (optional)
26563 }, RecordDef);
26564 </code></pre>
26565  * <p>
26566  * This would consume an XML file like this:
26567  * <pre><code>
26568 &lt;?xml?>
26569 &lt;dataset>
26570  &lt;results>2&lt;/results>
26571  &lt;row>
26572    &lt;id>1&lt;/id>
26573    &lt;name>Bill&lt;/name>
26574    &lt;occupation>Gardener&lt;/occupation>
26575  &lt;/row>
26576  &lt;row>
26577    &lt;id>2&lt;/id>
26578    &lt;name>Ben&lt;/name>
26579    &lt;occupation>Horticulturalist&lt;/occupation>
26580  &lt;/row>
26581 &lt;/dataset>
26582 </code></pre>
26583  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
26584  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
26585  * paged from the remote server.
26586  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
26587  * @cfg {String} success The DomQuery path to the success attribute used by forms.
26588  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
26589  * a record identifier value.
26590  * @constructor
26591  * Create a new XmlReader
26592  * @param {Object} meta Metadata configuration options
26593  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
26594  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
26595  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
26596  */
26597 Roo.data.XmlReader = function(meta, recordType){
26598     meta = meta || {};
26599     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26600 };
26601 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
26602     
26603     readerType : 'Xml',
26604     
26605     /**
26606      * This method is only used by a DataProxy which has retrieved data from a remote server.
26607          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
26608          * to contain a method called 'responseXML' that returns an XML document object.
26609      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26610      * a cache of Roo.data.Records.
26611      */
26612     read : function(response){
26613         var doc = response.responseXML;
26614         if(!doc) {
26615             throw {message: "XmlReader.read: XML Document not available"};
26616         }
26617         return this.readRecords(doc);
26618     },
26619
26620     /**
26621      * Create a data block containing Roo.data.Records from an XML document.
26622          * @param {Object} doc A parsed XML document.
26623      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26624      * a cache of Roo.data.Records.
26625      */
26626     readRecords : function(doc){
26627         /**
26628          * After any data loads/reads, the raw XML Document is available for further custom processing.
26629          * @type XMLDocument
26630          */
26631         this.xmlData = doc;
26632         var root = doc.documentElement || doc;
26633         var q = Roo.DomQuery;
26634         var recordType = this.recordType, fields = recordType.prototype.fields;
26635         var sid = this.meta.id;
26636         var totalRecords = 0, success = true;
26637         if(this.meta.totalRecords){
26638             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
26639         }
26640         
26641         if(this.meta.success){
26642             var sv = q.selectValue(this.meta.success, root, true);
26643             success = sv !== false && sv !== 'false';
26644         }
26645         var records = [];
26646         var ns = q.select(this.meta.record, root);
26647         for(var i = 0, len = ns.length; i < len; i++) {
26648                 var n = ns[i];
26649                 var values = {};
26650                 var id = sid ? q.selectValue(sid, n) : undefined;
26651                 for(var j = 0, jlen = fields.length; j < jlen; j++){
26652                     var f = fields.items[j];
26653                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
26654                     v = f.convert(v);
26655                     values[f.name] = v;
26656                 }
26657                 var record = new recordType(values, id);
26658                 record.node = n;
26659                 records[records.length] = record;
26660             }
26661
26662             return {
26663                 success : success,
26664                 records : records,
26665                 totalRecords : totalRecords || records.length
26666             };
26667     }
26668 });/*
26669  * Based on:
26670  * Ext JS Library 1.1.1
26671  * Copyright(c) 2006-2007, Ext JS, LLC.
26672  *
26673  * Originally Released Under LGPL - original licence link has changed is not relivant.
26674  *
26675  * Fork - LGPL
26676  * <script type="text/javascript">
26677  */
26678
26679 /**
26680  * @class Roo.data.ArrayReader
26681  * @extends Roo.data.DataReader
26682  * Data reader class to create an Array of Roo.data.Record objects from an Array.
26683  * Each element of that Array represents a row of data fields. The
26684  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
26685  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
26686  * <p>
26687  * Example code:.
26688  * <pre><code>
26689 var RecordDef = Roo.data.Record.create([
26690     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
26691     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
26692 ]);
26693 var myReader = new Roo.data.ArrayReader({
26694     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
26695 }, RecordDef);
26696 </code></pre>
26697  * <p>
26698  * This would consume an Array like this:
26699  * <pre><code>
26700 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
26701   </code></pre>
26702  
26703  * @constructor
26704  * Create a new JsonReader
26705  * @param {Object} meta Metadata configuration options.
26706  * @param {Object|Array} recordType Either an Array of field definition objects
26707  * 
26708  * @cfg {Array} fields Array of field definition objects
26709  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26710  * as specified to {@link Roo.data.Record#create},
26711  * or an {@link Roo.data.Record} object
26712  *
26713  * 
26714  * created using {@link Roo.data.Record#create}.
26715  */
26716 Roo.data.ArrayReader = function(meta, recordType)
26717 {    
26718     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26719 };
26720
26721 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
26722     
26723       /**
26724      * Create a data block containing Roo.data.Records from an XML document.
26725      * @param {Object} o An Array of row objects which represents the dataset.
26726      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
26727      * a cache of Roo.data.Records.
26728      */
26729     readRecords : function(o)
26730     {
26731         var sid = this.meta ? this.meta.id : null;
26732         var recordType = this.recordType, fields = recordType.prototype.fields;
26733         var records = [];
26734         var root = o;
26735         for(var i = 0; i < root.length; i++){
26736             var n = root[i];
26737             var values = {};
26738             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
26739             for(var j = 0, jlen = fields.length; j < jlen; j++){
26740                 var f = fields.items[j];
26741                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
26742                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
26743                 v = f.convert(v);
26744                 values[f.name] = v;
26745             }
26746             var record = new recordType(values, id);
26747             record.json = n;
26748             records[records.length] = record;
26749         }
26750         return {
26751             records : records,
26752             totalRecords : records.length
26753         };
26754     },
26755     // used when loading children.. @see loadDataFromChildren
26756     toLoadData: function(rec)
26757     {
26758         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26759         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26760         
26761     }
26762     
26763     
26764 });/*
26765  * Based on:
26766  * Ext JS Library 1.1.1
26767  * Copyright(c) 2006-2007, Ext JS, LLC.
26768  *
26769  * Originally Released Under LGPL - original licence link has changed is not relivant.
26770  *
26771  * Fork - LGPL
26772  * <script type="text/javascript">
26773  */
26774
26775
26776 /**
26777  * @class Roo.data.Tree
26778  * @extends Roo.util.Observable
26779  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
26780  * in the tree have most standard DOM functionality.
26781  * @constructor
26782  * @param {Node} root (optional) The root node
26783  */
26784 Roo.data.Tree = function(root){
26785    this.nodeHash = {};
26786    /**
26787     * The root node for this tree
26788     * @type Node
26789     */
26790    this.root = null;
26791    if(root){
26792        this.setRootNode(root);
26793    }
26794    this.addEvents({
26795        /**
26796         * @event append
26797         * Fires when a new child node is appended to a node in this tree.
26798         * @param {Tree} tree The owner tree
26799         * @param {Node} parent The parent node
26800         * @param {Node} node The newly appended node
26801         * @param {Number} index The index of the newly appended node
26802         */
26803        "append" : true,
26804        /**
26805         * @event remove
26806         * Fires when a child node is removed from a node in this tree.
26807         * @param {Tree} tree The owner tree
26808         * @param {Node} parent The parent node
26809         * @param {Node} node The child node removed
26810         */
26811        "remove" : true,
26812        /**
26813         * @event move
26814         * Fires when a node is moved to a new location in the tree
26815         * @param {Tree} tree The owner tree
26816         * @param {Node} node The node moved
26817         * @param {Node} oldParent The old parent of this node
26818         * @param {Node} newParent The new parent of this node
26819         * @param {Number} index The index it was moved to
26820         */
26821        "move" : true,
26822        /**
26823         * @event insert
26824         * Fires when a new child node is inserted in a node in this tree.
26825         * @param {Tree} tree The owner tree
26826         * @param {Node} parent The parent node
26827         * @param {Node} node The child node inserted
26828         * @param {Node} refNode The child node the node was inserted before
26829         */
26830        "insert" : true,
26831        /**
26832         * @event beforeappend
26833         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
26834         * @param {Tree} tree The owner tree
26835         * @param {Node} parent The parent node
26836         * @param {Node} node The child node to be appended
26837         */
26838        "beforeappend" : true,
26839        /**
26840         * @event beforeremove
26841         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
26842         * @param {Tree} tree The owner tree
26843         * @param {Node} parent The parent node
26844         * @param {Node} node The child node to be removed
26845         */
26846        "beforeremove" : true,
26847        /**
26848         * @event beforemove
26849         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
26850         * @param {Tree} tree The owner tree
26851         * @param {Node} node The node being moved
26852         * @param {Node} oldParent The parent of the node
26853         * @param {Node} newParent The new parent the node is moving to
26854         * @param {Number} index The index it is being moved to
26855         */
26856        "beforemove" : true,
26857        /**
26858         * @event beforeinsert
26859         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
26860         * @param {Tree} tree The owner tree
26861         * @param {Node} parent The parent node
26862         * @param {Node} node The child node to be inserted
26863         * @param {Node} refNode The child node the node is being inserted before
26864         */
26865        "beforeinsert" : true
26866    });
26867
26868     Roo.data.Tree.superclass.constructor.call(this);
26869 };
26870
26871 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
26872     pathSeparator: "/",
26873
26874     proxyNodeEvent : function(){
26875         return this.fireEvent.apply(this, arguments);
26876     },
26877
26878     /**
26879      * Returns the root node for this tree.
26880      * @return {Node}
26881      */
26882     getRootNode : function(){
26883         return this.root;
26884     },
26885
26886     /**
26887      * Sets the root node for this tree.
26888      * @param {Node} node
26889      * @return {Node}
26890      */
26891     setRootNode : function(node){
26892         this.root = node;
26893         node.ownerTree = this;
26894         node.isRoot = true;
26895         this.registerNode(node);
26896         return node;
26897     },
26898
26899     /**
26900      * Gets a node in this tree by its id.
26901      * @param {String} id
26902      * @return {Node}
26903      */
26904     getNodeById : function(id){
26905         return this.nodeHash[id];
26906     },
26907
26908     registerNode : function(node){
26909         this.nodeHash[node.id] = node;
26910     },
26911
26912     unregisterNode : function(node){
26913         delete this.nodeHash[node.id];
26914     },
26915
26916     toString : function(){
26917         return "[Tree"+(this.id?" "+this.id:"")+"]";
26918     }
26919 });
26920
26921 /**
26922  * @class Roo.data.Node
26923  * @extends Roo.util.Observable
26924  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
26925  * @cfg {String} id The id for this node. If one is not specified, one is generated.
26926  * @constructor
26927  * @param {Object} attributes The attributes/config for the node
26928  */
26929 Roo.data.Node = function(attributes){
26930     /**
26931      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
26932      * @type {Object}
26933      */
26934     this.attributes = attributes || {};
26935     this.leaf = this.attributes.leaf;
26936     /**
26937      * The node id. @type String
26938      */
26939     this.id = this.attributes.id;
26940     if(!this.id){
26941         this.id = Roo.id(null, "ynode-");
26942         this.attributes.id = this.id;
26943     }
26944      
26945     
26946     /**
26947      * All child nodes of this node. @type Array
26948      */
26949     this.childNodes = [];
26950     if(!this.childNodes.indexOf){ // indexOf is a must
26951         this.childNodes.indexOf = function(o){
26952             for(var i = 0, len = this.length; i < len; i++){
26953                 if(this[i] == o) {
26954                     return i;
26955                 }
26956             }
26957             return -1;
26958         };
26959     }
26960     /**
26961      * The parent node for this node. @type Node
26962      */
26963     this.parentNode = null;
26964     /**
26965      * The first direct child node of this node, or null if this node has no child nodes. @type Node
26966      */
26967     this.firstChild = null;
26968     /**
26969      * The last direct child node of this node, or null if this node has no child nodes. @type Node
26970      */
26971     this.lastChild = null;
26972     /**
26973      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
26974      */
26975     this.previousSibling = null;
26976     /**
26977      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
26978      */
26979     this.nextSibling = null;
26980
26981     this.addEvents({
26982        /**
26983         * @event append
26984         * Fires when a new child node is appended
26985         * @param {Tree} tree The owner tree
26986         * @param {Node} this This node
26987         * @param {Node} node The newly appended node
26988         * @param {Number} index The index of the newly appended node
26989         */
26990        "append" : true,
26991        /**
26992         * @event remove
26993         * Fires when a child node is removed
26994         * @param {Tree} tree The owner tree
26995         * @param {Node} this This node
26996         * @param {Node} node The removed node
26997         */
26998        "remove" : true,
26999        /**
27000         * @event move
27001         * Fires when this node is moved to a new location in the tree
27002         * @param {Tree} tree The owner tree
27003         * @param {Node} this This node
27004         * @param {Node} oldParent The old parent of this node
27005         * @param {Node} newParent The new parent of this node
27006         * @param {Number} index The index it was moved to
27007         */
27008        "move" : true,
27009        /**
27010         * @event insert
27011         * Fires when a new child node is inserted.
27012         * @param {Tree} tree The owner tree
27013         * @param {Node} this This node
27014         * @param {Node} node The child node inserted
27015         * @param {Node} refNode The child node the node was inserted before
27016         */
27017        "insert" : true,
27018        /**
27019         * @event beforeappend
27020         * Fires before a new child is appended, return false to cancel the append.
27021         * @param {Tree} tree The owner tree
27022         * @param {Node} this This node
27023         * @param {Node} node The child node to be appended
27024         */
27025        "beforeappend" : true,
27026        /**
27027         * @event beforeremove
27028         * Fires before a child is removed, return false to cancel the remove.
27029         * @param {Tree} tree The owner tree
27030         * @param {Node} this This node
27031         * @param {Node} node The child node to be removed
27032         */
27033        "beforeremove" : true,
27034        /**
27035         * @event beforemove
27036         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
27037         * @param {Tree} tree The owner tree
27038         * @param {Node} this This node
27039         * @param {Node} oldParent The parent of this node
27040         * @param {Node} newParent The new parent this node is moving to
27041         * @param {Number} index The index it is being moved to
27042         */
27043        "beforemove" : true,
27044        /**
27045         * @event beforeinsert
27046         * Fires before a new child is inserted, return false to cancel the insert.
27047         * @param {Tree} tree The owner tree
27048         * @param {Node} this This node
27049         * @param {Node} node The child node to be inserted
27050         * @param {Node} refNode The child node the node is being inserted before
27051         */
27052        "beforeinsert" : true
27053    });
27054     this.listeners = this.attributes.listeners;
27055     Roo.data.Node.superclass.constructor.call(this);
27056 };
27057
27058 Roo.extend(Roo.data.Node, Roo.util.Observable, {
27059     fireEvent : function(evtName){
27060         // first do standard event for this node
27061         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
27062             return false;
27063         }
27064         // then bubble it up to the tree if the event wasn't cancelled
27065         var ot = this.getOwnerTree();
27066         if(ot){
27067             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
27068                 return false;
27069             }
27070         }
27071         return true;
27072     },
27073
27074     /**
27075      * Returns true if this node is a leaf
27076      * @return {Boolean}
27077      */
27078     isLeaf : function(){
27079         return this.leaf === true;
27080     },
27081
27082     // private
27083     setFirstChild : function(node){
27084         this.firstChild = node;
27085     },
27086
27087     //private
27088     setLastChild : function(node){
27089         this.lastChild = node;
27090     },
27091
27092
27093     /**
27094      * Returns true if this node is the last child of its parent
27095      * @return {Boolean}
27096      */
27097     isLast : function(){
27098        return (!this.parentNode ? true : this.parentNode.lastChild == this);
27099     },
27100
27101     /**
27102      * Returns true if this node is the first child of its parent
27103      * @return {Boolean}
27104      */
27105     isFirst : function(){
27106        return (!this.parentNode ? true : this.parentNode.firstChild == this);
27107     },
27108
27109     hasChildNodes : function(){
27110         return !this.isLeaf() && this.childNodes.length > 0;
27111     },
27112
27113     /**
27114      * Insert node(s) as the last child node of this node.
27115      * @param {Node/Array} node The node or Array of nodes to append
27116      * @return {Node} The appended node if single append, or null if an array was passed
27117      */
27118     appendChild : function(node){
27119         var multi = false;
27120         if(node instanceof Array){
27121             multi = node;
27122         }else if(arguments.length > 1){
27123             multi = arguments;
27124         }
27125         
27126         // if passed an array or multiple args do them one by one
27127         if(multi){
27128             for(var i = 0, len = multi.length; i < len; i++) {
27129                 this.appendChild(multi[i]);
27130             }
27131         }else{
27132             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
27133                 return false;
27134             }
27135             var index = this.childNodes.length;
27136             var oldParent = node.parentNode;
27137             // it's a move, make sure we move it cleanly
27138             if(oldParent){
27139                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
27140                     return false;
27141                 }
27142                 oldParent.removeChild(node);
27143             }
27144             
27145             index = this.childNodes.length;
27146             if(index == 0){
27147                 this.setFirstChild(node);
27148             }
27149             this.childNodes.push(node);
27150             node.parentNode = this;
27151             var ps = this.childNodes[index-1];
27152             if(ps){
27153                 node.previousSibling = ps;
27154                 ps.nextSibling = node;
27155             }else{
27156                 node.previousSibling = null;
27157             }
27158             node.nextSibling = null;
27159             this.setLastChild(node);
27160             node.setOwnerTree(this.getOwnerTree());
27161             this.fireEvent("append", this.ownerTree, this, node, index);
27162             if(this.ownerTree) {
27163                 this.ownerTree.fireEvent("appendnode", this, node, index);
27164             }
27165             if(oldParent){
27166                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
27167             }
27168             return node;
27169         }
27170     },
27171
27172     /**
27173      * Removes a child node from this node.
27174      * @param {Node} node The node to remove
27175      * @return {Node} The removed node
27176      */
27177     removeChild : function(node){
27178         var index = this.childNodes.indexOf(node);
27179         if(index == -1){
27180             return false;
27181         }
27182         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
27183             return false;
27184         }
27185
27186         // remove it from childNodes collection
27187         this.childNodes.splice(index, 1);
27188
27189         // update siblings
27190         if(node.previousSibling){
27191             node.previousSibling.nextSibling = node.nextSibling;
27192         }
27193         if(node.nextSibling){
27194             node.nextSibling.previousSibling = node.previousSibling;
27195         }
27196
27197         // update child refs
27198         if(this.firstChild == node){
27199             this.setFirstChild(node.nextSibling);
27200         }
27201         if(this.lastChild == node){
27202             this.setLastChild(node.previousSibling);
27203         }
27204
27205         node.setOwnerTree(null);
27206         // clear any references from the node
27207         node.parentNode = null;
27208         node.previousSibling = null;
27209         node.nextSibling = null;
27210         this.fireEvent("remove", this.ownerTree, this, node);
27211         return node;
27212     },
27213
27214     /**
27215      * Inserts the first node before the second node in this nodes childNodes collection.
27216      * @param {Node} node The node to insert
27217      * @param {Node} refNode The node to insert before (if null the node is appended)
27218      * @return {Node} The inserted node
27219      */
27220     insertBefore : function(node, refNode){
27221         if(!refNode){ // like standard Dom, refNode can be null for append
27222             return this.appendChild(node);
27223         }
27224         // nothing to do
27225         if(node == refNode){
27226             return false;
27227         }
27228
27229         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
27230             return false;
27231         }
27232         var index = this.childNodes.indexOf(refNode);
27233         var oldParent = node.parentNode;
27234         var refIndex = index;
27235
27236         // when moving internally, indexes will change after remove
27237         if(oldParent == this && this.childNodes.indexOf(node) < index){
27238             refIndex--;
27239         }
27240
27241         // it's a move, make sure we move it cleanly
27242         if(oldParent){
27243             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
27244                 return false;
27245             }
27246             oldParent.removeChild(node);
27247         }
27248         if(refIndex == 0){
27249             this.setFirstChild(node);
27250         }
27251         this.childNodes.splice(refIndex, 0, node);
27252         node.parentNode = this;
27253         var ps = this.childNodes[refIndex-1];
27254         if(ps){
27255             node.previousSibling = ps;
27256             ps.nextSibling = node;
27257         }else{
27258             node.previousSibling = null;
27259         }
27260         node.nextSibling = refNode;
27261         refNode.previousSibling = node;
27262         node.setOwnerTree(this.getOwnerTree());
27263         this.fireEvent("insert", this.ownerTree, this, node, refNode);
27264         if(oldParent){
27265             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
27266         }
27267         return node;
27268     },
27269
27270     /**
27271      * Returns the child node at the specified index.
27272      * @param {Number} index
27273      * @return {Node}
27274      */
27275     item : function(index){
27276         return this.childNodes[index];
27277     },
27278
27279     /**
27280      * Replaces one child node in this node with another.
27281      * @param {Node} newChild The replacement node
27282      * @param {Node} oldChild The node to replace
27283      * @return {Node} The replaced node
27284      */
27285     replaceChild : function(newChild, oldChild){
27286         this.insertBefore(newChild, oldChild);
27287         this.removeChild(oldChild);
27288         return oldChild;
27289     },
27290
27291     /**
27292      * Returns the index of a child node
27293      * @param {Node} node
27294      * @return {Number} The index of the node or -1 if it was not found
27295      */
27296     indexOf : function(child){
27297         return this.childNodes.indexOf(child);
27298     },
27299
27300     /**
27301      * Returns the tree this node is in.
27302      * @return {Tree}
27303      */
27304     getOwnerTree : function(){
27305         // if it doesn't have one, look for one
27306         if(!this.ownerTree){
27307             var p = this;
27308             while(p){
27309                 if(p.ownerTree){
27310                     this.ownerTree = p.ownerTree;
27311                     break;
27312                 }
27313                 p = p.parentNode;
27314             }
27315         }
27316         return this.ownerTree;
27317     },
27318
27319     /**
27320      * Returns depth of this node (the root node has a depth of 0)
27321      * @return {Number}
27322      */
27323     getDepth : function(){
27324         var depth = 0;
27325         var p = this;
27326         while(p.parentNode){
27327             ++depth;
27328             p = p.parentNode;
27329         }
27330         return depth;
27331     },
27332
27333     // private
27334     setOwnerTree : function(tree){
27335         // if it's move, we need to update everyone
27336         if(tree != this.ownerTree){
27337             if(this.ownerTree){
27338                 this.ownerTree.unregisterNode(this);
27339             }
27340             this.ownerTree = tree;
27341             var cs = this.childNodes;
27342             for(var i = 0, len = cs.length; i < len; i++) {
27343                 cs[i].setOwnerTree(tree);
27344             }
27345             if(tree){
27346                 tree.registerNode(this);
27347             }
27348         }
27349     },
27350
27351     /**
27352      * Returns the path for this node. The path can be used to expand or select this node programmatically.
27353      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
27354      * @return {String} The path
27355      */
27356     getPath : function(attr){
27357         attr = attr || "id";
27358         var p = this.parentNode;
27359         var b = [this.attributes[attr]];
27360         while(p){
27361             b.unshift(p.attributes[attr]);
27362             p = p.parentNode;
27363         }
27364         var sep = this.getOwnerTree().pathSeparator;
27365         return sep + b.join(sep);
27366     },
27367
27368     /**
27369      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
27370      * function call will be the scope provided or the current node. The arguments to the function
27371      * will be the args provided or the current node. If the function returns false at any point,
27372      * the bubble is stopped.
27373      * @param {Function} fn The function to call
27374      * @param {Object} scope (optional) The scope of the function (defaults to current node)
27375      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27376      */
27377     bubble : function(fn, scope, args){
27378         var p = this;
27379         while(p){
27380             if(fn.call(scope || p, args || p) === false){
27381                 break;
27382             }
27383             p = p.parentNode;
27384         }
27385     },
27386
27387     /**
27388      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
27389      * function call will be the scope provided or the current node. The arguments to the function
27390      * will be the args provided or the current node. If the function returns false at any point,
27391      * the cascade is stopped on that branch.
27392      * @param {Function} fn The function to call
27393      * @param {Object} scope (optional) The scope of the function (defaults to current node)
27394      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27395      */
27396     cascade : function(fn, scope, args){
27397         if(fn.call(scope || this, args || this) !== false){
27398             var cs = this.childNodes;
27399             for(var i = 0, len = cs.length; i < len; i++) {
27400                 cs[i].cascade(fn, scope, args);
27401             }
27402         }
27403     },
27404
27405     /**
27406      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
27407      * function call will be the scope provided or the current node. The arguments to the function
27408      * will be the args provided or the current node. If the function returns false at any point,
27409      * the iteration stops.
27410      * @param {Function} fn The function to call
27411      * @param {Object} scope (optional) The scope of the function (defaults to current node)
27412      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27413      */
27414     eachChild : function(fn, scope, args){
27415         var cs = this.childNodes;
27416         for(var i = 0, len = cs.length; i < len; i++) {
27417                 if(fn.call(scope || this, args || cs[i]) === false){
27418                     break;
27419                 }
27420         }
27421     },
27422
27423     /**
27424      * Finds the first child that has the attribute with the specified value.
27425      * @param {String} attribute The attribute name
27426      * @param {Mixed} value The value to search for
27427      * @return {Node} The found child or null if none was found
27428      */
27429     findChild : function(attribute, value){
27430         var cs = this.childNodes;
27431         for(var i = 0, len = cs.length; i < len; i++) {
27432                 if(cs[i].attributes[attribute] == value){
27433                     return cs[i];
27434                 }
27435         }
27436         return null;
27437     },
27438
27439     /**
27440      * Finds the first child by a custom function. The child matches if the function passed
27441      * returns true.
27442      * @param {Function} fn
27443      * @param {Object} scope (optional)
27444      * @return {Node} The found child or null if none was found
27445      */
27446     findChildBy : function(fn, scope){
27447         var cs = this.childNodes;
27448         for(var i = 0, len = cs.length; i < len; i++) {
27449                 if(fn.call(scope||cs[i], cs[i]) === true){
27450                     return cs[i];
27451                 }
27452         }
27453         return null;
27454     },
27455
27456     /**
27457      * Sorts this nodes children using the supplied sort function
27458      * @param {Function} fn
27459      * @param {Object} scope (optional)
27460      */
27461     sort : function(fn, scope){
27462         var cs = this.childNodes;
27463         var len = cs.length;
27464         if(len > 0){
27465             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
27466             cs.sort(sortFn);
27467             for(var i = 0; i < len; i++){
27468                 var n = cs[i];
27469                 n.previousSibling = cs[i-1];
27470                 n.nextSibling = cs[i+1];
27471                 if(i == 0){
27472                     this.setFirstChild(n);
27473                 }
27474                 if(i == len-1){
27475                     this.setLastChild(n);
27476                 }
27477             }
27478         }
27479     },
27480
27481     /**
27482      * Returns true if this node is an ancestor (at any point) of the passed node.
27483      * @param {Node} node
27484      * @return {Boolean}
27485      */
27486     contains : function(node){
27487         return node.isAncestor(this);
27488     },
27489
27490     /**
27491      * Returns true if the passed node is an ancestor (at any point) of this node.
27492      * @param {Node} node
27493      * @return {Boolean}
27494      */
27495     isAncestor : function(node){
27496         var p = this.parentNode;
27497         while(p){
27498             if(p == node){
27499                 return true;
27500             }
27501             p = p.parentNode;
27502         }
27503         return false;
27504     },
27505
27506     toString : function(){
27507         return "[Node"+(this.id?" "+this.id:"")+"]";
27508     }
27509 });/*
27510  * Based on:
27511  * Ext JS Library 1.1.1
27512  * Copyright(c) 2006-2007, Ext JS, LLC.
27513  *
27514  * Originally Released Under LGPL - original licence link has changed is not relivant.
27515  *
27516  * Fork - LGPL
27517  * <script type="text/javascript">
27518  */
27519
27520
27521 /**
27522  * @class Roo.Shadow
27523  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
27524  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
27525  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
27526  * @constructor
27527  * Create a new Shadow
27528  * @param {Object} config The config object
27529  */
27530 Roo.Shadow = function(config){
27531     Roo.apply(this, config);
27532     if(typeof this.mode != "string"){
27533         this.mode = this.defaultMode;
27534     }
27535     var o = this.offset, a = {h: 0};
27536     var rad = Math.floor(this.offset/2);
27537     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
27538         case "drop":
27539             a.w = 0;
27540             a.l = a.t = o;
27541             a.t -= 1;
27542             if(Roo.isIE){
27543                 a.l -= this.offset + rad;
27544                 a.t -= this.offset + rad;
27545                 a.w -= rad;
27546                 a.h -= rad;
27547                 a.t += 1;
27548             }
27549         break;
27550         case "sides":
27551             a.w = (o*2);
27552             a.l = -o;
27553             a.t = o-1;
27554             if(Roo.isIE){
27555                 a.l -= (this.offset - rad);
27556                 a.t -= this.offset + rad;
27557                 a.l += 1;
27558                 a.w -= (this.offset - rad)*2;
27559                 a.w -= rad + 1;
27560                 a.h -= 1;
27561             }
27562         break;
27563         case "frame":
27564             a.w = a.h = (o*2);
27565             a.l = a.t = -o;
27566             a.t += 1;
27567             a.h -= 2;
27568             if(Roo.isIE){
27569                 a.l -= (this.offset - rad);
27570                 a.t -= (this.offset - rad);
27571                 a.l += 1;
27572                 a.w -= (this.offset + rad + 1);
27573                 a.h -= (this.offset + rad);
27574                 a.h += 1;
27575             }
27576         break;
27577     };
27578
27579     this.adjusts = a;
27580 };
27581
27582 Roo.Shadow.prototype = {
27583     /**
27584      * @cfg {String} mode
27585      * The shadow display mode.  Supports the following options:<br />
27586      * sides: Shadow displays on both sides and bottom only<br />
27587      * frame: Shadow displays equally on all four sides<br />
27588      * drop: Traditional bottom-right drop shadow (default)
27589      */
27590     mode: false,
27591     /**
27592      * @cfg {String} offset
27593      * The number of pixels to offset the shadow from the element (defaults to 4)
27594      */
27595     offset: 4,
27596
27597     // private
27598     defaultMode: "drop",
27599
27600     /**
27601      * Displays the shadow under the target element
27602      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
27603      */
27604     show : function(target){
27605         target = Roo.get(target);
27606         if(!this.el){
27607             this.el = Roo.Shadow.Pool.pull();
27608             if(this.el.dom.nextSibling != target.dom){
27609                 this.el.insertBefore(target);
27610             }
27611         }
27612         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
27613         if(Roo.isIE){
27614             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
27615         }
27616         this.realign(
27617             target.getLeft(true),
27618             target.getTop(true),
27619             target.getWidth(),
27620             target.getHeight()
27621         );
27622         this.el.dom.style.display = "block";
27623     },
27624
27625     /**
27626      * Returns true if the shadow is visible, else false
27627      */
27628     isVisible : function(){
27629         return this.el ? true : false;  
27630     },
27631
27632     /**
27633      * Direct alignment when values are already available. Show must be called at least once before
27634      * calling this method to ensure it is initialized.
27635      * @param {Number} left The target element left position
27636      * @param {Number} top The target element top position
27637      * @param {Number} width The target element width
27638      * @param {Number} height The target element height
27639      */
27640     realign : function(l, t, w, h){
27641         if(!this.el){
27642             return;
27643         }
27644         var a = this.adjusts, d = this.el.dom, s = d.style;
27645         var iea = 0;
27646         s.left = (l+a.l)+"px";
27647         s.top = (t+a.t)+"px";
27648         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
27649  
27650         if(s.width != sws || s.height != shs){
27651             s.width = sws;
27652             s.height = shs;
27653             if(!Roo.isIE){
27654                 var cn = d.childNodes;
27655                 var sww = Math.max(0, (sw-12))+"px";
27656                 cn[0].childNodes[1].style.width = sww;
27657                 cn[1].childNodes[1].style.width = sww;
27658                 cn[2].childNodes[1].style.width = sww;
27659                 cn[1].style.height = Math.max(0, (sh-12))+"px";
27660             }
27661         }
27662     },
27663
27664     /**
27665      * Hides this shadow
27666      */
27667     hide : function(){
27668         if(this.el){
27669             this.el.dom.style.display = "none";
27670             Roo.Shadow.Pool.push(this.el);
27671             delete this.el;
27672         }
27673     },
27674
27675     /**
27676      * Adjust the z-index of this shadow
27677      * @param {Number} zindex The new z-index
27678      */
27679     setZIndex : function(z){
27680         this.zIndex = z;
27681         if(this.el){
27682             this.el.setStyle("z-index", z);
27683         }
27684     }
27685 };
27686
27687 // Private utility class that manages the internal Shadow cache
27688 Roo.Shadow.Pool = function(){
27689     var p = [];
27690     var markup = Roo.isIE ?
27691                  '<div class="x-ie-shadow"></div>' :
27692                  '<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>';
27693     return {
27694         pull : function(){
27695             var sh = p.shift();
27696             if(!sh){
27697                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
27698                 sh.autoBoxAdjust = false;
27699             }
27700             return sh;
27701         },
27702
27703         push : function(sh){
27704             p.push(sh);
27705         }
27706     };
27707 }();/*
27708  * Based on:
27709  * Ext JS Library 1.1.1
27710  * Copyright(c) 2006-2007, Ext JS, LLC.
27711  *
27712  * Originally Released Under LGPL - original licence link has changed is not relivant.
27713  *
27714  * Fork - LGPL
27715  * <script type="text/javascript">
27716  */
27717
27718
27719 /**
27720  * @class Roo.SplitBar
27721  * @extends Roo.util.Observable
27722  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
27723  * <br><br>
27724  * Usage:
27725  * <pre><code>
27726 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
27727                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
27728 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
27729 split.minSize = 100;
27730 split.maxSize = 600;
27731 split.animate = true;
27732 split.on('moved', splitterMoved);
27733 </code></pre>
27734  * @constructor
27735  * Create a new SplitBar
27736  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
27737  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
27738  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27739  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
27740                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
27741                         position of the SplitBar).
27742  */
27743 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
27744     
27745     /** @private */
27746     this.el = Roo.get(dragElement, true);
27747     this.el.dom.unselectable = "on";
27748     /** @private */
27749     this.resizingEl = Roo.get(resizingElement, true);
27750
27751     /**
27752      * @private
27753      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27754      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
27755      * @type Number
27756      */
27757     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
27758     
27759     /**
27760      * The minimum size of the resizing element. (Defaults to 0)
27761      * @type Number
27762      */
27763     this.minSize = 0;
27764     
27765     /**
27766      * The maximum size of the resizing element. (Defaults to 2000)
27767      * @type Number
27768      */
27769     this.maxSize = 2000;
27770     
27771     /**
27772      * Whether to animate the transition to the new size
27773      * @type Boolean
27774      */
27775     this.animate = false;
27776     
27777     /**
27778      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
27779      * @type Boolean
27780      */
27781     this.useShim = false;
27782     
27783     /** @private */
27784     this.shim = null;
27785     
27786     if(!existingProxy){
27787         /** @private */
27788         this.proxy = Roo.SplitBar.createProxy(this.orientation);
27789     }else{
27790         this.proxy = Roo.get(existingProxy).dom;
27791     }
27792     /** @private */
27793     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
27794     
27795     /** @private */
27796     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
27797     
27798     /** @private */
27799     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
27800     
27801     /** @private */
27802     this.dragSpecs = {};
27803     
27804     /**
27805      * @private The adapter to use to positon and resize elements
27806      */
27807     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
27808     this.adapter.init(this);
27809     
27810     if(this.orientation == Roo.SplitBar.HORIZONTAL){
27811         /** @private */
27812         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
27813         this.el.addClass("x-splitbar-h");
27814     }else{
27815         /** @private */
27816         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
27817         this.el.addClass("x-splitbar-v");
27818     }
27819     
27820     this.addEvents({
27821         /**
27822          * @event resize
27823          * Fires when the splitter is moved (alias for {@link #event-moved})
27824          * @param {Roo.SplitBar} this
27825          * @param {Number} newSize the new width or height
27826          */
27827         "resize" : true,
27828         /**
27829          * @event moved
27830          * Fires when the splitter is moved
27831          * @param {Roo.SplitBar} this
27832          * @param {Number} newSize the new width or height
27833          */
27834         "moved" : true,
27835         /**
27836          * @event beforeresize
27837          * Fires before the splitter is dragged
27838          * @param {Roo.SplitBar} this
27839          */
27840         "beforeresize" : true,
27841
27842         "beforeapply" : true
27843     });
27844
27845     Roo.util.Observable.call(this);
27846 };
27847
27848 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
27849     onStartProxyDrag : function(x, y){
27850         this.fireEvent("beforeresize", this);
27851         if(!this.overlay){
27852             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
27853             o.unselectable();
27854             o.enableDisplayMode("block");
27855             // all splitbars share the same overlay
27856             Roo.SplitBar.prototype.overlay = o;
27857         }
27858         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27859         this.overlay.show();
27860         Roo.get(this.proxy).setDisplayed("block");
27861         var size = this.adapter.getElementSize(this);
27862         this.activeMinSize = this.getMinimumSize();;
27863         this.activeMaxSize = this.getMaximumSize();;
27864         var c1 = size - this.activeMinSize;
27865         var c2 = Math.max(this.activeMaxSize - size, 0);
27866         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27867             this.dd.resetConstraints();
27868             this.dd.setXConstraint(
27869                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
27870                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
27871             );
27872             this.dd.setYConstraint(0, 0);
27873         }else{
27874             this.dd.resetConstraints();
27875             this.dd.setXConstraint(0, 0);
27876             this.dd.setYConstraint(
27877                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
27878                 this.placement == Roo.SplitBar.TOP ? c2 : c1
27879             );
27880          }
27881         this.dragSpecs.startSize = size;
27882         this.dragSpecs.startPoint = [x, y];
27883         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
27884     },
27885     
27886     /** 
27887      * @private Called after the drag operation by the DDProxy
27888      */
27889     onEndProxyDrag : function(e){
27890         Roo.get(this.proxy).setDisplayed(false);
27891         var endPoint = Roo.lib.Event.getXY(e);
27892         if(this.overlay){
27893             this.overlay.hide();
27894         }
27895         var newSize;
27896         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27897             newSize = this.dragSpecs.startSize + 
27898                 (this.placement == Roo.SplitBar.LEFT ?
27899                     endPoint[0] - this.dragSpecs.startPoint[0] :
27900                     this.dragSpecs.startPoint[0] - endPoint[0]
27901                 );
27902         }else{
27903             newSize = this.dragSpecs.startSize + 
27904                 (this.placement == Roo.SplitBar.TOP ?
27905                     endPoint[1] - this.dragSpecs.startPoint[1] :
27906                     this.dragSpecs.startPoint[1] - endPoint[1]
27907                 );
27908         }
27909         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
27910         if(newSize != this.dragSpecs.startSize){
27911             if(this.fireEvent('beforeapply', this, newSize) !== false){
27912                 this.adapter.setElementSize(this, newSize);
27913                 this.fireEvent("moved", this, newSize);
27914                 this.fireEvent("resize", this, newSize);
27915             }
27916         }
27917     },
27918     
27919     /**
27920      * Get the adapter this SplitBar uses
27921      * @return The adapter object
27922      */
27923     getAdapter : function(){
27924         return this.adapter;
27925     },
27926     
27927     /**
27928      * Set the adapter this SplitBar uses
27929      * @param {Object} adapter A SplitBar adapter object
27930      */
27931     setAdapter : function(adapter){
27932         this.adapter = adapter;
27933         this.adapter.init(this);
27934     },
27935     
27936     /**
27937      * Gets the minimum size for the resizing element
27938      * @return {Number} The minimum size
27939      */
27940     getMinimumSize : function(){
27941         return this.minSize;
27942     },
27943     
27944     /**
27945      * Sets the minimum size for the resizing element
27946      * @param {Number} minSize The minimum size
27947      */
27948     setMinimumSize : function(minSize){
27949         this.minSize = minSize;
27950     },
27951     
27952     /**
27953      * Gets the maximum size for the resizing element
27954      * @return {Number} The maximum size
27955      */
27956     getMaximumSize : function(){
27957         return this.maxSize;
27958     },
27959     
27960     /**
27961      * Sets the maximum size for the resizing element
27962      * @param {Number} maxSize The maximum size
27963      */
27964     setMaximumSize : function(maxSize){
27965         this.maxSize = maxSize;
27966     },
27967     
27968     /**
27969      * Sets the initialize size for the resizing element
27970      * @param {Number} size The initial size
27971      */
27972     setCurrentSize : function(size){
27973         var oldAnimate = this.animate;
27974         this.animate = false;
27975         this.adapter.setElementSize(this, size);
27976         this.animate = oldAnimate;
27977     },
27978     
27979     /**
27980      * Destroy this splitbar. 
27981      * @param {Boolean} removeEl True to remove the element
27982      */
27983     destroy : function(removeEl){
27984         if(this.shim){
27985             this.shim.remove();
27986         }
27987         this.dd.unreg();
27988         this.proxy.parentNode.removeChild(this.proxy);
27989         if(removeEl){
27990             this.el.remove();
27991         }
27992     }
27993 });
27994
27995 /**
27996  * @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.
27997  */
27998 Roo.SplitBar.createProxy = function(dir){
27999     var proxy = new Roo.Element(document.createElement("div"));
28000     proxy.unselectable();
28001     var cls = 'x-splitbar-proxy';
28002     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
28003     document.body.appendChild(proxy.dom);
28004     return proxy.dom;
28005 };
28006
28007 /** 
28008  * @class Roo.SplitBar.BasicLayoutAdapter
28009  * Default Adapter. It assumes the splitter and resizing element are not positioned
28010  * elements and only gets/sets the width of the element. Generally used for table based layouts.
28011  */
28012 Roo.SplitBar.BasicLayoutAdapter = function(){
28013 };
28014
28015 Roo.SplitBar.BasicLayoutAdapter.prototype = {
28016     // do nothing for now
28017     init : function(s){
28018     
28019     },
28020     /**
28021      * Called before drag operations to get the current size of the resizing element. 
28022      * @param {Roo.SplitBar} s The SplitBar using this adapter
28023      */
28024      getElementSize : function(s){
28025         if(s.orientation == Roo.SplitBar.HORIZONTAL){
28026             return s.resizingEl.getWidth();
28027         }else{
28028             return s.resizingEl.getHeight();
28029         }
28030     },
28031     
28032     /**
28033      * Called after drag operations to set the size of the resizing element.
28034      * @param {Roo.SplitBar} s The SplitBar using this adapter
28035      * @param {Number} newSize The new size to set
28036      * @param {Function} onComplete A function to be invoked when resizing is complete
28037      */
28038     setElementSize : function(s, newSize, onComplete){
28039         if(s.orientation == Roo.SplitBar.HORIZONTAL){
28040             if(!s.animate){
28041                 s.resizingEl.setWidth(newSize);
28042                 if(onComplete){
28043                     onComplete(s, newSize);
28044                 }
28045             }else{
28046                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
28047             }
28048         }else{
28049             
28050             if(!s.animate){
28051                 s.resizingEl.setHeight(newSize);
28052                 if(onComplete){
28053                     onComplete(s, newSize);
28054                 }
28055             }else{
28056                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
28057             }
28058         }
28059     }
28060 };
28061
28062 /** 
28063  *@class Roo.SplitBar.AbsoluteLayoutAdapter
28064  * @extends Roo.SplitBar.BasicLayoutAdapter
28065  * Adapter that  moves the splitter element to align with the resized sizing element. 
28066  * Used with an absolute positioned SplitBar.
28067  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
28068  * document.body, make sure you assign an id to the body element.
28069  */
28070 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
28071     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
28072     this.container = Roo.get(container);
28073 };
28074
28075 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
28076     init : function(s){
28077         this.basic.init(s);
28078     },
28079     
28080     getElementSize : function(s){
28081         return this.basic.getElementSize(s);
28082     },
28083     
28084     setElementSize : function(s, newSize, onComplete){
28085         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
28086     },
28087     
28088     moveSplitter : function(s){
28089         var yes = Roo.SplitBar;
28090         switch(s.placement){
28091             case yes.LEFT:
28092                 s.el.setX(s.resizingEl.getRight());
28093                 break;
28094             case yes.RIGHT:
28095                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
28096                 break;
28097             case yes.TOP:
28098                 s.el.setY(s.resizingEl.getBottom());
28099                 break;
28100             case yes.BOTTOM:
28101                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
28102                 break;
28103         }
28104     }
28105 };
28106
28107 /**
28108  * Orientation constant - Create a vertical SplitBar
28109  * @static
28110  * @type Number
28111  */
28112 Roo.SplitBar.VERTICAL = 1;
28113
28114 /**
28115  * Orientation constant - Create a horizontal SplitBar
28116  * @static
28117  * @type Number
28118  */
28119 Roo.SplitBar.HORIZONTAL = 2;
28120
28121 /**
28122  * Placement constant - The resizing element is to the left of the splitter element
28123  * @static
28124  * @type Number
28125  */
28126 Roo.SplitBar.LEFT = 1;
28127
28128 /**
28129  * Placement constant - The resizing element is to the right of the splitter element
28130  * @static
28131  * @type Number
28132  */
28133 Roo.SplitBar.RIGHT = 2;
28134
28135 /**
28136  * Placement constant - The resizing element is positioned above the splitter element
28137  * @static
28138  * @type Number
28139  */
28140 Roo.SplitBar.TOP = 3;
28141
28142 /**
28143  * Placement constant - The resizing element is positioned under splitter element
28144  * @static
28145  * @type Number
28146  */
28147 Roo.SplitBar.BOTTOM = 4;
28148 /*
28149  * Based on:
28150  * Ext JS Library 1.1.1
28151  * Copyright(c) 2006-2007, Ext JS, LLC.
28152  *
28153  * Originally Released Under LGPL - original licence link has changed is not relivant.
28154  *
28155  * Fork - LGPL
28156  * <script type="text/javascript">
28157  */
28158
28159 /**
28160  * @class Roo.View
28161  * @extends Roo.util.Observable
28162  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
28163  * This class also supports single and multi selection modes. <br>
28164  * Create a data model bound view:
28165  <pre><code>
28166  var store = new Roo.data.Store(...);
28167
28168  var view = new Roo.View({
28169     el : "my-element",
28170     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
28171  
28172     singleSelect: true,
28173     selectedClass: "ydataview-selected",
28174     store: store
28175  });
28176
28177  // listen for node click?
28178  view.on("click", function(vw, index, node, e){
28179  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
28180  });
28181
28182  // load XML data
28183  dataModel.load("foobar.xml");
28184  </code></pre>
28185  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
28186  * <br><br>
28187  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
28188  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
28189  * 
28190  * Note: old style constructor is still suported (container, template, config)
28191  * 
28192  * @constructor
28193  * Create a new View
28194  * @param {Object} config The config object
28195  * 
28196  */
28197 Roo.View = function(config, depreciated_tpl, depreciated_config){
28198     
28199     this.parent = false;
28200     
28201     if (typeof(depreciated_tpl) == 'undefined') {
28202         // new way.. - universal constructor.
28203         Roo.apply(this, config);
28204         this.el  = Roo.get(this.el);
28205     } else {
28206         // old format..
28207         this.el  = Roo.get(config);
28208         this.tpl = depreciated_tpl;
28209         Roo.apply(this, depreciated_config);
28210     }
28211     this.wrapEl  = this.el.wrap().wrap();
28212     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
28213     
28214     
28215     if(typeof(this.tpl) == "string"){
28216         this.tpl = new Roo.Template(this.tpl);
28217     } else {
28218         // support xtype ctors..
28219         this.tpl = new Roo.factory(this.tpl, Roo);
28220     }
28221     
28222     
28223     this.tpl.compile();
28224     
28225     /** @private */
28226     this.addEvents({
28227         /**
28228          * @event beforeclick
28229          * Fires before a click is processed. Returns false to cancel the default action.
28230          * @param {Roo.View} this
28231          * @param {Number} index The index of the target node
28232          * @param {HTMLElement} node The target node
28233          * @param {Roo.EventObject} e The raw event object
28234          */
28235             "beforeclick" : true,
28236         /**
28237          * @event click
28238          * Fires when a template node is clicked.
28239          * @param {Roo.View} this
28240          * @param {Number} index The index of the target node
28241          * @param {HTMLElement} node The target node
28242          * @param {Roo.EventObject} e The raw event object
28243          */
28244             "click" : true,
28245         /**
28246          * @event dblclick
28247          * Fires when a template node is double clicked.
28248          * @param {Roo.View} this
28249          * @param {Number} index The index of the target node
28250          * @param {HTMLElement} node The target node
28251          * @param {Roo.EventObject} e The raw event object
28252          */
28253             "dblclick" : true,
28254         /**
28255          * @event contextmenu
28256          * Fires when a template node is right clicked.
28257          * @param {Roo.View} this
28258          * @param {Number} index The index of the target node
28259          * @param {HTMLElement} node The target node
28260          * @param {Roo.EventObject} e The raw event object
28261          */
28262             "contextmenu" : true,
28263         /**
28264          * @event selectionchange
28265          * Fires when the selected nodes change.
28266          * @param {Roo.View} this
28267          * @param {Array} selections Array of the selected nodes
28268          */
28269             "selectionchange" : true,
28270     
28271         /**
28272          * @event beforeselect
28273          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
28274          * @param {Roo.View} this
28275          * @param {HTMLElement} node The node to be selected
28276          * @param {Array} selections Array of currently selected nodes
28277          */
28278             "beforeselect" : true,
28279         /**
28280          * @event preparedata
28281          * Fires on every row to render, to allow you to change the data.
28282          * @param {Roo.View} this
28283          * @param {Object} data to be rendered (change this)
28284          */
28285           "preparedata" : true
28286           
28287           
28288         });
28289
28290
28291
28292     this.el.on({
28293         "click": this.onClick,
28294         "dblclick": this.onDblClick,
28295         "contextmenu": this.onContextMenu,
28296         scope:this
28297     });
28298
28299     this.selections = [];
28300     this.nodes = [];
28301     this.cmp = new Roo.CompositeElementLite([]);
28302     if(this.store){
28303         this.store = Roo.factory(this.store, Roo.data);
28304         this.setStore(this.store, true);
28305     }
28306     
28307     if ( this.footer && this.footer.xtype) {
28308            
28309          var fctr = this.wrapEl.appendChild(document.createElement("div"));
28310         
28311         this.footer.dataSource = this.store;
28312         this.footer.container = fctr;
28313         this.footer = Roo.factory(this.footer, Roo);
28314         fctr.insertFirst(this.el);
28315         
28316         // this is a bit insane - as the paging toolbar seems to detach the el..
28317 //        dom.parentNode.parentNode.parentNode
28318          // they get detached?
28319     }
28320     
28321     
28322     Roo.View.superclass.constructor.call(this);
28323     
28324     
28325 };
28326
28327 Roo.extend(Roo.View, Roo.util.Observable, {
28328     
28329      /**
28330      * @cfg {Roo.data.Store} store Data store to load data from.
28331      */
28332     store : false,
28333     
28334     /**
28335      * @cfg {String|Roo.Element} el The container element.
28336      */
28337     el : '',
28338     
28339     /**
28340      * @cfg {String|Roo.Template} tpl The template used by this View 
28341      */
28342     tpl : false,
28343     /**
28344      * @cfg {String} dataName the named area of the template to use as the data area
28345      *                          Works with domtemplates roo-name="name"
28346      */
28347     dataName: false,
28348     /**
28349      * @cfg {String} selectedClass The css class to add to selected nodes
28350      */
28351     selectedClass : "x-view-selected",
28352      /**
28353      * @cfg {String} emptyText The empty text to show when nothing is loaded.
28354      */
28355     emptyText : "",
28356     
28357     /**
28358      * @cfg {String} text to display on mask (default Loading)
28359      */
28360     mask : false,
28361     /**
28362      * @cfg {Boolean} multiSelect Allow multiple selection
28363      */
28364     multiSelect : false,
28365     /**
28366      * @cfg {Boolean} singleSelect Allow single selection
28367      */
28368     singleSelect:  false,
28369     
28370     /**
28371      * @cfg {Boolean} toggleSelect - selecting 
28372      */
28373     toggleSelect : false,
28374     
28375     /**
28376      * @cfg {Boolean} tickable - selecting 
28377      */
28378     tickable : false,
28379     
28380     /**
28381      * Returns the element this view is bound to.
28382      * @return {Roo.Element}
28383      */
28384     getEl : function(){
28385         return this.wrapEl;
28386     },
28387     
28388     
28389
28390     /**
28391      * Refreshes the view. - called by datachanged on the store. - do not call directly.
28392      */
28393     refresh : function(){
28394         //Roo.log('refresh');
28395         var t = this.tpl;
28396         
28397         // if we are using something like 'domtemplate', then
28398         // the what gets used is:
28399         // t.applySubtemplate(NAME, data, wrapping data..)
28400         // the outer template then get' applied with
28401         //     the store 'extra data'
28402         // and the body get's added to the
28403         //      roo-name="data" node?
28404         //      <span class='roo-tpl-{name}'></span> ?????
28405         
28406         
28407         
28408         this.clearSelections();
28409         this.el.update("");
28410         var html = [];
28411         var records = this.store.getRange();
28412         if(records.length < 1) {
28413             
28414             // is this valid??  = should it render a template??
28415             
28416             this.el.update(this.emptyText);
28417             return;
28418         }
28419         var el = this.el;
28420         if (this.dataName) {
28421             this.el.update(t.apply(this.store.meta)); //????
28422             el = this.el.child('.roo-tpl-' + this.dataName);
28423         }
28424         
28425         for(var i = 0, len = records.length; i < len; i++){
28426             var data = this.prepareData(records[i].data, i, records[i]);
28427             this.fireEvent("preparedata", this, data, i, records[i]);
28428             
28429             var d = Roo.apply({}, data);
28430             
28431             if(this.tickable){
28432                 Roo.apply(d, {'roo-id' : Roo.id()});
28433                 
28434                 var _this = this;
28435             
28436                 Roo.each(this.parent.item, function(item){
28437                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
28438                         return;
28439                     }
28440                     Roo.apply(d, {'roo-data-checked' : 'checked'});
28441                 });
28442             }
28443             
28444             html[html.length] = Roo.util.Format.trim(
28445                 this.dataName ?
28446                     t.applySubtemplate(this.dataName, d, this.store.meta) :
28447                     t.apply(d)
28448             );
28449         }
28450         
28451         
28452         
28453         el.update(html.join(""));
28454         this.nodes = el.dom.childNodes;
28455         this.updateIndexes(0);
28456     },
28457     
28458
28459     /**
28460      * Function to override to reformat the data that is sent to
28461      * the template for each node.
28462      * DEPRICATED - use the preparedata event handler.
28463      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
28464      * a JSON object for an UpdateManager bound view).
28465      */
28466     prepareData : function(data, index, record)
28467     {
28468         this.fireEvent("preparedata", this, data, index, record);
28469         return data;
28470     },
28471
28472     onUpdate : function(ds, record){
28473         // Roo.log('on update');   
28474         this.clearSelections();
28475         var index = this.store.indexOf(record);
28476         var n = this.nodes[index];
28477         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
28478         n.parentNode.removeChild(n);
28479         this.updateIndexes(index, index);
28480     },
28481
28482     
28483     
28484 // --------- FIXME     
28485     onAdd : function(ds, records, index)
28486     {
28487         //Roo.log(['on Add', ds, records, index] );        
28488         this.clearSelections();
28489         if(this.nodes.length == 0){
28490             this.refresh();
28491             return;
28492         }
28493         var n = this.nodes[index];
28494         for(var i = 0, len = records.length; i < len; i++){
28495             var d = this.prepareData(records[i].data, i, records[i]);
28496             if(n){
28497                 this.tpl.insertBefore(n, d);
28498             }else{
28499                 
28500                 this.tpl.append(this.el, d);
28501             }
28502         }
28503         this.updateIndexes(index);
28504     },
28505
28506     onRemove : function(ds, record, index){
28507        // Roo.log('onRemove');
28508         this.clearSelections();
28509         var el = this.dataName  ?
28510             this.el.child('.roo-tpl-' + this.dataName) :
28511             this.el; 
28512         
28513         el.dom.removeChild(this.nodes[index]);
28514         this.updateIndexes(index);
28515     },
28516
28517     /**
28518      * Refresh an individual node.
28519      * @param {Number} index
28520      */
28521     refreshNode : function(index){
28522         this.onUpdate(this.store, this.store.getAt(index));
28523     },
28524
28525     updateIndexes : function(startIndex, endIndex){
28526         var ns = this.nodes;
28527         startIndex = startIndex || 0;
28528         endIndex = endIndex || ns.length - 1;
28529         for(var i = startIndex; i <= endIndex; i++){
28530             ns[i].nodeIndex = i;
28531         }
28532     },
28533
28534     /**
28535      * Changes the data store this view uses and refresh the view.
28536      * @param {Store} store
28537      */
28538     setStore : function(store, initial){
28539         if(!initial && this.store){
28540             this.store.un("datachanged", this.refresh);
28541             this.store.un("add", this.onAdd);
28542             this.store.un("remove", this.onRemove);
28543             this.store.un("update", this.onUpdate);
28544             this.store.un("clear", this.refresh);
28545             this.store.un("beforeload", this.onBeforeLoad);
28546             this.store.un("load", this.onLoad);
28547             this.store.un("loadexception", this.onLoad);
28548         }
28549         if(store){
28550           
28551             store.on("datachanged", this.refresh, this);
28552             store.on("add", this.onAdd, this);
28553             store.on("remove", this.onRemove, this);
28554             store.on("update", this.onUpdate, this);
28555             store.on("clear", this.refresh, this);
28556             store.on("beforeload", this.onBeforeLoad, this);
28557             store.on("load", this.onLoad, this);
28558             store.on("loadexception", this.onLoad, this);
28559         }
28560         
28561         if(store){
28562             this.refresh();
28563         }
28564     },
28565     /**
28566      * onbeforeLoad - masks the loading area.
28567      *
28568      */
28569     onBeforeLoad : function(store,opts)
28570     {
28571          //Roo.log('onBeforeLoad');   
28572         if (!opts.add) {
28573             this.el.update("");
28574         }
28575         this.el.mask(this.mask ? this.mask : "Loading" ); 
28576     },
28577     onLoad : function ()
28578     {
28579         this.el.unmask();
28580     },
28581     
28582
28583     /**
28584      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
28585      * @param {HTMLElement} node
28586      * @return {HTMLElement} The template node
28587      */
28588     findItemFromChild : function(node){
28589         var el = this.dataName  ?
28590             this.el.child('.roo-tpl-' + this.dataName,true) :
28591             this.el.dom; 
28592         
28593         if(!node || node.parentNode == el){
28594                     return node;
28595             }
28596             var p = node.parentNode;
28597             while(p && p != el){
28598             if(p.parentNode == el){
28599                 return p;
28600             }
28601             p = p.parentNode;
28602         }
28603             return null;
28604     },
28605
28606     /** @ignore */
28607     onClick : function(e){
28608         var item = this.findItemFromChild(e.getTarget());
28609         if(item){
28610             var index = this.indexOf(item);
28611             if(this.onItemClick(item, index, e) !== false){
28612                 this.fireEvent("click", this, index, item, e);
28613             }
28614         }else{
28615             this.clearSelections();
28616         }
28617     },
28618
28619     /** @ignore */
28620     onContextMenu : function(e){
28621         var item = this.findItemFromChild(e.getTarget());
28622         if(item){
28623             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
28624         }
28625     },
28626
28627     /** @ignore */
28628     onDblClick : function(e){
28629         var item = this.findItemFromChild(e.getTarget());
28630         if(item){
28631             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
28632         }
28633     },
28634
28635     onItemClick : function(item, index, e)
28636     {
28637         if(this.fireEvent("beforeclick", this, index, item, e) === false){
28638             return false;
28639         }
28640         if (this.toggleSelect) {
28641             var m = this.isSelected(item) ? 'unselect' : 'select';
28642             //Roo.log(m);
28643             var _t = this;
28644             _t[m](item, true, false);
28645             return true;
28646         }
28647         if(this.multiSelect || this.singleSelect){
28648             if(this.multiSelect && e.shiftKey && this.lastSelection){
28649                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
28650             }else{
28651                 this.select(item, this.multiSelect && e.ctrlKey);
28652                 this.lastSelection = item;
28653             }
28654             
28655             if(!this.tickable){
28656                 e.preventDefault();
28657             }
28658             
28659         }
28660         return true;
28661     },
28662
28663     /**
28664      * Get the number of selected nodes.
28665      * @return {Number}
28666      */
28667     getSelectionCount : function(){
28668         return this.selections.length;
28669     },
28670
28671     /**
28672      * Get the currently selected nodes.
28673      * @return {Array} An array of HTMLElements
28674      */
28675     getSelectedNodes : function(){
28676         return this.selections;
28677     },
28678
28679     /**
28680      * Get the indexes of the selected nodes.
28681      * @return {Array}
28682      */
28683     getSelectedIndexes : function(){
28684         var indexes = [], s = this.selections;
28685         for(var i = 0, len = s.length; i < len; i++){
28686             indexes.push(s[i].nodeIndex);
28687         }
28688         return indexes;
28689     },
28690
28691     /**
28692      * Clear all selections
28693      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
28694      */
28695     clearSelections : function(suppressEvent){
28696         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
28697             this.cmp.elements = this.selections;
28698             this.cmp.removeClass(this.selectedClass);
28699             this.selections = [];
28700             if(!suppressEvent){
28701                 this.fireEvent("selectionchange", this, this.selections);
28702             }
28703         }
28704     },
28705
28706     /**
28707      * Returns true if the passed node is selected
28708      * @param {HTMLElement/Number} node The node or node index
28709      * @return {Boolean}
28710      */
28711     isSelected : function(node){
28712         var s = this.selections;
28713         if(s.length < 1){
28714             return false;
28715         }
28716         node = this.getNode(node);
28717         return s.indexOf(node) !== -1;
28718     },
28719
28720     /**
28721      * Selects nodes.
28722      * @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
28723      * @param {Boolean} keepExisting (optional) true to keep existing selections
28724      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28725      */
28726     select : function(nodeInfo, keepExisting, suppressEvent){
28727         if(nodeInfo instanceof Array){
28728             if(!keepExisting){
28729                 this.clearSelections(true);
28730             }
28731             for(var i = 0, len = nodeInfo.length; i < len; i++){
28732                 this.select(nodeInfo[i], true, true);
28733             }
28734             return;
28735         } 
28736         var node = this.getNode(nodeInfo);
28737         if(!node || this.isSelected(node)){
28738             return; // already selected.
28739         }
28740         if(!keepExisting){
28741             this.clearSelections(true);
28742         }
28743         
28744         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28745             Roo.fly(node).addClass(this.selectedClass);
28746             this.selections.push(node);
28747             if(!suppressEvent){
28748                 this.fireEvent("selectionchange", this, this.selections);
28749             }
28750         }
28751         
28752         
28753     },
28754       /**
28755      * Unselects nodes.
28756      * @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
28757      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
28758      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28759      */
28760     unselect : function(nodeInfo, keepExisting, suppressEvent)
28761     {
28762         if(nodeInfo instanceof Array){
28763             Roo.each(this.selections, function(s) {
28764                 this.unselect(s, nodeInfo);
28765             }, this);
28766             return;
28767         }
28768         var node = this.getNode(nodeInfo);
28769         if(!node || !this.isSelected(node)){
28770             //Roo.log("not selected");
28771             return; // not selected.
28772         }
28773         // fireevent???
28774         var ns = [];
28775         Roo.each(this.selections, function(s) {
28776             if (s == node ) {
28777                 Roo.fly(node).removeClass(this.selectedClass);
28778
28779                 return;
28780             }
28781             ns.push(s);
28782         },this);
28783         
28784         this.selections= ns;
28785         this.fireEvent("selectionchange", this, this.selections);
28786     },
28787
28788     /**
28789      * Gets a template node.
28790      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28791      * @return {HTMLElement} The node or null if it wasn't found
28792      */
28793     getNode : function(nodeInfo){
28794         if(typeof nodeInfo == "string"){
28795             return document.getElementById(nodeInfo);
28796         }else if(typeof nodeInfo == "number"){
28797             return this.nodes[nodeInfo];
28798         }
28799         return nodeInfo;
28800     },
28801
28802     /**
28803      * Gets a range template nodes.
28804      * @param {Number} startIndex
28805      * @param {Number} endIndex
28806      * @return {Array} An array of nodes
28807      */
28808     getNodes : function(start, end){
28809         var ns = this.nodes;
28810         start = start || 0;
28811         end = typeof end == "undefined" ? ns.length - 1 : end;
28812         var nodes = [];
28813         if(start <= end){
28814             for(var i = start; i <= end; i++){
28815                 nodes.push(ns[i]);
28816             }
28817         } else{
28818             for(var i = start; i >= end; i--){
28819                 nodes.push(ns[i]);
28820             }
28821         }
28822         return nodes;
28823     },
28824
28825     /**
28826      * Finds the index of the passed node
28827      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28828      * @return {Number} The index of the node or -1
28829      */
28830     indexOf : function(node){
28831         node = this.getNode(node);
28832         if(typeof node.nodeIndex == "number"){
28833             return node.nodeIndex;
28834         }
28835         var ns = this.nodes;
28836         for(var i = 0, len = ns.length; i < len; i++){
28837             if(ns[i] == node){
28838                 return i;
28839             }
28840         }
28841         return -1;
28842     }
28843 });
28844 /*
28845  * Based on:
28846  * Ext JS Library 1.1.1
28847  * Copyright(c) 2006-2007, Ext JS, LLC.
28848  *
28849  * Originally Released Under LGPL - original licence link has changed is not relivant.
28850  *
28851  * Fork - LGPL
28852  * <script type="text/javascript">
28853  */
28854
28855 /**
28856  * @class Roo.JsonView
28857  * @extends Roo.View
28858  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
28859 <pre><code>
28860 var view = new Roo.JsonView({
28861     container: "my-element",
28862     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
28863     multiSelect: true, 
28864     jsonRoot: "data" 
28865 });
28866
28867 // listen for node click?
28868 view.on("click", function(vw, index, node, e){
28869     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
28870 });
28871
28872 // direct load of JSON data
28873 view.load("foobar.php");
28874
28875 // Example from my blog list
28876 var tpl = new Roo.Template(
28877     '&lt;div class="entry"&gt;' +
28878     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
28879     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
28880     "&lt;/div&gt;&lt;hr /&gt;"
28881 );
28882
28883 var moreView = new Roo.JsonView({
28884     container :  "entry-list", 
28885     template : tpl,
28886     jsonRoot: "posts"
28887 });
28888 moreView.on("beforerender", this.sortEntries, this);
28889 moreView.load({
28890     url: "/blog/get-posts.php",
28891     params: "allposts=true",
28892     text: "Loading Blog Entries..."
28893 });
28894 </code></pre>
28895
28896 * Note: old code is supported with arguments : (container, template, config)
28897
28898
28899  * @constructor
28900  * Create a new JsonView
28901  * 
28902  * @param {Object} config The config object
28903  * 
28904  */
28905 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
28906     
28907     
28908     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
28909
28910     var um = this.el.getUpdateManager();
28911     um.setRenderer(this);
28912     um.on("update", this.onLoad, this);
28913     um.on("failure", this.onLoadException, this);
28914
28915     /**
28916      * @event beforerender
28917      * Fires before rendering of the downloaded JSON data.
28918      * @param {Roo.JsonView} this
28919      * @param {Object} data The JSON data loaded
28920      */
28921     /**
28922      * @event load
28923      * Fires when data is loaded.
28924      * @param {Roo.JsonView} this
28925      * @param {Object} data The JSON data loaded
28926      * @param {Object} response The raw Connect response object
28927      */
28928     /**
28929      * @event loadexception
28930      * Fires when loading fails.
28931      * @param {Roo.JsonView} this
28932      * @param {Object} response The raw Connect response object
28933      */
28934     this.addEvents({
28935         'beforerender' : true,
28936         'load' : true,
28937         'loadexception' : true
28938     });
28939 };
28940 Roo.extend(Roo.JsonView, Roo.View, {
28941     /**
28942      * @type {String} The root property in the loaded JSON object that contains the data
28943      */
28944     jsonRoot : "",
28945
28946     /**
28947      * Refreshes the view.
28948      */
28949     refresh : function(){
28950         this.clearSelections();
28951         this.el.update("");
28952         var html = [];
28953         var o = this.jsonData;
28954         if(o && o.length > 0){
28955             for(var i = 0, len = o.length; i < len; i++){
28956                 var data = this.prepareData(o[i], i, o);
28957                 html[html.length] = this.tpl.apply(data);
28958             }
28959         }else{
28960             html.push(this.emptyText);
28961         }
28962         this.el.update(html.join(""));
28963         this.nodes = this.el.dom.childNodes;
28964         this.updateIndexes(0);
28965     },
28966
28967     /**
28968      * 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.
28969      * @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:
28970      <pre><code>
28971      view.load({
28972          url: "your-url.php",
28973          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
28974          callback: yourFunction,
28975          scope: yourObject, //(optional scope)
28976          discardUrl: false,
28977          nocache: false,
28978          text: "Loading...",
28979          timeout: 30,
28980          scripts: false
28981      });
28982      </code></pre>
28983      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
28984      * 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.
28985      * @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}
28986      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
28987      * @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.
28988      */
28989     load : function(){
28990         var um = this.el.getUpdateManager();
28991         um.update.apply(um, arguments);
28992     },
28993
28994     // note - render is a standard framework call...
28995     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
28996     render : function(el, response){
28997         
28998         this.clearSelections();
28999         this.el.update("");
29000         var o;
29001         try{
29002             if (response != '') {
29003                 o = Roo.util.JSON.decode(response.responseText);
29004                 if(this.jsonRoot){
29005                     
29006                     o = o[this.jsonRoot];
29007                 }
29008             }
29009         } catch(e){
29010         }
29011         /**
29012          * The current JSON data or null
29013          */
29014         this.jsonData = o;
29015         this.beforeRender();
29016         this.refresh();
29017     },
29018
29019 /**
29020  * Get the number of records in the current JSON dataset
29021  * @return {Number}
29022  */
29023     getCount : function(){
29024         return this.jsonData ? this.jsonData.length : 0;
29025     },
29026
29027 /**
29028  * Returns the JSON object for the specified node(s)
29029  * @param {HTMLElement/Array} node The node or an array of nodes
29030  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
29031  * you get the JSON object for the node
29032  */
29033     getNodeData : function(node){
29034         if(node instanceof Array){
29035             var data = [];
29036             for(var i = 0, len = node.length; i < len; i++){
29037                 data.push(this.getNodeData(node[i]));
29038             }
29039             return data;
29040         }
29041         return this.jsonData[this.indexOf(node)] || null;
29042     },
29043
29044     beforeRender : function(){
29045         this.snapshot = this.jsonData;
29046         if(this.sortInfo){
29047             this.sort.apply(this, this.sortInfo);
29048         }
29049         this.fireEvent("beforerender", this, this.jsonData);
29050     },
29051
29052     onLoad : function(el, o){
29053         this.fireEvent("load", this, this.jsonData, o);
29054     },
29055
29056     onLoadException : function(el, o){
29057         this.fireEvent("loadexception", this, o);
29058     },
29059
29060 /**
29061  * Filter the data by a specific property.
29062  * @param {String} property A property on your JSON objects
29063  * @param {String/RegExp} value Either string that the property values
29064  * should start with, or a RegExp to test against the property
29065  */
29066     filter : function(property, value){
29067         if(this.jsonData){
29068             var data = [];
29069             var ss = this.snapshot;
29070             if(typeof value == "string"){
29071                 var vlen = value.length;
29072                 if(vlen == 0){
29073                     this.clearFilter();
29074                     return;
29075                 }
29076                 value = value.toLowerCase();
29077                 for(var i = 0, len = ss.length; i < len; i++){
29078                     var o = ss[i];
29079                     if(o[property].substr(0, vlen).toLowerCase() == value){
29080                         data.push(o);
29081                     }
29082                 }
29083             } else if(value.exec){ // regex?
29084                 for(var i = 0, len = ss.length; i < len; i++){
29085                     var o = ss[i];
29086                     if(value.test(o[property])){
29087                         data.push(o);
29088                     }
29089                 }
29090             } else{
29091                 return;
29092             }
29093             this.jsonData = data;
29094             this.refresh();
29095         }
29096     },
29097
29098 /**
29099  * Filter by a function. The passed function will be called with each
29100  * object in the current dataset. If the function returns true the value is kept,
29101  * otherwise it is filtered.
29102  * @param {Function} fn
29103  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
29104  */
29105     filterBy : function(fn, scope){
29106         if(this.jsonData){
29107             var data = [];
29108             var ss = this.snapshot;
29109             for(var i = 0, len = ss.length; i < len; i++){
29110                 var o = ss[i];
29111                 if(fn.call(scope || this, o)){
29112                     data.push(o);
29113                 }
29114             }
29115             this.jsonData = data;
29116             this.refresh();
29117         }
29118     },
29119
29120 /**
29121  * Clears the current filter.
29122  */
29123     clearFilter : function(){
29124         if(this.snapshot && this.jsonData != this.snapshot){
29125             this.jsonData = this.snapshot;
29126             this.refresh();
29127         }
29128     },
29129
29130
29131 /**
29132  * Sorts the data for this view and refreshes it.
29133  * @param {String} property A property on your JSON objects to sort on
29134  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
29135  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
29136  */
29137     sort : function(property, dir, sortType){
29138         this.sortInfo = Array.prototype.slice.call(arguments, 0);
29139         if(this.jsonData){
29140             var p = property;
29141             var dsc = dir && dir.toLowerCase() == "desc";
29142             var f = function(o1, o2){
29143                 var v1 = sortType ? sortType(o1[p]) : o1[p];
29144                 var v2 = sortType ? sortType(o2[p]) : o2[p];
29145                 ;
29146                 if(v1 < v2){
29147                     return dsc ? +1 : -1;
29148                 } else if(v1 > v2){
29149                     return dsc ? -1 : +1;
29150                 } else{
29151                     return 0;
29152                 }
29153             };
29154             this.jsonData.sort(f);
29155             this.refresh();
29156             if(this.jsonData != this.snapshot){
29157                 this.snapshot.sort(f);
29158             }
29159         }
29160     }
29161 });/*
29162  * Based on:
29163  * Ext JS Library 1.1.1
29164  * Copyright(c) 2006-2007, Ext JS, LLC.
29165  *
29166  * Originally Released Under LGPL - original licence link has changed is not relivant.
29167  *
29168  * Fork - LGPL
29169  * <script type="text/javascript">
29170  */
29171  
29172
29173 /**
29174  * @class Roo.ColorPalette
29175  * @extends Roo.Component
29176  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
29177  * Here's an example of typical usage:
29178  * <pre><code>
29179 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
29180 cp.render('my-div');
29181
29182 cp.on('select', function(palette, selColor){
29183     // do something with selColor
29184 });
29185 </code></pre>
29186  * @constructor
29187  * Create a new ColorPalette
29188  * @param {Object} config The config object
29189  */
29190 Roo.ColorPalette = function(config){
29191     Roo.ColorPalette.superclass.constructor.call(this, config);
29192     this.addEvents({
29193         /**
29194              * @event select
29195              * Fires when a color is selected
29196              * @param {ColorPalette} this
29197              * @param {String} color The 6-digit color hex code (without the # symbol)
29198              */
29199         select: true
29200     });
29201
29202     if(this.handler){
29203         this.on("select", this.handler, this.scope, true);
29204     }
29205 };
29206 Roo.extend(Roo.ColorPalette, Roo.Component, {
29207     /**
29208      * @cfg {String} itemCls
29209      * The CSS class to apply to the containing element (defaults to "x-color-palette")
29210      */
29211     itemCls : "x-color-palette",
29212     /**
29213      * @cfg {String} value
29214      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
29215      * the hex codes are case-sensitive.
29216      */
29217     value : null,
29218     clickEvent:'click',
29219     // private
29220     ctype: "Roo.ColorPalette",
29221
29222     /**
29223      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
29224      */
29225     allowReselect : false,
29226
29227     /**
29228      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
29229      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
29230      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
29231      * of colors with the width setting until the box is symmetrical.</p>
29232      * <p>You can override individual colors if needed:</p>
29233      * <pre><code>
29234 var cp = new Roo.ColorPalette();
29235 cp.colors[0] = "FF0000";  // change the first box to red
29236 </code></pre>
29237
29238 Or you can provide a custom array of your own for complete control:
29239 <pre><code>
29240 var cp = new Roo.ColorPalette();
29241 cp.colors = ["000000", "993300", "333300"];
29242 </code></pre>
29243      * @type Array
29244      */
29245     colors : [
29246         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
29247         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
29248         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
29249         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
29250         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
29251     ],
29252
29253     // private
29254     onRender : function(container, position){
29255         var t = new Roo.MasterTemplate(
29256             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
29257         );
29258         var c = this.colors;
29259         for(var i = 0, len = c.length; i < len; i++){
29260             t.add([c[i]]);
29261         }
29262         var el = document.createElement("div");
29263         el.className = this.itemCls;
29264         t.overwrite(el);
29265         container.dom.insertBefore(el, position);
29266         this.el = Roo.get(el);
29267         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
29268         if(this.clickEvent != 'click'){
29269             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
29270         }
29271     },
29272
29273     // private
29274     afterRender : function(){
29275         Roo.ColorPalette.superclass.afterRender.call(this);
29276         if(this.value){
29277             var s = this.value;
29278             this.value = null;
29279             this.select(s);
29280         }
29281     },
29282
29283     // private
29284     handleClick : function(e, t){
29285         e.preventDefault();
29286         if(!this.disabled){
29287             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
29288             this.select(c.toUpperCase());
29289         }
29290     },
29291
29292     /**
29293      * Selects the specified color in the palette (fires the select event)
29294      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
29295      */
29296     select : function(color){
29297         color = color.replace("#", "");
29298         if(color != this.value || this.allowReselect){
29299             var el = this.el;
29300             if(this.value){
29301                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
29302             }
29303             el.child("a.color-"+color).addClass("x-color-palette-sel");
29304             this.value = color;
29305             this.fireEvent("select", this, color);
29306         }
29307     }
29308 });/*
29309  * Based on:
29310  * Ext JS Library 1.1.1
29311  * Copyright(c) 2006-2007, Ext JS, LLC.
29312  *
29313  * Originally Released Under LGPL - original licence link has changed is not relivant.
29314  *
29315  * Fork - LGPL
29316  * <script type="text/javascript">
29317  */
29318  
29319 /**
29320  * @class Roo.DatePicker
29321  * @extends Roo.Component
29322  * Simple date picker class.
29323  * @constructor
29324  * Create a new DatePicker
29325  * @param {Object} config The config object
29326  */
29327 Roo.DatePicker = function(config){
29328     Roo.DatePicker.superclass.constructor.call(this, config);
29329
29330     this.value = config && config.value ?
29331                  config.value.clearTime() : new Date().clearTime();
29332
29333     this.addEvents({
29334         /**
29335              * @event select
29336              * Fires when a date is selected
29337              * @param {DatePicker} this
29338              * @param {Date} date The selected date
29339              */
29340         'select': true,
29341         /**
29342              * @event monthchange
29343              * Fires when the displayed month changes 
29344              * @param {DatePicker} this
29345              * @param {Date} date The selected month
29346              */
29347         'monthchange': true
29348     });
29349
29350     if(this.handler){
29351         this.on("select", this.handler,  this.scope || this);
29352     }
29353     // build the disabledDatesRE
29354     if(!this.disabledDatesRE && this.disabledDates){
29355         var dd = this.disabledDates;
29356         var re = "(?:";
29357         for(var i = 0; i < dd.length; i++){
29358             re += dd[i];
29359             if(i != dd.length-1) {
29360                 re += "|";
29361             }
29362         }
29363         this.disabledDatesRE = new RegExp(re + ")");
29364     }
29365 };
29366
29367 Roo.extend(Roo.DatePicker, Roo.Component, {
29368     /**
29369      * @cfg {String} todayText
29370      * The text to display on the button that selects the current date (defaults to "Today")
29371      */
29372     todayText : "Today",
29373     /**
29374      * @cfg {String} okText
29375      * The text to display on the ok button
29376      */
29377     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
29378     /**
29379      * @cfg {String} cancelText
29380      * The text to display on the cancel button
29381      */
29382     cancelText : "Cancel",
29383     /**
29384      * @cfg {String} todayTip
29385      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
29386      */
29387     todayTip : "{0} (Spacebar)",
29388     /**
29389      * @cfg {Date} minDate
29390      * Minimum allowable date (JavaScript date object, defaults to null)
29391      */
29392     minDate : null,
29393     /**
29394      * @cfg {Date} maxDate
29395      * Maximum allowable date (JavaScript date object, defaults to null)
29396      */
29397     maxDate : null,
29398     /**
29399      * @cfg {String} minText
29400      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
29401      */
29402     minText : "This date is before the minimum date",
29403     /**
29404      * @cfg {String} maxText
29405      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
29406      */
29407     maxText : "This date is after the maximum date",
29408     /**
29409      * @cfg {String} format
29410      * The default date format string which can be overriden for localization support.  The format must be
29411      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
29412      */
29413     format : "m/d/y",
29414     /**
29415      * @cfg {Array} disabledDays
29416      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
29417      */
29418     disabledDays : null,
29419     /**
29420      * @cfg {String} disabledDaysText
29421      * The tooltip to display when the date falls on a disabled day (defaults to "")
29422      */
29423     disabledDaysText : "",
29424     /**
29425      * @cfg {RegExp} disabledDatesRE
29426      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
29427      */
29428     disabledDatesRE : null,
29429     /**
29430      * @cfg {String} disabledDatesText
29431      * The tooltip text to display when the date falls on a disabled date (defaults to "")
29432      */
29433     disabledDatesText : "",
29434     /**
29435      * @cfg {Boolean} constrainToViewport
29436      * True to constrain the date picker to the viewport (defaults to true)
29437      */
29438     constrainToViewport : true,
29439     /**
29440      * @cfg {Array} monthNames
29441      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
29442      */
29443     monthNames : Date.monthNames,
29444     /**
29445      * @cfg {Array} dayNames
29446      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
29447      */
29448     dayNames : Date.dayNames,
29449     /**
29450      * @cfg {String} nextText
29451      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
29452      */
29453     nextText: 'Next Month (Control+Right)',
29454     /**
29455      * @cfg {String} prevText
29456      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
29457      */
29458     prevText: 'Previous Month (Control+Left)',
29459     /**
29460      * @cfg {String} monthYearText
29461      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
29462      */
29463     monthYearText: 'Choose a month (Control+Up/Down to move years)',
29464     /**
29465      * @cfg {Number} startDay
29466      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
29467      */
29468     startDay : 0,
29469     /**
29470      * @cfg {Bool} showClear
29471      * Show a clear button (usefull for date form elements that can be blank.)
29472      */
29473     
29474     showClear: false,
29475     
29476     /**
29477      * Sets the value of the date field
29478      * @param {Date} value The date to set
29479      */
29480     setValue : function(value){
29481         var old = this.value;
29482         
29483         if (typeof(value) == 'string') {
29484          
29485             value = Date.parseDate(value, this.format);
29486         }
29487         if (!value) {
29488             value = new Date();
29489         }
29490         
29491         this.value = value.clearTime(true);
29492         if(this.el){
29493             this.update(this.value);
29494         }
29495     },
29496
29497     /**
29498      * Gets the current selected value of the date field
29499      * @return {Date} The selected date
29500      */
29501     getValue : function(){
29502         return this.value;
29503     },
29504
29505     // private
29506     focus : function(){
29507         if(this.el){
29508             this.update(this.activeDate);
29509         }
29510     },
29511
29512     // privateval
29513     onRender : function(container, position){
29514         
29515         var m = [
29516              '<table cellspacing="0">',
29517                 '<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>',
29518                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
29519         var dn = this.dayNames;
29520         for(var i = 0; i < 7; i++){
29521             var d = this.startDay+i;
29522             if(d > 6){
29523                 d = d-7;
29524             }
29525             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
29526         }
29527         m[m.length] = "</tr></thead><tbody><tr>";
29528         for(var i = 0; i < 42; i++) {
29529             if(i % 7 == 0 && i != 0){
29530                 m[m.length] = "</tr><tr>";
29531             }
29532             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
29533         }
29534         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
29535             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
29536
29537         var el = document.createElement("div");
29538         el.className = "x-date-picker";
29539         el.innerHTML = m.join("");
29540
29541         container.dom.insertBefore(el, position);
29542
29543         this.el = Roo.get(el);
29544         this.eventEl = Roo.get(el.firstChild);
29545
29546         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
29547             handler: this.showPrevMonth,
29548             scope: this,
29549             preventDefault:true,
29550             stopDefault:true
29551         });
29552
29553         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
29554             handler: this.showNextMonth,
29555             scope: this,
29556             preventDefault:true,
29557             stopDefault:true
29558         });
29559
29560         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
29561
29562         this.monthPicker = this.el.down('div.x-date-mp');
29563         this.monthPicker.enableDisplayMode('block');
29564         
29565         var kn = new Roo.KeyNav(this.eventEl, {
29566             "left" : function(e){
29567                 e.ctrlKey ?
29568                     this.showPrevMonth() :
29569                     this.update(this.activeDate.add("d", -1));
29570             },
29571
29572             "right" : function(e){
29573                 e.ctrlKey ?
29574                     this.showNextMonth() :
29575                     this.update(this.activeDate.add("d", 1));
29576             },
29577
29578             "up" : function(e){
29579                 e.ctrlKey ?
29580                     this.showNextYear() :
29581                     this.update(this.activeDate.add("d", -7));
29582             },
29583
29584             "down" : function(e){
29585                 e.ctrlKey ?
29586                     this.showPrevYear() :
29587                     this.update(this.activeDate.add("d", 7));
29588             },
29589
29590             "pageUp" : function(e){
29591                 this.showNextMonth();
29592             },
29593
29594             "pageDown" : function(e){
29595                 this.showPrevMonth();
29596             },
29597
29598             "enter" : function(e){
29599                 e.stopPropagation();
29600                 return true;
29601             },
29602
29603             scope : this
29604         });
29605
29606         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
29607
29608         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
29609
29610         this.el.unselectable();
29611         
29612         this.cells = this.el.select("table.x-date-inner tbody td");
29613         this.textNodes = this.el.query("table.x-date-inner tbody span");
29614
29615         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
29616             text: "&#160;",
29617             tooltip: this.monthYearText
29618         });
29619
29620         this.mbtn.on('click', this.showMonthPicker, this);
29621         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
29622
29623
29624         var today = (new Date()).dateFormat(this.format);
29625         
29626         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
29627         if (this.showClear) {
29628             baseTb.add( new Roo.Toolbar.Fill());
29629         }
29630         baseTb.add({
29631             text: String.format(this.todayText, today),
29632             tooltip: String.format(this.todayTip, today),
29633             handler: this.selectToday,
29634             scope: this
29635         });
29636         
29637         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
29638             
29639         //});
29640         if (this.showClear) {
29641             
29642             baseTb.add( new Roo.Toolbar.Fill());
29643             baseTb.add({
29644                 text: '&#160;',
29645                 cls: 'x-btn-icon x-btn-clear',
29646                 handler: function() {
29647                     //this.value = '';
29648                     this.fireEvent("select", this, '');
29649                 },
29650                 scope: this
29651             });
29652         }
29653         
29654         
29655         if(Roo.isIE){
29656             this.el.repaint();
29657         }
29658         this.update(this.value);
29659     },
29660
29661     createMonthPicker : function(){
29662         if(!this.monthPicker.dom.firstChild){
29663             var buf = ['<table border="0" cellspacing="0">'];
29664             for(var i = 0; i < 6; i++){
29665                 buf.push(
29666                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
29667                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
29668                     i == 0 ?
29669                     '<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>' :
29670                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
29671                 );
29672             }
29673             buf.push(
29674                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
29675                     this.okText,
29676                     '</button><button type="button" class="x-date-mp-cancel">',
29677                     this.cancelText,
29678                     '</button></td></tr>',
29679                 '</table>'
29680             );
29681             this.monthPicker.update(buf.join(''));
29682             this.monthPicker.on('click', this.onMonthClick, this);
29683             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
29684
29685             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
29686             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
29687
29688             this.mpMonths.each(function(m, a, i){
29689                 i += 1;
29690                 if((i%2) == 0){
29691                     m.dom.xmonth = 5 + Math.round(i * .5);
29692                 }else{
29693                     m.dom.xmonth = Math.round((i-1) * .5);
29694                 }
29695             });
29696         }
29697     },
29698
29699     showMonthPicker : function(){
29700         this.createMonthPicker();
29701         var size = this.el.getSize();
29702         this.monthPicker.setSize(size);
29703         this.monthPicker.child('table').setSize(size);
29704
29705         this.mpSelMonth = (this.activeDate || this.value).getMonth();
29706         this.updateMPMonth(this.mpSelMonth);
29707         this.mpSelYear = (this.activeDate || this.value).getFullYear();
29708         this.updateMPYear(this.mpSelYear);
29709
29710         this.monthPicker.slideIn('t', {duration:.2});
29711     },
29712
29713     updateMPYear : function(y){
29714         this.mpyear = y;
29715         var ys = this.mpYears.elements;
29716         for(var i = 1; i <= 10; i++){
29717             var td = ys[i-1], y2;
29718             if((i%2) == 0){
29719                 y2 = y + Math.round(i * .5);
29720                 td.firstChild.innerHTML = y2;
29721                 td.xyear = y2;
29722             }else{
29723                 y2 = y - (5-Math.round(i * .5));
29724                 td.firstChild.innerHTML = y2;
29725                 td.xyear = y2;
29726             }
29727             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
29728         }
29729     },
29730
29731     updateMPMonth : function(sm){
29732         this.mpMonths.each(function(m, a, i){
29733             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
29734         });
29735     },
29736
29737     selectMPMonth: function(m){
29738         
29739     },
29740
29741     onMonthClick : function(e, t){
29742         e.stopEvent();
29743         var el = new Roo.Element(t), pn;
29744         if(el.is('button.x-date-mp-cancel')){
29745             this.hideMonthPicker();
29746         }
29747         else if(el.is('button.x-date-mp-ok')){
29748             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29749             this.hideMonthPicker();
29750         }
29751         else if(pn = el.up('td.x-date-mp-month', 2)){
29752             this.mpMonths.removeClass('x-date-mp-sel');
29753             pn.addClass('x-date-mp-sel');
29754             this.mpSelMonth = pn.dom.xmonth;
29755         }
29756         else if(pn = el.up('td.x-date-mp-year', 2)){
29757             this.mpYears.removeClass('x-date-mp-sel');
29758             pn.addClass('x-date-mp-sel');
29759             this.mpSelYear = pn.dom.xyear;
29760         }
29761         else if(el.is('a.x-date-mp-prev')){
29762             this.updateMPYear(this.mpyear-10);
29763         }
29764         else if(el.is('a.x-date-mp-next')){
29765             this.updateMPYear(this.mpyear+10);
29766         }
29767     },
29768
29769     onMonthDblClick : function(e, t){
29770         e.stopEvent();
29771         var el = new Roo.Element(t), pn;
29772         if(pn = el.up('td.x-date-mp-month', 2)){
29773             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
29774             this.hideMonthPicker();
29775         }
29776         else if(pn = el.up('td.x-date-mp-year', 2)){
29777             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29778             this.hideMonthPicker();
29779         }
29780     },
29781
29782     hideMonthPicker : function(disableAnim){
29783         if(this.monthPicker){
29784             if(disableAnim === true){
29785                 this.monthPicker.hide();
29786             }else{
29787                 this.monthPicker.slideOut('t', {duration:.2});
29788             }
29789         }
29790     },
29791
29792     // private
29793     showPrevMonth : function(e){
29794         this.update(this.activeDate.add("mo", -1));
29795     },
29796
29797     // private
29798     showNextMonth : function(e){
29799         this.update(this.activeDate.add("mo", 1));
29800     },
29801
29802     // private
29803     showPrevYear : function(){
29804         this.update(this.activeDate.add("y", -1));
29805     },
29806
29807     // private
29808     showNextYear : function(){
29809         this.update(this.activeDate.add("y", 1));
29810     },
29811
29812     // private
29813     handleMouseWheel : function(e){
29814         var delta = e.getWheelDelta();
29815         if(delta > 0){
29816             this.showPrevMonth();
29817             e.stopEvent();
29818         } else if(delta < 0){
29819             this.showNextMonth();
29820             e.stopEvent();
29821         }
29822     },
29823
29824     // private
29825     handleDateClick : function(e, t){
29826         e.stopEvent();
29827         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
29828             this.setValue(new Date(t.dateValue));
29829             this.fireEvent("select", this, this.value);
29830         }
29831     },
29832
29833     // private
29834     selectToday : function(){
29835         this.setValue(new Date().clearTime());
29836         this.fireEvent("select", this, this.value);
29837     },
29838
29839     // private
29840     update : function(date)
29841     {
29842         var vd = this.activeDate;
29843         this.activeDate = date;
29844         if(vd && this.el){
29845             var t = date.getTime();
29846             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
29847                 this.cells.removeClass("x-date-selected");
29848                 this.cells.each(function(c){
29849                    if(c.dom.firstChild.dateValue == t){
29850                        c.addClass("x-date-selected");
29851                        setTimeout(function(){
29852                             try{c.dom.firstChild.focus();}catch(e){}
29853                        }, 50);
29854                        return false;
29855                    }
29856                 });
29857                 return;
29858             }
29859         }
29860         
29861         var days = date.getDaysInMonth();
29862         var firstOfMonth = date.getFirstDateOfMonth();
29863         var startingPos = firstOfMonth.getDay()-this.startDay;
29864
29865         if(startingPos <= this.startDay){
29866             startingPos += 7;
29867         }
29868
29869         var pm = date.add("mo", -1);
29870         var prevStart = pm.getDaysInMonth()-startingPos;
29871
29872         var cells = this.cells.elements;
29873         var textEls = this.textNodes;
29874         days += startingPos;
29875
29876         // convert everything to numbers so it's fast
29877         var day = 86400000;
29878         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
29879         var today = new Date().clearTime().getTime();
29880         var sel = date.clearTime().getTime();
29881         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
29882         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
29883         var ddMatch = this.disabledDatesRE;
29884         var ddText = this.disabledDatesText;
29885         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
29886         var ddaysText = this.disabledDaysText;
29887         var format = this.format;
29888
29889         var setCellClass = function(cal, cell){
29890             cell.title = "";
29891             var t = d.getTime();
29892             cell.firstChild.dateValue = t;
29893             if(t == today){
29894                 cell.className += " x-date-today";
29895                 cell.title = cal.todayText;
29896             }
29897             if(t == sel){
29898                 cell.className += " x-date-selected";
29899                 setTimeout(function(){
29900                     try{cell.firstChild.focus();}catch(e){}
29901                 }, 50);
29902             }
29903             // disabling
29904             if(t < min) {
29905                 cell.className = " x-date-disabled";
29906                 cell.title = cal.minText;
29907                 return;
29908             }
29909             if(t > max) {
29910                 cell.className = " x-date-disabled";
29911                 cell.title = cal.maxText;
29912                 return;
29913             }
29914             if(ddays){
29915                 if(ddays.indexOf(d.getDay()) != -1){
29916                     cell.title = ddaysText;
29917                     cell.className = " x-date-disabled";
29918                 }
29919             }
29920             if(ddMatch && format){
29921                 var fvalue = d.dateFormat(format);
29922                 if(ddMatch.test(fvalue)){
29923                     cell.title = ddText.replace("%0", fvalue);
29924                     cell.className = " x-date-disabled";
29925                 }
29926             }
29927         };
29928
29929         var i = 0;
29930         for(; i < startingPos; i++) {
29931             textEls[i].innerHTML = (++prevStart);
29932             d.setDate(d.getDate()+1);
29933             cells[i].className = "x-date-prevday";
29934             setCellClass(this, cells[i]);
29935         }
29936         for(; i < days; i++){
29937             intDay = i - startingPos + 1;
29938             textEls[i].innerHTML = (intDay);
29939             d.setDate(d.getDate()+1);
29940             cells[i].className = "x-date-active";
29941             setCellClass(this, cells[i]);
29942         }
29943         var extraDays = 0;
29944         for(; i < 42; i++) {
29945              textEls[i].innerHTML = (++extraDays);
29946              d.setDate(d.getDate()+1);
29947              cells[i].className = "x-date-nextday";
29948              setCellClass(this, cells[i]);
29949         }
29950
29951         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
29952         this.fireEvent('monthchange', this, date);
29953         
29954         if(!this.internalRender){
29955             var main = this.el.dom.firstChild;
29956             var w = main.offsetWidth;
29957             this.el.setWidth(w + this.el.getBorderWidth("lr"));
29958             Roo.fly(main).setWidth(w);
29959             this.internalRender = true;
29960             // opera does not respect the auto grow header center column
29961             // then, after it gets a width opera refuses to recalculate
29962             // without a second pass
29963             if(Roo.isOpera && !this.secondPass){
29964                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
29965                 this.secondPass = true;
29966                 this.update.defer(10, this, [date]);
29967             }
29968         }
29969         
29970         
29971     }
29972 });Roo.panel = {};
29973 /*
29974 * Licence: LGPL
29975 */
29976
29977 /**
29978  * @class Roo.panel.Cropbox
29979  * @extends Roo.BoxComponent
29980  * Panel Cropbox class
29981  * @cfg {String} emptyText show when image has been loaded
29982  * @cfg {String} rotateNotify show when image too small to rotate
29983  * @cfg {Number} errorTimeout default 3000
29984  * @cfg {Number} minWidth default 300
29985  * @cfg {Number} minHeight default 300
29986  * @cfg {Number} outputMaxWidth default 1200
29987  * @cfg {Number} windowSize default 300
29988  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29989  * @cfg {Boolean} isDocument (true|false) default false
29990  * @cfg {String} url action url
29991  * @cfg {String} paramName default 'imageUpload'
29992  * @cfg {String} method default POST
29993  * @cfg {Boolean} loadMask (true|false) default true
29994  * @cfg {Boolean} loadingText default 'Loading...'
29995  * 
29996  * @constructor
29997  * Create a new Cropbox
29998  * @param {Object} config The config object
29999  */
30000
30001  Roo.panel.Cropbox = function(config){
30002     Roo.panel.Cropbox.superclass.constructor.call(this, config);
30003     
30004     this.addEvents({
30005         /**
30006          * @event beforeselectfile
30007          * Fire before select file
30008          * @param {Roo.panel.Cropbox} this
30009          */
30010         "beforeselectfile" : true,
30011         /**
30012          * @event initial
30013          * Fire after initEvent
30014          * @param {Roo.panel.Cropbox} this
30015          */
30016         "initial" : true,
30017         /**
30018          * @event crop
30019          * Fire after initEvent
30020          * @param {Roo.panel.Cropbox} this
30021          * @param {String} data
30022          */
30023         "crop" : true,
30024         /**
30025          * @event prepare
30026          * Fire when preparing the file data
30027          * @param {Roo.panel.Cropbox} this
30028          * @param {Object} file
30029          */
30030         "prepare" : true,
30031         /**
30032          * @event exception
30033          * Fire when get exception
30034          * @param {Roo.panel.Cropbox} this
30035          * @param {XMLHttpRequest} xhr
30036          */
30037         "exception" : true,
30038         /**
30039          * @event beforeloadcanvas
30040          * Fire before load the canvas
30041          * @param {Roo.panel.Cropbox} this
30042          * @param {String} src
30043          */
30044         "beforeloadcanvas" : true,
30045         /**
30046          * @event trash
30047          * Fire when trash image
30048          * @param {Roo.panel.Cropbox} this
30049          */
30050         "trash" : true,
30051         /**
30052          * @event download
30053          * Fire when download the image
30054          * @param {Roo.panel.Cropbox} this
30055          */
30056         "download" : true,
30057         /**
30058          * @event footerbuttonclick
30059          * Fire when footerbuttonclick
30060          * @param {Roo.panel.Cropbox} this
30061          * @param {String} type
30062          */
30063         "footerbuttonclick" : true,
30064         /**
30065          * @event resize
30066          * Fire when resize
30067          * @param {Roo.panel.Cropbox} this
30068          */
30069         "resize" : true,
30070         /**
30071          * @event rotate
30072          * Fire when rotate the image
30073          * @param {Roo.panel.Cropbox} this
30074          * @param {String} pos
30075          */
30076         "rotate" : true,
30077         /**
30078          * @event inspect
30079          * Fire when inspect the file
30080          * @param {Roo.panel.Cropbox} this
30081          * @param {Object} file
30082          */
30083         "inspect" : true,
30084         /**
30085          * @event upload
30086          * Fire when xhr upload the file
30087          * @param {Roo.panel.Cropbox} this
30088          * @param {Object} data
30089          */
30090         "upload" : true,
30091         /**
30092          * @event arrange
30093          * Fire when arrange the file data
30094          * @param {Roo.panel.Cropbox} this
30095          * @param {Object} formData
30096          */
30097         "arrange" : true,
30098         /**
30099          * @event loadcanvas
30100          * Fire after load the canvas
30101          * @param {Roo.panel.Cropbox}
30102          * @param {Object} imgEl
30103          */
30104         "loadcanvas" : true
30105     });
30106     
30107     this.buttons = this.buttons || Roo.panel.Cropbox.footer.STANDARD;
30108 };
30109
30110 Roo.extend(Roo.panel.Cropbox, Roo.Component,  {
30111     
30112     emptyText : 'Click to upload image',
30113     rotateNotify : 'Image is too small to rotate',
30114     errorTimeout : 3000,
30115     scale : 0,
30116     baseScale : 1,
30117     rotate : 0,
30118     dragable : false,
30119     pinching : false,
30120     mouseX : 0,
30121     mouseY : 0,
30122     cropData : false,
30123     minWidth : 300,
30124     minHeight : 300,
30125     outputMaxWidth : 1200,
30126     windowSize : 300,
30127     file : false,
30128     exif : {},
30129     baseRotate : 1,
30130     cropType : 'image/jpeg',
30131     buttons : false,
30132     canvasLoaded : false,
30133     isDocument : false,
30134     method : 'POST',
30135     paramName : 'imageUpload',
30136     loadMask : true,
30137     loadingText : 'Loading...',
30138     maskEl : false,
30139     
30140     getAutoCreate : function()
30141     {
30142         var cfg = {
30143             tag : 'div',
30144             cls : 'roo-upload-cropbox',
30145             cn : [
30146                 {
30147                     tag : 'input',
30148                     cls : 'roo-upload-cropbox-selector',
30149                     type : 'file'
30150                 },
30151                 {
30152                     tag : 'div',
30153                     cls : 'roo-upload-cropbox-body',
30154                     style : 'cursor:pointer',
30155                     cn : [
30156                         {
30157                             tag : 'div',
30158                             cls : 'roo-upload-cropbox-preview'
30159                         },
30160                         {
30161                             tag : 'div',
30162                             cls : 'roo-upload-cropbox-thumb'
30163                         },
30164                         {
30165                             tag : 'div',
30166                             cls : 'roo-upload-cropbox-empty-notify',
30167                             html : this.emptyText
30168                         },
30169                         {
30170                             tag : 'div',
30171                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30172                             html : this.rotateNotify
30173                         }
30174                     ]
30175                 },
30176                 {
30177                     tag : 'div',
30178                     cls : 'roo-upload-cropbox-footer',
30179                     cn : {
30180                         tag : 'div',
30181                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30182                         cn : []
30183                     }
30184                 }
30185             ]
30186         };
30187         
30188         return cfg;
30189     },
30190     
30191     onRender : function(ct, position)
30192     {
30193         Roo.panel.Cropbox.superclass.onRender.call(this, ct, position);
30194
30195         if(this.el){
30196             if (this.el.attr('xtype')) {
30197                 this.el.attr('xtypex', this.el.attr('xtype'));
30198                 this.el.dom.removeAttribute('xtype');
30199                 
30200                 this.initEvents();
30201             }
30202         }
30203         else {
30204             var cfg = Roo.apply({},  this.getAutoCreate());
30205         
30206             cfg.id = this.id || Roo.id();
30207             
30208             if (this.cls) {
30209                 cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
30210             }
30211             
30212             if (this.style) { // fixme needs to support more complex style data.
30213                 cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
30214             }
30215             
30216             this.el = ct.createChild(cfg, position);
30217             
30218             this.initEvents();
30219         }
30220         
30221         if (this.buttons.length) {
30222             
30223             Roo.each(this.buttons, function(bb) {
30224                 
30225                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30226                 
30227                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30228                 
30229             }, this);
30230         }
30231         
30232         if(this.loadMask){
30233             this.maskEl = this.el;
30234         }
30235     },
30236     
30237     initEvents : function()
30238     {
30239         this.urlAPI = (window.createObjectURL && window) || 
30240                                 (window.URL && URL.revokeObjectURL && URL) || 
30241                                 (window.webkitURL && webkitURL);
30242                         
30243         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30244         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30245         
30246         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30247         this.selectorEl.hide();
30248         
30249         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30250         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30251         
30252         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30253         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30254         this.thumbEl.hide();
30255         
30256         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30257         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30258         
30259         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30260         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30261         this.errorEl.hide();
30262         
30263         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30264         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30265         this.footerEl.hide();
30266         
30267         this.setThumbBoxSize();
30268         
30269         this.bind();
30270         
30271         this.resize();
30272         
30273         this.fireEvent('initial', this);
30274     },
30275
30276     bind : function()
30277     {
30278         var _this = this;
30279         
30280         window.addEventListener("resize", function() { _this.resize(); } );
30281         
30282         this.bodyEl.on('click', this.beforeSelectFile, this);
30283         
30284         if(Roo.isTouch){
30285             this.bodyEl.on('touchstart', this.onTouchStart, this);
30286             this.bodyEl.on('touchmove', this.onTouchMove, this);
30287             this.bodyEl.on('touchend', this.onTouchEnd, this);
30288         }
30289         
30290         if(!Roo.isTouch){
30291             this.bodyEl.on('mousedown', this.onMouseDown, this);
30292             this.bodyEl.on('mousemove', this.onMouseMove, this);
30293             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30294             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30295             Roo.get(document).on('mouseup', this.onMouseUp, this);
30296         }
30297         
30298         this.selectorEl.on('change', this.onFileSelected, this);
30299     },
30300     
30301     reset : function()
30302     {    
30303         this.scale = 0;
30304         this.baseScale = 1;
30305         this.rotate = 0;
30306         this.baseRotate = 1;
30307         this.dragable = false;
30308         this.pinching = false;
30309         this.mouseX = 0;
30310         this.mouseY = 0;
30311         this.cropData = false;
30312         this.notifyEl.dom.innerHTML = this.emptyText;
30313         
30314         // this.selectorEl.dom.value = '';
30315         
30316     },
30317     
30318     resize : function()
30319     {
30320         if(this.fireEvent('resize', this) != false){
30321             this.setThumbBoxPosition();
30322             this.setCanvasPosition();
30323         }
30324     },
30325     
30326     onFooterButtonClick : function(e, el, o, type)
30327     {
30328         switch (type) {
30329             case 'rotate-left' :
30330                 this.onRotateLeft(e);
30331                 break;
30332             case 'rotate-right' :
30333                 this.onRotateRight(e);
30334                 break;
30335             case 'picture' :
30336                 this.beforeSelectFile(e);
30337                 break;
30338             case 'trash' :
30339                 this.trash(e);
30340                 break;
30341             case 'crop' :
30342                 this.crop(e);
30343                 break;
30344             case 'download' :
30345                 this.download(e);
30346                 break;
30347             case 'center' :
30348                 this.center(e);
30349                 break;
30350             default :
30351                 break;
30352         }
30353         
30354         this.fireEvent('footerbuttonclick', this, type);
30355     },
30356     
30357     beforeSelectFile : function(e)
30358     {
30359         e.preventDefault();
30360         
30361         if(this.fireEvent('beforeselectfile', this) != false){
30362             this.selectorEl.dom.click();
30363         }
30364     },
30365     
30366     onFileSelected : function(e)
30367     {
30368         e.preventDefault();
30369         
30370         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30371             return;
30372         }
30373         
30374         var file = this.selectorEl.dom.files[0];
30375         
30376         if(this.fireEvent('inspect', this, file) != false){
30377             this.prepare(file);
30378         }
30379         
30380     },
30381     
30382     trash : function(e)
30383     {
30384         this.fireEvent('trash', this);
30385     },
30386     
30387     download : function(e)
30388     {
30389         this.fireEvent('download', this);
30390     },
30391
30392     center : function(e)
30393     {
30394         this.setCanvasPosition();
30395     },
30396     
30397     loadCanvas : function(src)
30398     {   
30399         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30400             
30401             this.reset();
30402             
30403             this.imageEl = document.createElement('img');
30404             
30405             var _this = this;
30406             
30407             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30408             
30409             this.imageEl.src = src;
30410         }
30411     },
30412     
30413     onLoadCanvas : function()
30414     {   
30415         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30416         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30417
30418         if(this.fireEvent('loadcanvas', this, this.imageEl) != false){
30419         
30420             this.bodyEl.un('click', this.beforeSelectFile, this);
30421             
30422             this.notifyEl.hide();
30423             this.thumbEl.show();
30424             this.footerEl.show();
30425             
30426             this.baseRotateLevel();
30427             
30428             if(this.isDocument){
30429                 this.setThumbBoxSize();
30430             }
30431             
30432             this.setThumbBoxPosition();
30433             
30434             this.baseScaleLevel();
30435             
30436             this.draw();
30437             
30438             this.resize();
30439             
30440             this.canvasLoaded = true;
30441         
30442         }
30443         
30444         if(this.loadMask){
30445             this.maskEl.unmask();
30446         }
30447         
30448     },
30449     
30450     setCanvasPosition : function(center = true)
30451     {   
30452         if(!this.canvasEl){
30453             return;
30454         }
30455
30456         var newCenterLeft = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30457         var newCenterTop = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30458
30459         if(center) {
30460             this.previewEl.setLeft(newCenterLeft);
30461             this.previewEl.setTop(newCenterTop);
30462
30463             return;
30464         }
30465         
30466         var oldScaleLevel = this.baseScale * Math.pow(1.02, this.startScale);
30467         var oldCanvasWidth = Math.floor(this.imageEl.OriginWidth * oldScaleLevel);
30468         var oldCanvasHeight = Math.floor(this.imageEl.OriginHeight * oldScaleLevel);
30469
30470         var oldCenterLeft = Math.ceil((this.bodyEl.getWidth() - oldCanvasWidth) / 2);
30471         var oldCenterTop = Math.ceil((this.bodyEl.getHeight() - oldCanvasHeight) / 2);
30472
30473         var leftDiff = newCenterLeft - oldCenterLeft;
30474         var topDiff = newCenterTop - oldCenterTop;
30475
30476         var newPreviewLeft = this.previewEl.getLeft(true) + leftDiff;
30477         var newPreviewTop = this.previewEl.getTop(true) + topDiff;
30478
30479         this.previewEl.setLeft(newPreviewLeft);
30480         this.previewEl.setTop(newPreviewTop);
30481         
30482     },
30483     
30484     onMouseDown : function(e)
30485     {   
30486         e.stopEvent();
30487         
30488         this.dragable = true;
30489         this.pinching = false;
30490         
30491         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30492             this.dragable = false;
30493             return;
30494         }
30495         
30496         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30497         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30498         
30499     },
30500     
30501     onMouseMove : function(e)
30502     {   
30503         e.stopEvent();
30504         
30505         if(!this.canvasLoaded){
30506             return;
30507         }
30508         
30509         if (!this.dragable){
30510             return;
30511         }
30512
30513         var maxPaddingLeft = this.canvasEl.width / 0.9 * 0.05;
30514         var maxPaddingTop = maxPaddingLeft * this.minHeight / this.minWidth;
30515
30516         if ((this.imageEl.OriginWidth / this.imageEl.OriginHeight <= this.minWidth / this.minHeight)) {
30517             maxPaddingLeft = (this.canvasEl.height * this.minWidth / this.minHeight - this.canvasEl.width) / 2 + maxPaddingLeft;
30518         }
30519
30520         if ((this.imageEl.OriginWidth / this.imageEl.OriginHeight >= this.minWidth / this.minHeight)) {
30521             maxPaddingTop = (this.canvasEl.width * this.minHeight / this.minWidth - this.canvasEl.height) / 2 + maxPaddingTop;
30522         }
30523         
30524         var minX = Math.ceil(this.thumbEl.getLeft(true) + this.thumbEl.getWidth() - this.canvasEl.width - maxPaddingLeft);
30525         var minY = Math.ceil(this.thumbEl.getTop(true) + this.thumbEl.getHeight() - this.canvasEl.height - maxPaddingTop);
30526         
30527         var maxX = Math.ceil(this.thumbEl.getLeft(true) + maxPaddingLeft);
30528         var maxY = Math.ceil(this.thumbEl.getTop(true) +  maxPaddingTop);
30529
30530         if(minX > maxX) {
30531             var tempX = minX;
30532             minX = maxX;
30533             maxX = tempX;
30534         }
30535
30536         if(minY > maxY) {
30537             var tempY = minY;
30538             minY = maxY;
30539             maxY = tempY;
30540         }
30541
30542         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30543         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30544         
30545         x = x - this.mouseX;
30546         y = y - this.mouseY;
30547
30548         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30549         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30550         
30551         bgX = (bgX < minX) ? minX : ((bgX > maxX) ? maxX : bgX);
30552         bgY = (bgY < minY) ? minY : ((bgY > maxY) ? maxY : bgY);
30553         
30554         this.previewEl.setLeft(bgX);
30555         this.previewEl.setTop(bgY);
30556         
30557         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30558         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30559     },
30560     
30561     onMouseUp : function(e)
30562     {   
30563         e.stopEvent();
30564         
30565         this.dragable = false;
30566     },
30567     
30568     onMouseWheel : function(e)
30569     {   
30570         e.stopEvent();
30571         
30572         this.startScale = this.scale;
30573         this.scale = (e.getWheelDelta() > 0) ? (this.scale + 1) : (this.scale - 1);
30574         
30575         if(!this.zoomable()){
30576             this.scale = this.startScale;
30577             return;
30578         }
30579
30580         
30581         this.draw();
30582         
30583         return;
30584     },
30585     
30586     zoomable : function()
30587     {
30588         var minScale = this.thumbEl.getWidth() / this.minWidth;
30589         
30590         if(this.minWidth < this.minHeight){
30591             minScale = this.thumbEl.getHeight() / this.minHeight;
30592         }
30593         
30594         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30595         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30596  
30597         var maxWidth = this.imageEl.OriginWidth;
30598         var maxHeight = this.imageEl.OriginHeight;
30599
30600
30601         var newCanvasWidth = Math.floor(this.imageEl.OriginWidth * this.getScaleLevel());
30602         var newCanvasHeight = Math.floor(this.imageEl.OriginHeight * this.getScaleLevel());
30603
30604         var oldCenterLeft = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30605         var oldCenterTop = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30606
30607         var newCenterLeft = Math.ceil((this.bodyEl.getWidth() - newCanvasWidth) / 2);
30608         var newCenterTop = Math.ceil((this.bodyEl.getHeight() - newCanvasHeight) / 2);
30609
30610         var leftDiff = newCenterLeft - oldCenterLeft;
30611         var topDiff = newCenterTop - oldCenterTop;
30612
30613         var newPreviewLeft = this.previewEl.getLeft(true) + leftDiff;
30614         var newPreviewTop = this.previewEl.getTop(true) + topDiff;
30615
30616         var paddingLeft = newPreviewLeft - this.thumbEl.getLeft(true);
30617         var paddingTop = newPreviewTop - this.thumbEl.getTop(true);
30618
30619         var paddingRight = this.thumbEl.getLeft(true) + this.thumbEl.getWidth() - newCanvasWidth - newPreviewLeft;
30620         var paddingBottom = this.thumbEl.getTop(true) + this.thumbEl.getHeight() - newCanvasHeight - newPreviewTop;
30621
30622         var maxPaddingLeft = newCanvasWidth / 0.9 * 0.05;
30623         var maxPaddingTop = maxPaddingLeft * this.minHeight / this.minWidth;
30624
30625         if ((this.imageEl.OriginWidth / this.imageEl.OriginHeight <= this.minWidth / this.minHeight)) {
30626             maxPaddingLeft = (newCanvasHeight * this.minWidth / this.minHeight - newCanvasWidth) / 2 + maxPaddingLeft;
30627         }
30628
30629         if ((this.imageEl.OriginWidth / this.imageEl.OriginHeight >= this.minWidth / this.minHeight)) {
30630             maxPaddingTop = (newCanvasWidth * this.minHeight / this.minWidth - newCanvasHeight) / 2 + maxPaddingTop;
30631         }
30632         
30633         if(
30634                 this.isDocument &&
30635                 (this.rotate == 0 || this.rotate == 180) && 
30636                 (
30637                     width > this.imageEl.OriginWidth || 
30638                     height > this.imageEl.OriginHeight ||
30639                     (width < this.minWidth && height < this.minHeight)
30640                 )
30641         ){
30642             return false;
30643         }
30644         
30645         if(
30646                 this.isDocument &&
30647                 (this.rotate == 90 || this.rotate == 270) && 
30648                 (
30649                     width > this.imageEl.OriginWidth || 
30650                     height > this.imageEl.OriginHeight ||
30651                     (width < this.minHeight && height < this.minWidth)
30652                 )
30653         ){
30654             return false;
30655         }
30656         
30657         if(
30658                 !this.isDocument &&
30659                 (this.rotate == 0 || this.rotate == 180) && 
30660                 (
30661                     // for zoom out
30662                     paddingLeft > maxPaddingLeft ||
30663                     paddingRight > maxPaddingLeft ||
30664                     paddingTop > maxPaddingTop ||
30665                     paddingBottom > maxPaddingTop ||
30666                     // for zoom in
30667                     width > maxWidth ||
30668                     height > maxHeight
30669                 )
30670         ){
30671             return false;
30672         }
30673         
30674         if(
30675                 !this.isDocument &&
30676                 (this.rotate == 90 || this.rotate == 270) && 
30677                 (
30678                     width < this.minHeight || 
30679                     width > this.imageEl.OriginWidth || 
30680                     height < this.minWidth || 
30681                     height > this.imageEl.OriginHeight
30682                 )
30683         ){
30684             return false;
30685         }
30686         
30687         return true;
30688         
30689     },
30690     
30691     onRotateLeft : function(e)
30692     {   
30693         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30694             
30695             var minScale = this.thumbEl.getWidth() / this.minWidth;
30696             
30697             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30698             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30699             
30700             this.startScale = this.scale;
30701             
30702             while (this.getScaleLevel() < minScale){
30703             
30704                 this.scale = this.scale + 1;
30705                 
30706                 if(!this.zoomable()){
30707                     break;
30708                 }
30709                 
30710                 if(
30711                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30712                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30713                 ){
30714                     continue;
30715                 }
30716                 
30717                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30718
30719                 this.draw();
30720                 
30721                 return;
30722             }
30723             
30724             this.scale = this.startScale;
30725             
30726             this.onRotateFail();
30727             
30728             return false;
30729         }
30730         
30731         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30732
30733         if(this.isDocument){
30734             this.setThumbBoxSize();
30735             this.setThumbBoxPosition();
30736             this.setCanvasPosition();
30737         }
30738         
30739         this.draw();
30740         
30741         this.fireEvent('rotate', this, 'left');
30742         
30743     },
30744     
30745     onRotateRight : function(e)
30746     {
30747         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30748             
30749             var minScale = this.thumbEl.getWidth() / this.minWidth;
30750         
30751             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30752             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30753             
30754             this.startScale = this.scale;
30755             
30756             while (this.getScaleLevel() < minScale){
30757             
30758                 this.scale = this.scale + 1;
30759                 
30760                 if(!this.zoomable()){
30761                     break;
30762                 }
30763                 
30764                 if(
30765                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30766                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30767                 ){
30768                     continue;
30769                 }
30770                 
30771                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30772
30773                 this.draw();
30774                 
30775                 return;
30776             }
30777             
30778             this.scale = this.startScale;
30779             
30780             this.onRotateFail();
30781             
30782             return false;
30783         }
30784         
30785         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30786
30787         if(this.isDocument){
30788             this.setThumbBoxSize();
30789             this.setThumbBoxPosition();
30790             this.setCanvasPosition();
30791         }
30792         
30793         this.draw();
30794         
30795         this.fireEvent('rotate', this, 'right');
30796     },
30797     
30798     onRotateFail : function()
30799     {
30800         this.errorEl.show(true);
30801         
30802         var _this = this;
30803         
30804         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30805     },
30806     
30807     draw : function()
30808     {
30809         this.previewEl.dom.innerHTML = '';
30810         
30811         var canvasEl = document.createElement("canvas");
30812         
30813         var contextEl = canvasEl.getContext("2d");
30814         
30815         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30816         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30817         var center = this.imageEl.OriginWidth / 2;
30818         
30819         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30820             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30821             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30822             center = this.imageEl.OriginHeight / 2;
30823         }
30824         
30825         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30826         
30827         contextEl.translate(center, center);
30828         contextEl.rotate(this.rotate * Math.PI / 180);
30829
30830         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30831         
30832         this.canvasEl = document.createElement("canvas");
30833         
30834         this.contextEl = this.canvasEl.getContext("2d");
30835         
30836         switch (this.rotate) {
30837             case 0 :
30838                 
30839                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30840                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30841                 
30842                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30843                 
30844                 break;
30845             case 90 : 
30846                 
30847                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30848                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30849                 
30850                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30851                     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);
30852                     break;
30853                 }
30854                 
30855                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30856                 
30857                 break;
30858             case 180 :
30859                 
30860                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30861                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30862                 
30863                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30864                     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);
30865                     break;
30866                 }
30867                 
30868                 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);
30869                 
30870                 break;
30871             case 270 :
30872                 
30873                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30874                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30875         
30876                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30877                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30878                     break;
30879                 }
30880                 
30881                 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);
30882                 
30883                 break;
30884             default : 
30885                 break;
30886         }
30887         
30888         this.previewEl.appendChild(this.canvasEl);
30889         
30890         this.setCanvasPosition(false);
30891     },
30892     
30893     crop : function()
30894     {
30895         if(!this.canvasLoaded){
30896             return;
30897         }
30898         
30899         var imageCanvas = document.createElement("canvas");
30900         
30901         var imageContext = imageCanvas.getContext("2d");
30902         
30903         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30904         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30905         
30906         var center = imageCanvas.width / 2;
30907         
30908         imageContext.translate(center, center);
30909         
30910         imageContext.rotate(this.rotate * Math.PI / 180);
30911         
30912         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30913         
30914         var canvas = document.createElement("canvas");
30915         
30916         var context = canvas.getContext("2d");
30917
30918         canvas.width = this.thumbEl.getWidth() / this.getScaleLevel();
30919         
30920         canvas.height = this.thumbEl.getHeight() / this.getScaleLevel();
30921
30922         switch (this.rotate) {
30923             case 0 :
30924                 
30925                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30926                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30927                 
30928                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30929                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30930                 
30931                 var sx = this.thumbEl.getLeft(true) - this.previewEl.getLeft(true);
30932                 var sy = this.thumbEl.getTop(true) - this.previewEl.getTop(true);
30933
30934                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30935                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30936
30937                 if(canvas.width > this.outputMaxWidth) {
30938                     var scale = this.outputMaxWidth / canvas.width;
30939                     canvas.width = canvas.width * scale;
30940                     canvas.height = canvas.height * scale;
30941                     context.scale(scale, scale);
30942                 }
30943
30944                 context.fillStyle = 'white';
30945                 context.fillRect(0, 0, this.thumbEl.getWidth() / this.getScaleLevel(), this.thumbEl.getHeight() / this.getScaleLevel());
30946
30947
30948                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30949                 
30950                 break;
30951             case 90 : 
30952                 
30953                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30954                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30955                 
30956                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30957                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30958                 
30959                 var targetWidth = this.minWidth - 2 * x;
30960                 var targetHeight = this.minHeight - 2 * y;
30961                 
30962                 var scale = 1;
30963                 
30964                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30965                     scale = targetWidth / width;
30966                 }
30967                 
30968                 if(x > 0 && y == 0){
30969                     scale = targetHeight / height;
30970                 }
30971                 
30972                 if(x > 0 && y > 0){
30973                     scale = targetWidth / width;
30974                     
30975                     if(width < height){
30976                         scale = targetHeight / height;
30977                     }
30978                 }
30979                 
30980                 context.scale(scale, scale);
30981                 
30982                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30983                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30984
30985                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30986                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30987                 
30988                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30989                 
30990                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30991                 
30992                 break;
30993             case 180 :
30994                 
30995                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30996                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30997                 
30998                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30999                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31000                 
31001                 var targetWidth = this.minWidth - 2 * x;
31002                 var targetHeight = this.minHeight - 2 * y;
31003                 
31004                 var scale = 1;
31005                 
31006                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31007                     scale = targetWidth / width;
31008                 }
31009                 
31010                 if(x > 0 && y == 0){
31011                     scale = targetHeight / height;
31012                 }
31013                 
31014                 if(x > 0 && y > 0){
31015                     scale = targetWidth / width;
31016                     
31017                     if(width < height){
31018                         scale = targetHeight / height;
31019                     }
31020                 }
31021                 
31022                 context.scale(scale, scale);
31023                 
31024                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31025                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31026
31027                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31028                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31029
31030                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31031                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31032                 
31033                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31034                 
31035                 break;
31036             case 270 :
31037                 
31038                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31039                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31040                 
31041                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31042                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31043                 
31044                 var targetWidth = this.minWidth - 2 * x;
31045                 var targetHeight = this.minHeight - 2 * y;
31046                 
31047                 var scale = 1;
31048                 
31049                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31050                     scale = targetWidth / width;
31051                 }
31052                 
31053                 if(x > 0 && y == 0){
31054                     scale = targetHeight / height;
31055                 }
31056                 
31057                 if(x > 0 && y > 0){
31058                     scale = targetWidth / width;
31059                     
31060                     if(width < height){
31061                         scale = targetHeight / height;
31062                     }
31063                 }
31064                 
31065                 context.scale(scale, scale);
31066                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31067                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31068
31069                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31070                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31071                 
31072                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31073                 
31074                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31075                 
31076                 break;
31077             default : 
31078                 break;
31079         }
31080         
31081         this.cropData = canvas.toDataURL(this.cropType);
31082         
31083         if(this.fireEvent('crop', this, this.cropData) !== false){
31084             this.process(this.file, this.cropData);
31085         }
31086         
31087         return;
31088         
31089     },
31090     
31091     setThumbBoxSize : function()
31092     {
31093         var width, height;
31094         
31095         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31096             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31097             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31098             
31099             this.minWidth = width;
31100             this.minHeight = height;
31101             
31102             if(this.rotate == 90 || this.rotate == 270){
31103                 this.minWidth = height;
31104                 this.minHeight = width;
31105             }
31106         }
31107         
31108         height = this.windowSize;
31109         width = Math.ceil(this.minWidth * height / this.minHeight);
31110         
31111         if(this.minWidth > this.minHeight){
31112             width = this.windowSize;
31113             height = Math.ceil(this.minHeight * width / this.minWidth);
31114         }
31115         
31116         this.thumbEl.setStyle({
31117             width : width + 'px',
31118             height : height + 'px'
31119         });
31120
31121         return;
31122             
31123     },
31124     
31125     setThumbBoxPosition : function()
31126     {
31127         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31128         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31129         
31130         this.thumbEl.setLeft(x);
31131         this.thumbEl.setTop(y);
31132         
31133     },
31134     
31135     baseRotateLevel : function()
31136     {
31137         this.baseRotate = 1;
31138         
31139         if(
31140                 typeof(this.exif) != 'undefined' &&
31141                 typeof(this.exif[Roo.panel.Cropbox['tags']['Orientation']]) != 'undefined' &&
31142                 [1, 3, 6, 8].indexOf(this.exif[Roo.panel.Cropbox['tags']['Orientation']]) != -1
31143         ){
31144             this.baseRotate = this.exif[Roo.panel.Cropbox['tags']['Orientation']];
31145         }
31146         
31147         this.rotate = Roo.panel.Cropbox['Orientation'][this.baseRotate];
31148         
31149     },
31150     
31151     baseScaleLevel : function()
31152     {
31153         var width, height;
31154         
31155         if(this.isDocument){
31156             
31157             if(this.baseRotate == 6 || this.baseRotate == 8){
31158             
31159                 height = this.thumbEl.getHeight();
31160                 this.baseScale = height / this.imageEl.OriginWidth;
31161
31162                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31163                     width = this.thumbEl.getWidth();
31164                     this.baseScale = width / this.imageEl.OriginHeight;
31165                 }
31166
31167                 return;
31168             }
31169
31170             height = this.thumbEl.getHeight();
31171             this.baseScale = height / this.imageEl.OriginHeight;
31172
31173             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31174                 width = this.thumbEl.getWidth();
31175                 this.baseScale = width / this.imageEl.OriginWidth;
31176             }
31177
31178             return;
31179         }
31180         
31181         if(this.baseRotate == 6 || this.baseRotate == 8){
31182             
31183             width = this.thumbEl.getHeight();
31184             this.baseScale = width / this.imageEl.OriginHeight;
31185             
31186             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31187                 height = this.thumbEl.getWidth();
31188                 this.baseScale = height / this.imageEl.OriginHeight;
31189             }
31190             
31191             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31192                 height = this.thumbEl.getWidth();
31193                 this.baseScale = height / this.imageEl.OriginHeight;
31194                 
31195                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31196                     width = this.thumbEl.getHeight();
31197                     this.baseScale = width / this.imageEl.OriginWidth;
31198                 }
31199             }
31200             
31201             return;
31202         }
31203         
31204         width = this.thumbEl.getWidth();
31205         this.baseScale = width / this.imageEl.OriginWidth;
31206         
31207         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31208             height = this.thumbEl.getHeight();
31209             this.baseScale = height / this.imageEl.OriginHeight;
31210         }
31211         
31212         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31213             
31214             height = this.thumbEl.getHeight();
31215             this.baseScale = height / this.imageEl.OriginHeight;
31216             
31217             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31218                 width = this.thumbEl.getWidth();
31219                 this.baseScale = width / this.imageEl.OriginWidth;
31220             }
31221             
31222         }
31223
31224         if(this.imageEl.OriginWidth < this.minWidth || this.imageEl.OriginHeight < this.minHeight) {
31225             this.baseScale = width / this.minWidth;
31226         }
31227
31228         return;
31229     },
31230     
31231     getScaleLevel : function()
31232     {
31233         return this.baseScale * Math.pow(1.02, this.scale);
31234     },
31235     
31236     onTouchStart : function(e)
31237     {
31238         if(!this.canvasLoaded){
31239             this.beforeSelectFile(e);
31240             return;
31241         }
31242         
31243         var touches = e.browserEvent.touches;
31244         
31245         if(!touches){
31246             return;
31247         }
31248         
31249         if(touches.length == 1){
31250             this.onMouseDown(e);
31251             return;
31252         }
31253         
31254         if(touches.length != 2){
31255             return;
31256         }
31257         
31258         var coords = [];
31259         
31260         for(var i = 0, finger; finger = touches[i]; i++){
31261             coords.push(finger.pageX, finger.pageY);
31262         }
31263         
31264         var x = Math.pow(coords[0] - coords[2], 2);
31265         var y = Math.pow(coords[1] - coords[3], 2);
31266         
31267         this.startDistance = Math.sqrt(x + y);
31268         
31269         this.startScale = this.scale;
31270         
31271         this.pinching = true;
31272         this.dragable = false;
31273         
31274     },
31275     
31276     onTouchMove : function(e)
31277     {
31278         if(!this.pinching && !this.dragable){
31279             return;
31280         }
31281         
31282         var touches = e.browserEvent.touches;
31283         
31284         if(!touches){
31285             return;
31286         }
31287         
31288         if(this.dragable){
31289             this.onMouseMove(e);
31290             return;
31291         }
31292         
31293         var coords = [];
31294         
31295         for(var i = 0, finger; finger = touches[i]; i++){
31296             coords.push(finger.pageX, finger.pageY);
31297         }
31298         
31299         var x = Math.pow(coords[0] - coords[2], 2);
31300         var y = Math.pow(coords[1] - coords[3], 2);
31301         
31302         this.endDistance = Math.sqrt(x + y);
31303         
31304         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31305         
31306         if(!this.zoomable()){
31307             this.scale = this.startScale;
31308             return;
31309         }
31310         
31311         this.draw();
31312         
31313     },
31314     
31315     onTouchEnd : function(e)
31316     {
31317         this.pinching = false;
31318         this.dragable = false;
31319         
31320     },
31321     
31322     process : function(file, crop)
31323     {
31324         if(this.loadMask){
31325             this.maskEl.mask(this.loadingText);
31326         }
31327         
31328         this.xhr = new XMLHttpRequest();
31329         
31330         file.xhr = this.xhr;
31331
31332         this.xhr.open(this.method, this.url, true);
31333         
31334         var headers = {
31335             "Accept": "application/json",
31336             "Cache-Control": "no-cache",
31337             "X-Requested-With": "XMLHttpRequest"
31338         };
31339         
31340         for (var headerName in headers) {
31341             var headerValue = headers[headerName];
31342             if (headerValue) {
31343                 this.xhr.setRequestHeader(headerName, headerValue);
31344             }
31345         }
31346         
31347         var _this = this;
31348         
31349         this.xhr.onload = function()
31350         {
31351             _this.xhrOnLoad(_this.xhr);
31352         }
31353         
31354         this.xhr.onerror = function()
31355         {
31356             _this.xhrOnError(_this.xhr);
31357         }
31358         
31359         var formData = new FormData();
31360
31361         formData.append('returnHTML', 'NO');
31362
31363         if(crop){
31364             formData.append('crop', crop);
31365             var blobBin = atob(crop.split(',')[1]);
31366             var array = [];
31367             for(var i = 0; i < blobBin.length; i++) {
31368                 array.push(blobBin.charCodeAt(i));
31369             }
31370             var croppedFile =new Blob([new Uint8Array(array)], {type: this.cropType});
31371             formData.append(this.paramName, croppedFile, file.name);
31372         }
31373         
31374         if(typeof(file.filename) != 'undefined'){
31375             formData.append('filename', file.filename);
31376         }
31377         
31378         if(typeof(file.mimetype) != 'undefined'){
31379             formData.append('mimetype', file.mimetype);
31380         }
31381
31382         if(this.fireEvent('arrange', this, formData) != false){
31383             this.xhr.send(formData);
31384         };
31385     },
31386     
31387     xhrOnLoad : function(xhr)
31388     {
31389         if(this.loadMask){
31390             this.maskEl.unmask();
31391         }
31392         
31393         if (xhr.readyState !== 4) {
31394             this.fireEvent('exception', this, xhr);
31395             return;
31396         }
31397
31398         var response = Roo.decode(xhr.responseText);
31399         
31400         if(!response.success){
31401             this.fireEvent('exception', this, xhr);
31402             return;
31403         }
31404         
31405         var response = Roo.decode(xhr.responseText);
31406         
31407         this.fireEvent('upload', this, response);
31408         
31409     },
31410     
31411     xhrOnError : function()
31412     {
31413         if(this.loadMask){
31414             this.maskEl.unmask();
31415         }
31416         
31417         Roo.log('xhr on error');
31418         
31419         var response = Roo.decode(xhr.responseText);
31420           
31421         Roo.log(response);
31422         
31423     },
31424     
31425     prepare : function(file)
31426     {   
31427         if(this.loadMask){
31428             this.maskEl.mask(this.loadingText);
31429         }
31430         
31431         this.file = false;
31432         this.exif = {};
31433         
31434         if(typeof(file) === 'string'){
31435             this.loadCanvas(file);
31436             return;
31437         }
31438         
31439         if(!file || !this.urlAPI){
31440             return;
31441         }
31442         
31443         this.file = file;
31444         if(typeof(file.type) != 'undefined' && file.type.length != 0) {
31445             this.cropType = file.type;
31446         }
31447         
31448         var _this = this;
31449         
31450         if(this.fireEvent('prepare', this, this.file) != false){
31451             
31452             var reader = new FileReader();
31453             
31454             reader.onload = function (e) {
31455                 if (e.target.error) {
31456                     Roo.log(e.target.error);
31457                     return;
31458                 }
31459                 
31460                 var buffer = e.target.result,
31461                     dataView = new DataView(buffer),
31462                     offset = 2,
31463                     maxOffset = dataView.byteLength - 4,
31464                     markerBytes,
31465                     markerLength;
31466                 
31467                 if (dataView.getUint16(0) === 0xffd8) {
31468                     while (offset < maxOffset) {
31469                         markerBytes = dataView.getUint16(offset);
31470                         
31471                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31472                             markerLength = dataView.getUint16(offset + 2) + 2;
31473                             if (offset + markerLength > dataView.byteLength) {
31474                                 Roo.log('Invalid meta data: Invalid segment size.');
31475                                 break;
31476                             }
31477                             
31478                             if(markerBytes == 0xffe1){
31479                                 _this.parseExifData(
31480                                     dataView,
31481                                     offset,
31482                                     markerLength
31483                                 );
31484                             }
31485                             
31486                             offset += markerLength;
31487                             
31488                             continue;
31489                         }
31490                         
31491                         break;
31492                     }
31493                     
31494                 }
31495                 
31496                 var url = _this.urlAPI.createObjectURL(_this.file);
31497                 
31498                 _this.loadCanvas(url);
31499                 
31500                 return;
31501             }
31502             
31503             reader.readAsArrayBuffer(this.file);
31504             
31505         }
31506         
31507     },
31508     
31509     parseExifData : function(dataView, offset, length)
31510     {
31511         var tiffOffset = offset + 10,
31512             littleEndian,
31513             dirOffset;
31514     
31515         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31516             // No Exif data, might be XMP data instead
31517             return;
31518         }
31519         
31520         // Check for the ASCII code for "Exif" (0x45786966):
31521         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31522             // No Exif data, might be XMP data instead
31523             return;
31524         }
31525         if (tiffOffset + 8 > dataView.byteLength) {
31526             Roo.log('Invalid Exif data: Invalid segment size.');
31527             return;
31528         }
31529         // Check for the two null bytes:
31530         if (dataView.getUint16(offset + 8) !== 0x0000) {
31531             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31532             return;
31533         }
31534         // Check the byte alignment:
31535         switch (dataView.getUint16(tiffOffset)) {
31536         case 0x4949:
31537             littleEndian = true;
31538             break;
31539         case 0x4D4D:
31540             littleEndian = false;
31541             break;
31542         default:
31543             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31544             return;
31545         }
31546         // Check for the TIFF tag marker (0x002A):
31547         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31548             Roo.log('Invalid Exif data: Missing TIFF marker.');
31549             return;
31550         }
31551         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31552         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31553         
31554         this.parseExifTags(
31555             dataView,
31556             tiffOffset,
31557             tiffOffset + dirOffset,
31558             littleEndian
31559         );
31560     },
31561     
31562     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31563     {
31564         var tagsNumber,
31565             dirEndOffset,
31566             i;
31567         if (dirOffset + 6 > dataView.byteLength) {
31568             Roo.log('Invalid Exif data: Invalid directory offset.');
31569             return;
31570         }
31571         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31572         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31573         if (dirEndOffset + 4 > dataView.byteLength) {
31574             Roo.log('Invalid Exif data: Invalid directory size.');
31575             return;
31576         }
31577         for (i = 0; i < tagsNumber; i += 1) {
31578             this.parseExifTag(
31579                 dataView,
31580                 tiffOffset,
31581                 dirOffset + 2 + 12 * i, // tag offset
31582                 littleEndian
31583             );
31584         }
31585         // Return the offset to the next directory:
31586         return dataView.getUint32(dirEndOffset, littleEndian);
31587     },
31588     
31589     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31590     {
31591         var tag = dataView.getUint16(offset, littleEndian);
31592         
31593         this.exif[tag] = this.getExifValue(
31594             dataView,
31595             tiffOffset,
31596             offset,
31597             dataView.getUint16(offset + 2, littleEndian), // tag type
31598             dataView.getUint32(offset + 4, littleEndian), // tag length
31599             littleEndian
31600         );
31601     },
31602     
31603     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31604     {
31605         var tagType = Roo.panel.Cropbox.exifTagTypes[type],
31606             tagSize,
31607             dataOffset,
31608             values,
31609             i,
31610             str,
31611             c;
31612     
31613         if (!tagType) {
31614             Roo.log('Invalid Exif data: Invalid tag type.');
31615             return;
31616         }
31617         
31618         tagSize = tagType.size * length;
31619         // Determine if the value is contained in the dataOffset bytes,
31620         // or if the value at the dataOffset is a pointer to the actual data:
31621         dataOffset = tagSize > 4 ?
31622                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31623         if (dataOffset + tagSize > dataView.byteLength) {
31624             Roo.log('Invalid Exif data: Invalid data offset.');
31625             return;
31626         }
31627         if (length === 1) {
31628             return tagType.getValue(dataView, dataOffset, littleEndian);
31629         }
31630         values = [];
31631         for (i = 0; i < length; i += 1) {
31632             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31633         }
31634         
31635         if (tagType.ascii) {
31636             str = '';
31637             // Concatenate the chars:
31638             for (i = 0; i < values.length; i += 1) {
31639                 c = values[i];
31640                 // Ignore the terminating NULL byte(s):
31641                 if (c === '\u0000') {
31642                     break;
31643                 }
31644                 str += c;
31645             }
31646             return str;
31647         }
31648         return values;
31649     }
31650     
31651 });
31652
31653 Roo.apply(Roo.panel.Cropbox, {
31654     tags : {
31655         'Orientation': 0x0112
31656     },
31657     
31658     Orientation: {
31659             1: 0, //'top-left',
31660 //            2: 'top-right',
31661             3: 180, //'bottom-right',
31662 //            4: 'bottom-left',
31663 //            5: 'left-top',
31664             6: 90, //'right-top',
31665 //            7: 'right-bottom',
31666             8: 270 //'left-bottom'
31667     },
31668     
31669     exifTagTypes : {
31670         // byte, 8-bit unsigned int:
31671         1: {
31672             getValue: function (dataView, dataOffset) {
31673                 return dataView.getUint8(dataOffset);
31674             },
31675             size: 1
31676         },
31677         // ascii, 8-bit byte:
31678         2: {
31679             getValue: function (dataView, dataOffset) {
31680                 return String.fromCharCode(dataView.getUint8(dataOffset));
31681             },
31682             size: 1,
31683             ascii: true
31684         },
31685         // short, 16 bit int:
31686         3: {
31687             getValue: function (dataView, dataOffset, littleEndian) {
31688                 return dataView.getUint16(dataOffset, littleEndian);
31689             },
31690             size: 2
31691         },
31692         // long, 32 bit int:
31693         4: {
31694             getValue: function (dataView, dataOffset, littleEndian) {
31695                 return dataView.getUint32(dataOffset, littleEndian);
31696             },
31697             size: 4
31698         },
31699         // rational = two long values, first is numerator, second is denominator:
31700         5: {
31701             getValue: function (dataView, dataOffset, littleEndian) {
31702                 return dataView.getUint32(dataOffset, littleEndian) /
31703                     dataView.getUint32(dataOffset + 4, littleEndian);
31704             },
31705             size: 8
31706         },
31707         // slong, 32 bit signed int:
31708         9: {
31709             getValue: function (dataView, dataOffset, littleEndian) {
31710                 return dataView.getInt32(dataOffset, littleEndian);
31711             },
31712             size: 4
31713         },
31714         // srational, two slongs, first is numerator, second is denominator:
31715         10: {
31716             getValue: function (dataView, dataOffset, littleEndian) {
31717                 return dataView.getInt32(dataOffset, littleEndian) /
31718                     dataView.getInt32(dataOffset + 4, littleEndian);
31719             },
31720             size: 8
31721         }
31722     },
31723     
31724     footer : {
31725         STANDARD : [
31726             {
31727                 tag : 'div',
31728                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31729                 action : 'rotate-left',
31730                 cn : [
31731                     {
31732                         tag : 'button',
31733                         cls : 'btn btn-default',
31734                         html : '<i class="fa fa-undo"></i>'
31735                     }
31736                 ]
31737             },
31738             {
31739                 tag : 'div',
31740                 cls : 'btn-group roo-upload-cropbox-picture',
31741                 action : 'picture',
31742                 cn : [
31743                     {
31744                         tag : 'button',
31745                         cls : 'btn btn-default',
31746                         html : '<i class="fa fa-picture-o"></i>'
31747                     }
31748                 ]
31749             },
31750             {
31751                 tag : 'div',
31752                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31753                 action : 'rotate-right',
31754                 cn : [
31755                     {
31756                         tag : 'button',
31757                         cls : 'btn btn-default',
31758                         html : '<i class="fa fa-repeat"></i>'
31759                     }
31760                 ]
31761             }
31762         ],
31763         DOCUMENT : [
31764             {
31765                 tag : 'div',
31766                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31767                 action : 'rotate-left',
31768                 cn : [
31769                     {
31770                         tag : 'button',
31771                         cls : 'btn btn-default',
31772                         html : '<i class="fa fa-undo"></i>'
31773                     }
31774                 ]
31775             },
31776             {
31777                 tag : 'div',
31778                 cls : 'btn-group roo-upload-cropbox-download',
31779                 action : 'download',
31780                 cn : [
31781                     {
31782                         tag : 'button',
31783                         cls : 'btn btn-default',
31784                         html : '<i class="fa fa-download"></i>'
31785                     }
31786                 ]
31787             },
31788             {
31789                 tag : 'div',
31790                 cls : 'btn-group roo-upload-cropbox-crop',
31791                 action : 'crop',
31792                 cn : [
31793                     {
31794                         tag : 'button',
31795                         cls : 'btn btn-default',
31796                         html : '<i class="fa fa-crop"></i>'
31797                     }
31798                 ]
31799             },
31800             {
31801                 tag : 'div',
31802                 cls : 'btn-group roo-upload-cropbox-trash',
31803                 action : 'trash',
31804                 cn : [
31805                     {
31806                         tag : 'button',
31807                         cls : 'btn btn-default',
31808                         html : '<i class="fa fa-trash"></i>'
31809                     }
31810                 ]
31811             },
31812             {
31813                 tag : 'div',
31814                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31815                 action : 'rotate-right',
31816                 cn : [
31817                     {
31818                         tag : 'button',
31819                         cls : 'btn btn-default',
31820                         html : '<i class="fa fa-repeat"></i>'
31821                     }
31822                 ]
31823             }
31824         ],
31825         ROTATOR : [
31826             {
31827                 tag : 'div',
31828                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31829                 action : 'rotate-left',
31830                 cn : [
31831                     {
31832                         tag : 'button',
31833                         cls : 'btn btn-default',
31834                         html : '<i class="fa fa-undo"></i>'
31835                     }
31836                 ]
31837             },
31838             {
31839                 tag : 'div',
31840                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31841                 action : 'rotate-right',
31842                 cn : [
31843                     {
31844                         tag : 'button',
31845                         cls : 'btn btn-default',
31846                         html : '<i class="fa fa-repeat"></i>'
31847                     }
31848                 ]
31849             }
31850         ],
31851         CENTER : [
31852             {
31853                 tag : 'div',
31854                 cls : 'btn-group roo-upload-cropbox-center',
31855                 action : 'center',
31856                 cn : [
31857                     {
31858                         tag : 'button',
31859                         cls : 'btn btn-default',
31860                         html : 'CENTER'
31861                     }
31862                 ]
31863             }
31864         ]
31865     }
31866 });
31867         /*
31868  * Based on:
31869  * Ext JS Library 1.1.1
31870  * Copyright(c) 2006-2007, Ext JS, LLC.
31871  *
31872  * Originally Released Under LGPL - original licence link has changed is not relivant.
31873  *
31874  * Fork - LGPL
31875  * <script type="text/javascript">
31876  */
31877 /**
31878  * @class Roo.panel.Tab
31879  * @extends Roo.util.Observable
31880  * A lightweight tab container.
31881  * <br><br>
31882  * Usage:
31883  * <pre><code>
31884 // basic tabs 1, built from existing content
31885 var tabs = new Roo.panel.Tab("tabs1");
31886 tabs.addTab("script", "View Script");
31887 tabs.addTab("markup", "View Markup");
31888 tabs.activate("script");
31889
31890 // more advanced tabs, built from javascript
31891 var jtabs = new Roo.panel.Tab("jtabs");
31892 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
31893
31894 // set up the UpdateManager
31895 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
31896 var updater = tab2.getUpdateManager();
31897 updater.setDefaultUrl("ajax1.htm");
31898 tab2.on('activate', updater.refresh, updater, true);
31899
31900 // Use setUrl for Ajax loading
31901 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
31902 tab3.setUrl("ajax2.htm", null, true);
31903
31904 // Disabled tab
31905 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
31906 tab4.disable();
31907
31908 jtabs.activate("jtabs-1");
31909  * </code></pre>
31910  * @constructor
31911  * Create a new TabPanel.
31912  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
31913  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
31914  */
31915 Roo.panel.Tab = function(container, config){
31916     /**
31917     * The container element for this TabPanel.
31918     * @type Roo.Element
31919     */
31920     this.el = Roo.get(container, true);
31921     if(config){
31922         if(typeof config == "boolean"){
31923             this.tabPosition = config ? "bottom" : "top";
31924         }else{
31925             Roo.apply(this, config);
31926         }
31927     }
31928     if(this.tabPosition == "bottom"){
31929         this.bodyEl = Roo.get(this.createBody(this.el.dom));
31930         this.el.addClass("x-tabs-bottom");
31931     }
31932     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
31933     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
31934     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
31935     if(Roo.isIE){
31936         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
31937     }
31938     if(this.tabPosition != "bottom"){
31939         /** The body element that contains {@link Roo.panel.TabItem} bodies. +
31940          * @type Roo.Element
31941          */
31942         this.bodyEl = Roo.get(this.createBody(this.el.dom));
31943         this.el.addClass("x-tabs-top");
31944     }
31945     this.items = [];
31946
31947     this.bodyEl.setStyle("position", "relative");
31948
31949     this.active = null;
31950     this.activateDelegate = this.activate.createDelegate(this);
31951
31952     this.addEvents({
31953         /**
31954          * @event tabchange
31955          * Fires when the active tab changes
31956          * @param {Roo.panel.Tab} this
31957          * @param {Roo.panel.TabItem} activePanel The new active tab
31958          */
31959         "tabchange": true,
31960         /**
31961          * @event beforetabchange
31962          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
31963          * @param {Roo.panel.Tab} this
31964          * @param {Object} e Set cancel to true on this object to cancel the tab change
31965          * @param {Roo.panel.TabItem} tab The tab being changed to
31966          */
31967         "beforetabchange" : true
31968     });
31969
31970     Roo.EventManager.onWindowResize(this.onResize, this);
31971     this.cpad = this.el.getPadding("lr");
31972     this.hiddenCount = 0;
31973
31974
31975     // toolbar on the tabbar support...
31976     if (this.toolbar) {
31977         var tcfg = this.toolbar;
31978         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
31979         this.toolbar = new Roo.Toolbar(tcfg);
31980         if (Roo.isSafari) {
31981             var tbl = tcfg.container.child('table', true);
31982             tbl.setAttribute('width', '100%');
31983         }
31984         
31985     }
31986    
31987
31988
31989     Roo.panel.Tab.superclass.constructor.call(this);
31990 };
31991
31992 Roo.extend(Roo.panel.Tab, Roo.util.Observable, {
31993     /*
31994      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
31995      */
31996     tabPosition : "top",
31997     /*
31998      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
31999      */
32000     currentTabWidth : 0,
32001     /*
32002      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
32003      */
32004     minTabWidth : 40,
32005     /*
32006      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
32007      */
32008     maxTabWidth : 250,
32009     /*
32010      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
32011      */
32012     preferredTabWidth : 175,
32013     /*
32014      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
32015      */
32016     resizeTabs : false,
32017     /*
32018      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
32019      */
32020     monitorResize : true,
32021     /*
32022      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
32023      */
32024     toolbar : false,
32025
32026     /**
32027      * Creates a new {@link Roo.panel.TabItem} by looking for an existing element with the provided id -- if it's not found it creates one.
32028      * @param {String} id The id of the div to use <b>or create</b>
32029      * @param {String} text The text for the tab
32030      * @param {String} content (optional) Content to put in the TabPanelItem body
32031      * @param {Boolean} closable (optional) True to create a close icon on the tab
32032      * @return {Roo.panel.TabItem} The created TabPanelItem
32033      */
32034     addTab : function(id, text, content, closable){
32035         var item = new Roo.panel.TabItem(this, id, text, closable);
32036         this.addTabItem(item);
32037         if(content){
32038             item.setContent(content);
32039         }
32040         return item;
32041     },
32042
32043     /**
32044      * Returns the {@link Roo.panel.TabItem} with the specified id/index
32045      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
32046      * @return {Roo.panel.TabItem}
32047      */
32048     getTab : function(id){
32049         return this.items[id];
32050     },
32051
32052     /**
32053      * Hides the {@link Roo.panel.TabItem} with the specified id/index
32054      * @param {String/Number} id The id or index of the TabPanelItem to hide.
32055      */
32056     hideTab : function(id){
32057         var t = this.items[id];
32058         if(!t.isHidden()){
32059            t.setHidden(true);
32060            this.hiddenCount++;
32061            this.autoSizeTabs();
32062         }
32063     },
32064
32065     /**
32066      * "Unhides" the {@link Roo.panel.TabItem} with the specified id/index.
32067      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
32068      */
32069     unhideTab : function(id){
32070         var t = this.items[id];
32071         if(t.isHidden()){
32072            t.setHidden(false);
32073            this.hiddenCount--;
32074            this.autoSizeTabs();
32075         }
32076     },
32077
32078     /**
32079      * Adds an existing {@link Roo.panel.TabItem}.
32080      * @param {Roo.panel.TabItem} item The TabPanelItem to add
32081      */
32082     addTabItem : function(item){
32083         this.items[item.id] = item;
32084         this.items.push(item);
32085         if(this.resizeTabs){
32086            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
32087            this.autoSizeTabs();
32088         }else{
32089             item.autoSize();
32090         }
32091     },
32092
32093     /**
32094      * Removes a {@link Roo.panel.TabItem}.
32095      * @param {String/Number} id The id or index of the TabPanelItem to remove.
32096      */
32097     removeTab : function(id){
32098         var items = this.items;
32099         var tab = items[id];
32100         if(!tab) { return; }
32101         var index = items.indexOf(tab);
32102         if(this.active == tab && items.length > 1){
32103             var newTab = this.getNextAvailable(index);
32104             if(newTab) {
32105                 newTab.activate();
32106             }
32107         }
32108         this.stripEl.dom.removeChild(tab.pnode.dom);
32109         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
32110             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
32111         }
32112         items.splice(index, 1);
32113         delete this.items[tab.id];
32114         tab.fireEvent("close", tab);
32115         tab.purgeListeners();
32116         this.autoSizeTabs();
32117     },
32118
32119     getNextAvailable : function(start){
32120         var items = this.items;
32121         var index = start;
32122         // look for a next tab that will slide over to
32123         // replace the one being removed
32124         while(index < items.length){
32125             var item = items[++index];
32126             if(item && !item.isHidden()){
32127                 return item;
32128             }
32129         }
32130         // if one isn't found select the previous tab (on the left)
32131         index = start;
32132         while(index >= 0){
32133             var item = items[--index];
32134             if(item && !item.isHidden()){
32135                 return item;
32136             }
32137         }
32138         return null;
32139     },
32140
32141     /**
32142      * Disables a {@link Roo.panel.TabItem}. It cannot be the active tab, if it is this call is ignored.
32143      * @param {String/Number} id The id or index of the TabPanelItem to disable.
32144      */
32145     disableTab : function(id){
32146         var tab = this.items[id];
32147         if(tab && this.active != tab){
32148             tab.disable();
32149         }
32150     },
32151
32152     /**
32153      * Enables a {@link Roo.panel.TabItem} that is disabled.
32154      * @param {String/Number} id The id or index of the TabPanelItem to enable.
32155      */
32156     enableTab : function(id){
32157         var tab = this.items[id];
32158         tab.enable();
32159     },
32160
32161     /**
32162      * Activates a {@link Roo.panel.TabItem}. The currently active one will be deactivated.
32163      * @param {String/Number} id The id or index of the TabPanelItem to activate.
32164      * @return {Roo.panel.TabItem} The TabPanelItem.
32165      */
32166     activate : function(id){
32167         var tab = this.items[id];
32168         if(!tab){
32169             return null;
32170         }
32171         if(tab == this.active || tab.disabled){
32172             return tab;
32173         }
32174         var e = {};
32175         this.fireEvent("beforetabchange", this, e, tab);
32176         if(e.cancel !== true && !tab.disabled){
32177             if(this.active){
32178                 this.active.hide();
32179             }
32180             this.active = this.items[id];
32181             this.active.show();
32182             this.fireEvent("tabchange", this, this.active);
32183         }
32184         return tab;
32185     },
32186
32187     /**
32188      * Gets the active {@link Roo.panel.TabItem}.
32189      * @return {Roo.panel.TabItem} The active TabPanelItem or null if none are active.
32190      */
32191     getActiveTab : function(){
32192         return this.active;
32193     },
32194
32195     /**
32196      * Updates the tab body element to fit the height of the container element
32197      * for overflow scrolling
32198      * @param {Number} targetHeight (optional) Override the starting height from the elements height
32199      */
32200     syncHeight : function(targetHeight){
32201         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32202         var bm = this.bodyEl.getMargins();
32203         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
32204         this.bodyEl.setHeight(newHeight);
32205         return newHeight;
32206     },
32207
32208     onResize : function(){
32209         if(this.monitorResize){
32210             this.autoSizeTabs();
32211         }
32212     },
32213
32214     /**
32215      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
32216      */
32217     beginUpdate : function(){
32218         this.updating = true;
32219     },
32220
32221     /**
32222      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
32223      */
32224     endUpdate : function(){
32225         this.updating = false;
32226         this.autoSizeTabs();
32227     },
32228
32229     /**
32230      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
32231      */
32232     autoSizeTabs : function(){
32233         var count = this.items.length;
32234         var vcount = count - this.hiddenCount;
32235         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
32236             return;
32237         }
32238         var w = Math.max(this.el.getWidth() - this.cpad, 10);
32239         var availWidth = Math.floor(w / vcount);
32240         var b = this.stripBody;
32241         if(b.getWidth() > w){
32242             var tabs = this.items;
32243             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
32244             if(availWidth < this.minTabWidth){
32245                 /*if(!this.sleft){    // incomplete scrolling code
32246                     this.createScrollButtons();
32247                 }
32248                 this.showScroll();
32249                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
32250             }
32251         }else{
32252             if(this.currentTabWidth < this.preferredTabWidth){
32253                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
32254             }
32255         }
32256     },
32257
32258     /**
32259      * Returns the number of tabs in this TabPanel.
32260      * @return {Number}
32261      */
32262      getCount : function(){
32263          return this.items.length;
32264      },
32265
32266     /**
32267      * Resizes all the tabs to the passed width
32268      * @param {Number} The new width
32269      */
32270     setTabWidth : function(width){
32271         this.currentTabWidth = width;
32272         for(var i = 0, len = this.items.length; i < len; i++) {
32273                 if(!this.items[i].isHidden()) {
32274                 this.items[i].setWidth(width);
32275             }
32276         }
32277     },
32278
32279     /**
32280      * Destroys this TabPanel
32281      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
32282      */
32283     destroy : function(removeEl){
32284         Roo.EventManager.removeResizeListener(this.onResize, this);
32285         for(var i = 0, len = this.items.length; i < len; i++){
32286             this.items[i].purgeListeners();
32287         }
32288         if(removeEl === true){
32289             this.el.update("");
32290             this.el.remove();
32291         }
32292     }
32293 });
32294
32295
32296 /** @private */
32297 Roo.panel.Tab.prototype.createStripList = function(strip){
32298     // div wrapper for retard IE
32299     // returns the "tr" element.
32300     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
32301         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
32302         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
32303     return strip.firstChild.firstChild.firstChild.firstChild;
32304 };
32305 /** @private */
32306 Roo.panel.Tab.prototype.createBody = function(container){
32307     var body = document.createElement("div");
32308     Roo.id(body, "tab-body");
32309     Roo.fly(body).addClass("x-tabs-body");
32310     container.appendChild(body);
32311     return body;
32312 };
32313 /** @private */
32314 Roo.panel.Tab.prototype.createItemBody = function(bodyEl, id){
32315     var body = Roo.getDom(id);
32316     if(!body){
32317         body = document.createElement("div");
32318         body.id = id;
32319     }
32320     Roo.fly(body).addClass("x-tabs-item-body");
32321     bodyEl.insertBefore(body, bodyEl.firstChild);
32322     return body;
32323 };
32324 /** @private */
32325 Roo.panel.Tab.prototype.createStripElements = function(stripEl, text, closable){
32326     var td = document.createElement("td");
32327     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
32328     //stripEl.appendChild(td);
32329     if(closable){
32330         td.className = "x-tabs-closable";
32331         if(!this.closeTpl){
32332             this.closeTpl = new Roo.Template(
32333                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
32334                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
32335                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
32336             );
32337         }
32338         var el = this.closeTpl.overwrite(td, {"text": text});
32339         var close = el.getElementsByTagName("div")[0];
32340         var inner = el.getElementsByTagName("em")[0];
32341         return {"el": el, "close": close, "inner": inner};
32342     } else {
32343         if(!this.tabTpl){
32344             this.tabTpl = new Roo.Template(
32345                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
32346                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
32347             );
32348         }
32349         var el = this.tabTpl.overwrite(td, {"text": text});
32350         var inner = el.getElementsByTagName("em")[0];
32351         return {"el": el, "inner": inner};
32352     }
32353 };/**
32354  * @class Roo.panel.TabItem
32355  * @extends Roo.util.Observable
32356  * Represents an individual item (tab plus body) in a TabPanel.
32357  * @param {Roo.panel.Tab} tabPanel The {@link Roo.panel.Tab} this TabPanelItem belongs to
32358  * @param {String} id The id of this TabPanelItem
32359  * @param {String} text The text for the tab of this TabPanelItem
32360  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
32361  */
32362  Roo.panel.TabItem = function(tabPanel, id, text, closable){
32363     /**
32364      * The {@link Roo.panel.Tab} this TabPanelItem belongs to
32365      * @type Roo.panel.Tab
32366      */
32367     this.tabPanel = tabPanel;
32368     /**
32369      * The id for this TabPanelItem
32370      * @type String
32371      */
32372     this.id = id;
32373     /** @private */
32374     this.disabled = false;
32375     /** @private */
32376     this.text = text;
32377     /** @private */
32378     this.loaded = false;
32379     this.closable = closable;
32380
32381     /**
32382      * The body element for this TabPanelItem.
32383      * @type Roo.Element
32384      */
32385     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
32386     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
32387     this.bodyEl.setStyle("display", "block");
32388     this.bodyEl.setStyle("zoom", "1");
32389     this.hideAction();
32390
32391     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
32392     /** @private */
32393     this.el = Roo.get(els.el, true);
32394     this.inner = Roo.get(els.inner, true);
32395     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
32396     this.pnode = Roo.get(els.el.parentNode, true);
32397     this.el.on("mousedown", this.onTabMouseDown, this);
32398     this.el.on("click", this.onTabClick, this);
32399     /** @private */
32400     if(closable){
32401         var c = Roo.get(els.close, true);
32402         c.dom.title = this.closeText;
32403         c.addClassOnOver("close-over");
32404         c.on("click", this.closeClick, this);
32405      }
32406
32407     this.addEvents({
32408          /**
32409          * @event activate
32410          * Fires when this tab becomes the active tab.
32411          * @param {Roo.panel.Tab} tabPanel The parent TabPanel
32412          * @param {Roo.panel.TabItem} this
32413          */
32414         "activate": true,
32415         /**
32416          * @event beforeclose
32417          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
32418          * @param {Roo.panel.TabItem} this
32419          * @param {Object} e Set cancel to true on this object to cancel the close.
32420          */
32421         "beforeclose": true,
32422         /**
32423          * @event close
32424          * Fires when this tab is closed.
32425          * @param {Roo.panel.TabItem} this
32426          */
32427          "close": true,
32428         /**
32429          * @event deactivate
32430          * Fires when this tab is no longer the active tab.
32431          * @param {Roo.panel.Tab} tabPanel The parent TabPanel
32432          * @param {Roo.panel.TabItem} this
32433          */
32434          "deactivate" : true
32435     });
32436     this.hidden = false;
32437
32438     Roo.panel.TabItem.superclass.constructor.call(this);
32439 };
32440
32441 Roo.extend(Roo.panel.TabItem, Roo.util.Observable, {
32442     purgeListeners : function(){
32443        Roo.util.Observable.prototype.purgeListeners.call(this);
32444        this.el.removeAllListeners();
32445     },
32446     /**
32447      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
32448      */
32449     show : function(){
32450         this.pnode.addClass("on");
32451         this.showAction();
32452         if(Roo.isOpera){
32453             this.tabPanel.stripWrap.repaint();
32454         }
32455         this.fireEvent("activate", this.tabPanel, this);
32456     },
32457
32458     /**
32459      * Returns true if this tab is the active tab.
32460      * @return {Boolean}
32461      */
32462     isActive : function(){
32463         return this.tabPanel.getActiveTab() == this;
32464     },
32465
32466     /**
32467      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
32468      */
32469     hide : function(){
32470         this.pnode.removeClass("on");
32471         this.hideAction();
32472         this.fireEvent("deactivate", this.tabPanel, this);
32473     },
32474
32475     hideAction : function(){
32476         this.bodyEl.hide();
32477         this.bodyEl.setStyle("position", "absolute");
32478         this.bodyEl.setLeft("-20000px");
32479         this.bodyEl.setTop("-20000px");
32480     },
32481
32482     showAction : function(){
32483         this.bodyEl.setStyle("position", "relative");
32484         this.bodyEl.setTop("");
32485         this.bodyEl.setLeft("");
32486         this.bodyEl.show();
32487     },
32488
32489     /**
32490      * Set the tooltip for the tab.
32491      * @param {String} tooltip The tab's tooltip
32492      */
32493     setTooltip : function(text){
32494         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
32495             this.textEl.dom.qtip = text;
32496             this.textEl.dom.removeAttribute('title');
32497         }else{
32498             this.textEl.dom.title = text;
32499         }
32500     },
32501
32502     onTabClick : function(e){
32503         e.preventDefault();
32504         this.tabPanel.activate(this.id);
32505     },
32506
32507     onTabMouseDown : function(e){
32508         e.preventDefault();
32509         this.tabPanel.activate(this.id);
32510     },
32511
32512     getWidth : function(){
32513         return this.inner.getWidth();
32514     },
32515
32516     setWidth : function(width){
32517         var iwidth = width - this.pnode.getPadding("lr");
32518         this.inner.setWidth(iwidth);
32519         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
32520         this.pnode.setWidth(width);
32521     },
32522
32523     /**
32524      * Show or hide the tab
32525      * @param {Boolean} hidden True to hide or false to show.
32526      */
32527     setHidden : function(hidden){
32528         this.hidden = hidden;
32529         this.pnode.setStyle("display", hidden ? "none" : "");
32530     },
32531
32532     /**
32533      * Returns true if this tab is "hidden"
32534      * @return {Boolean}
32535      */
32536     isHidden : function(){
32537         return this.hidden;
32538     },
32539
32540     /**
32541      * Returns the text for this tab
32542      * @return {String}
32543      */
32544     getText : function(){
32545         return this.text;
32546     },
32547
32548     autoSize : function(){
32549         //this.el.beginMeasure();
32550         this.textEl.setWidth(1);
32551         /*
32552          *  #2804 [new] Tabs in Roojs
32553          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
32554          */
32555         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
32556         //this.el.endMeasure();
32557     },
32558
32559     /**
32560      * Sets the text for the tab (Note: this also sets the tooltip text)
32561      * @param {String} text The tab's text and tooltip
32562      */
32563     setText : function(text){
32564         this.text = text;
32565         this.textEl.update(text);
32566         this.setTooltip(text);
32567         if(!this.tabPanel.resizeTabs){
32568             this.autoSize();
32569         }
32570     },
32571     /**
32572      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
32573      */
32574     activate : function(){
32575         this.tabPanel.activate(this.id);
32576     },
32577
32578     /**
32579      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
32580      */
32581     disable : function(){
32582         if(this.tabPanel.active != this){
32583             this.disabled = true;
32584             this.pnode.addClass("disabled");
32585         }
32586     },
32587
32588     /**
32589      * Enables this TabPanelItem if it was previously disabled.
32590      */
32591     enable : function(){
32592         this.disabled = false;
32593         this.pnode.removeClass("disabled");
32594     },
32595
32596     /**
32597      * Sets the content for this TabPanelItem.
32598      * @param {String} content The content
32599      * @param {Boolean} loadScripts true to look for and load scripts
32600      */
32601     setContent : function(content, loadScripts){
32602         this.bodyEl.update(content, loadScripts);
32603     },
32604
32605     /**
32606      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
32607      * @return {Roo.UpdateManager} The UpdateManager
32608      */
32609     getUpdateManager : function(){
32610         return this.bodyEl.getUpdateManager();
32611     },
32612
32613     /**
32614      * Set a URL to be used to load the content for this TabPanelItem.
32615      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
32616      * @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)
32617      * @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)
32618      * @return {Roo.UpdateManager} The UpdateManager
32619      */
32620     setUrl : function(url, params, loadOnce){
32621         if(this.refreshDelegate){
32622             this.un('activate', this.refreshDelegate);
32623         }
32624         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
32625         this.on("activate", this.refreshDelegate);
32626         return this.bodyEl.getUpdateManager();
32627     },
32628
32629     /** @private */
32630     _handleRefresh : function(url, params, loadOnce){
32631         if(!loadOnce || !this.loaded){
32632             var updater = this.bodyEl.getUpdateManager();
32633             updater.update(url, params, this._setLoaded.createDelegate(this));
32634         }
32635     },
32636
32637     /**
32638      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
32639      *   Will fail silently if the setUrl method has not been called.
32640      *   This does not activate the panel, just updates its content.
32641      */
32642     refresh : function(){
32643         if(this.refreshDelegate){
32644            this.loaded = false;
32645            this.refreshDelegate();
32646         }
32647     },
32648
32649     /** @private */
32650     _setLoaded : function(){
32651         this.loaded = true;
32652     },
32653
32654     /** @private */
32655     closeClick : function(e){
32656         var o = {};
32657         e.stopEvent();
32658         this.fireEvent("beforeclose", this, o);
32659         if(o.cancel !== true){
32660             this.tabPanel.removeTab(this.id);
32661         }
32662     },
32663     /**
32664      * The text displayed in the tooltip for the close icon.
32665      * @type String
32666      */
32667     closeText : "Close this tab"
32668 });
32669
32670 /** @private */
32671 Roo.panel.Tab.prototype.createStrip = function(container){
32672     var strip = document.createElement("div");
32673     strip.className = "x-tabs-wrap";
32674     container.appendChild(strip);
32675     return strip;
32676 };/*
32677  * Based on:
32678  * Ext JS Library 1.1.1
32679  * Copyright(c) 2006-2007, Ext JS, LLC.
32680  *
32681  * Originally Released Under LGPL - original licence link has changed is not relivant.
32682  *
32683  * Fork - LGPL
32684  * <script type="text/javascript">
32685  */
32686
32687 /**
32688  * @class Roo.Button
32689  * @extends Roo.util.Observable
32690  * Simple Button class
32691  * @cfg {String} text The button text
32692  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
32693  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
32694  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
32695  * @cfg {Object} scope The scope of the handler
32696  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
32697  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
32698  * @cfg {Boolean} hidden True to start hidden (defaults to false)
32699  * @cfg {Boolean} disabled True to start disabled (defaults to false)
32700  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
32701  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
32702    applies if enableToggle = true)
32703  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
32704  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
32705   an {@link Roo.util.ClickRepeater} config object (defaults to false).
32706  * @constructor
32707  * Create a new button
32708  * @param {Object} config The config object
32709  */
32710 Roo.Button = function(renderTo, config)
32711 {
32712     if (!config) {
32713         config = renderTo;
32714         renderTo = config.renderTo || false;
32715     }
32716     
32717     Roo.apply(this, config);
32718     this.addEvents({
32719         /**
32720              * @event click
32721              * Fires when this button is clicked
32722              * @param {Button} this
32723              * @param {EventObject} e The click event
32724              */
32725             "click" : true,
32726         /**
32727              * @event toggle
32728              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
32729              * @param {Button} this
32730              * @param {Boolean} pressed
32731              */
32732             "toggle" : true,
32733         /**
32734              * @event mouseover
32735              * Fires when the mouse hovers over the button
32736              * @param {Button} this
32737              * @param {Event} e The event object
32738              */
32739         'mouseover' : true,
32740         /**
32741              * @event mouseout
32742              * Fires when the mouse exits the button
32743              * @param {Button} this
32744              * @param {Event} e The event object
32745              */
32746         'mouseout': true,
32747          /**
32748              * @event render
32749              * Fires when the button is rendered
32750              * @param {Button} this
32751              */
32752         'render': true
32753     });
32754     if(this.menu){
32755         this.menu = Roo.menu.MenuMgr.get(this.menu);
32756     }
32757     // register listeners first!!  - so render can be captured..
32758     Roo.util.Observable.call(this);
32759     if(renderTo){
32760         this.render(renderTo);
32761     }
32762     
32763   
32764 };
32765
32766 Roo.extend(Roo.Button, Roo.util.Observable, {
32767     /**
32768      * 
32769      */
32770     
32771     /**
32772      * Read-only. True if this button is hidden
32773      * @type Boolean
32774      */
32775     hidden : false,
32776     /**
32777      * Read-only. True if this button is disabled
32778      * @type Boolean
32779      */
32780     disabled : false,
32781     /**
32782      * Read-only. True if this button is pressed (only if enableToggle = true)
32783      * @type Boolean
32784      */
32785     pressed : false,
32786
32787     /**
32788      * @cfg {Number} tabIndex 
32789      * The DOM tabIndex for this button (defaults to undefined)
32790      */
32791     tabIndex : undefined,
32792
32793     /**
32794      * @cfg {Boolean} enableToggle
32795      * True to enable pressed/not pressed toggling (defaults to false)
32796      */
32797     enableToggle: false,
32798     /**
32799      * @cfg {Roo.menu.Menu} menu
32800      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
32801      */
32802     menu : undefined,
32803     /**
32804      * @cfg {String} menuAlign
32805      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
32806      */
32807     menuAlign : "tl-bl?",
32808
32809     /**
32810      * @cfg {String} iconCls
32811      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
32812      */
32813     iconCls : undefined,
32814     /**
32815      * @cfg {String} type
32816      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
32817      */
32818     type : 'button',
32819
32820     // private
32821     menuClassTarget: 'tr',
32822
32823     /**
32824      * @cfg {String} clickEvent
32825      * The type of event to map to the button's event handler (defaults to 'click')
32826      */
32827     clickEvent : 'click',
32828
32829     /**
32830      * @cfg {Boolean} handleMouseEvents
32831      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
32832      */
32833     handleMouseEvents : true,
32834
32835     /**
32836      * @cfg {String} tooltipType
32837      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
32838      */
32839     tooltipType : 'qtip',
32840
32841     /**
32842      * @cfg {String} cls
32843      * A CSS class to apply to the button's main element.
32844      */
32845     
32846     /**
32847      * @cfg {Roo.Template} template (Optional)
32848      * An {@link Roo.Template} with which to create the Button's main element. This Template must
32849      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
32850      * require code modifications if required elements (e.g. a button) aren't present.
32851      */
32852
32853     // private
32854     render : function(renderTo){
32855         var btn;
32856         if(this.hideParent){
32857             this.parentEl = Roo.get(renderTo);
32858         }
32859         if(!this.dhconfig){
32860             if(!this.template){
32861                 if(!Roo.Button.buttonTemplate){
32862                     // hideous table template
32863                     Roo.Button.buttonTemplate = new Roo.Template(
32864                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
32865                         '<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>',
32866                         "</tr></tbody></table>");
32867                 }
32868                 this.template = Roo.Button.buttonTemplate;
32869             }
32870             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
32871             var btnEl = btn.child("button:first");
32872             btnEl.on('focus', this.onFocus, this);
32873             btnEl.on('blur', this.onBlur, this);
32874             if(this.cls){
32875                 btn.addClass(this.cls);
32876             }
32877             if(this.icon){
32878                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
32879             }
32880             if(this.iconCls){
32881                 btnEl.addClass(this.iconCls);
32882                 if(!this.cls){
32883                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
32884                 }
32885             }
32886             if(this.tabIndex !== undefined){
32887                 btnEl.dom.tabIndex = this.tabIndex;
32888             }
32889             if(this.tooltip){
32890                 if(typeof this.tooltip == 'object'){
32891                     Roo.QuickTips.tips(Roo.apply({
32892                           target: btnEl.id
32893                     }, this.tooltip));
32894                 } else {
32895                     btnEl.dom[this.tooltipType] = this.tooltip;
32896                 }
32897             }
32898         }else{
32899             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
32900         }
32901         this.el = btn;
32902         if(this.id){
32903             this.el.dom.id = this.el.id = this.id;
32904         }
32905         if(this.menu){
32906             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
32907             this.menu.on("show", this.onMenuShow, this);
32908             this.menu.on("hide", this.onMenuHide, this);
32909         }
32910         btn.addClass("x-btn");
32911         if(Roo.isIE && !Roo.isIE7){
32912             this.autoWidth.defer(1, this);
32913         }else{
32914             this.autoWidth();
32915         }
32916         if(this.handleMouseEvents){
32917             btn.on("mouseover", this.onMouseOver, this);
32918             btn.on("mouseout", this.onMouseOut, this);
32919             btn.on("mousedown", this.onMouseDown, this);
32920         }
32921         btn.on(this.clickEvent, this.onClick, this);
32922         //btn.on("mouseup", this.onMouseUp, this);
32923         if(this.hidden){
32924             this.hide();
32925         }
32926         if(this.disabled){
32927             this.disable();
32928         }
32929         Roo.ButtonToggleMgr.register(this);
32930         if(this.pressed){
32931             this.el.addClass("x-btn-pressed");
32932         }
32933         if(this.repeat){
32934             var repeater = new Roo.util.ClickRepeater(btn,
32935                 typeof this.repeat == "object" ? this.repeat : {}
32936             );
32937             repeater.on("click", this.onClick,  this);
32938         }
32939         
32940         this.fireEvent('render', this);
32941         
32942     },
32943     /**
32944      * Returns the button's underlying element
32945      * @return {Roo.Element} The element
32946      */
32947     getEl : function(){
32948         return this.el;  
32949     },
32950     
32951     /**
32952      * Destroys this Button and removes any listeners.
32953      */
32954     destroy : function(){
32955         Roo.ButtonToggleMgr.unregister(this);
32956         this.el.removeAllListeners();
32957         this.purgeListeners();
32958         this.el.remove();
32959     },
32960
32961     // private
32962     autoWidth : function(){
32963         if(this.el){
32964             this.el.setWidth("auto");
32965             if(Roo.isIE7 && Roo.isStrict){
32966                 var ib = this.el.child('button');
32967                 if(ib && ib.getWidth() > 20){
32968                     ib.clip();
32969                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
32970                 }
32971             }
32972             if(this.minWidth){
32973                 if(this.hidden){
32974                     this.el.beginMeasure();
32975                 }
32976                 if(this.el.getWidth() < this.minWidth){
32977                     this.el.setWidth(this.minWidth);
32978                 }
32979                 if(this.hidden){
32980                     this.el.endMeasure();
32981                 }
32982             }
32983         }
32984     },
32985
32986     /**
32987      * Assigns this button's click handler
32988      * @param {Function} handler The function to call when the button is clicked
32989      * @param {Object} scope (optional) Scope for the function passed in
32990      */
32991     setHandler : function(handler, scope){
32992         this.handler = handler;
32993         this.scope = scope;  
32994     },
32995     
32996     /**
32997      * Sets this button's text
32998      * @param {String} text The button text
32999      */
33000     setText : function(text){
33001         this.text = text;
33002         if(this.el){
33003             this.el.child("td.x-btn-center button.x-btn-text").update(text);
33004         }
33005         this.autoWidth();
33006     },
33007     
33008     /**
33009      * Gets the text for this button
33010      * @return {String} The button text
33011      */
33012     getText : function(){
33013         return this.text;  
33014     },
33015     
33016     /**
33017      * Show this button
33018      */
33019     show: function(){
33020         this.hidden = false;
33021         if(this.el){
33022             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
33023         }
33024     },
33025     
33026     /**
33027      * Hide this button
33028      */
33029     hide: function(){
33030         this.hidden = true;
33031         if(this.el){
33032             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
33033         }
33034     },
33035     
33036     /**
33037      * Convenience function for boolean show/hide
33038      * @param {Boolean} visible True to show, false to hide
33039      */
33040     setVisible: function(visible){
33041         if(visible) {
33042             this.show();
33043         }else{
33044             this.hide();
33045         }
33046     },
33047     /**
33048          * Similar to toggle, but does not trigger event.
33049          * @param {Boolean} state [required] Force a particular state
33050          */
33051         setPressed : function(state)
33052         {
33053             if(state != this.pressed){
33054             if(state){
33055                 this.el.addClass("x-btn-pressed");
33056                 this.pressed = true;
33057             }else{
33058                 this.el.removeClass("x-btn-pressed");
33059                 this.pressed = false;
33060             }
33061         }
33062         },
33063         
33064     /**
33065      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
33066      * @param {Boolean} state (optional) Force a particular state
33067      */
33068     toggle : function(state){
33069         state = state === undefined ? !this.pressed : state;
33070         if(state != this.pressed){
33071             if(state){
33072                 this.el.addClass("x-btn-pressed");
33073                 this.pressed = true;
33074                 this.fireEvent("toggle", this, true);
33075             }else{
33076                 this.el.removeClass("x-btn-pressed");
33077                 this.pressed = false;
33078                 this.fireEvent("toggle", this, false);
33079             }
33080             if(this.toggleHandler){
33081                 this.toggleHandler.call(this.scope || this, this, state);
33082             }
33083         }
33084     },
33085     
33086         
33087         
33088     /**
33089      * Focus the button
33090      */
33091     focus : function(){
33092         this.el.child('button:first').focus();
33093     },
33094     
33095     /**
33096      * Disable this button
33097      */
33098     disable : function(){
33099         if(this.el){
33100             this.el.addClass("x-btn-disabled");
33101         }
33102         this.disabled = true;
33103     },
33104     
33105     /**
33106      * Enable this button
33107      */
33108     enable : function(){
33109         if(this.el){
33110             this.el.removeClass("x-btn-disabled");
33111         }
33112         this.disabled = false;
33113     },
33114
33115     /**
33116      * Convenience function for boolean enable/disable
33117      * @param {Boolean} enabled True to enable, false to disable
33118      */
33119     setDisabled : function(v){
33120         this[v !== true ? "enable" : "disable"]();
33121     },
33122
33123     // private
33124     onClick : function(e)
33125     {
33126         if(e){
33127             e.preventDefault();
33128         }
33129         if(e.button != 0){
33130             return;
33131         }
33132         if(!this.disabled){
33133             if(this.enableToggle){
33134                 this.toggle();
33135             }
33136             if(this.menu && !this.menu.isVisible()){
33137                 this.menu.show(this.el, this.menuAlign);
33138             }
33139             this.fireEvent("click", this, e);
33140             if(this.handler){
33141                 this.el.removeClass("x-btn-over");
33142                 this.handler.call(this.scope || this, this, e);
33143             }
33144         }
33145     },
33146     // private
33147     onMouseOver : function(e){
33148         if(!this.disabled){
33149             this.el.addClass("x-btn-over");
33150             this.fireEvent('mouseover', this, e);
33151         }
33152     },
33153     // private
33154     onMouseOut : function(e){
33155         if(!e.within(this.el,  true)){
33156             this.el.removeClass("x-btn-over");
33157             this.fireEvent('mouseout', this, e);
33158         }
33159     },
33160     // private
33161     onFocus : function(e){
33162         if(!this.disabled){
33163             this.el.addClass("x-btn-focus");
33164         }
33165     },
33166     // private
33167     onBlur : function(e){
33168         this.el.removeClass("x-btn-focus");
33169     },
33170     // private
33171     onMouseDown : function(e){
33172         if(!this.disabled && e.button == 0){
33173             this.el.addClass("x-btn-click");
33174             Roo.get(document).on('mouseup', this.onMouseUp, this);
33175         }
33176     },
33177     // private
33178     onMouseUp : function(e){
33179         if(e.button == 0){
33180             this.el.removeClass("x-btn-click");
33181             Roo.get(document).un('mouseup', this.onMouseUp, this);
33182         }
33183     },
33184     // private
33185     onMenuShow : function(e){
33186         this.el.addClass("x-btn-menu-active");
33187     },
33188     // private
33189     onMenuHide : function(e){
33190         this.el.removeClass("x-btn-menu-active");
33191     }   
33192 });
33193
33194 // Private utility class used by Button
33195 Roo.ButtonToggleMgr = function(){
33196    var groups = {};
33197    
33198    function toggleGroup(btn, state){
33199        if(state){
33200            var g = groups[btn.toggleGroup];
33201            for(var i = 0, l = g.length; i < l; i++){
33202                if(g[i] != btn){
33203                    g[i].toggle(false);
33204                }
33205            }
33206        }
33207    }
33208    
33209    return {
33210        register : function(btn){
33211            if(!btn.toggleGroup){
33212                return;
33213            }
33214            var g = groups[btn.toggleGroup];
33215            if(!g){
33216                g = groups[btn.toggleGroup] = [];
33217            }
33218            g.push(btn);
33219            btn.on("toggle", toggleGroup);
33220        },
33221        
33222        unregister : function(btn){
33223            if(!btn.toggleGroup){
33224                return;
33225            }
33226            var g = groups[btn.toggleGroup];
33227            if(g){
33228                g.remove(btn);
33229                btn.un("toggle", toggleGroup);
33230            }
33231        }
33232    };
33233 }();/*
33234  * Based on:
33235  * Ext JS Library 1.1.1
33236  * Copyright(c) 2006-2007, Ext JS, LLC.
33237  *
33238  * Originally Released Under LGPL - original licence link has changed is not relivant.
33239  *
33240  * Fork - LGPL
33241  * <script type="text/javascript">
33242  */
33243  
33244 /**
33245  * @class Roo.SplitButton
33246  * @extends Roo.Button
33247  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
33248  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
33249  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
33250  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
33251  * @cfg {String} arrowTooltip The title attribute of the arrow
33252  * @constructor
33253  * Create a new menu button
33254  * @param {String/HTMLElement/Element} renderTo The element to append the button to
33255  * @param {Object} config The config object
33256  */
33257 Roo.SplitButton = function(renderTo, config){
33258     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
33259     /**
33260      * @event arrowclick
33261      * Fires when this button's arrow is clicked
33262      * @param {SplitButton} this
33263      * @param {EventObject} e The click event
33264      */
33265     this.addEvents({"arrowclick":true});
33266 };
33267
33268 Roo.extend(Roo.SplitButton, Roo.Button, {
33269     render : function(renderTo){
33270         // this is one sweet looking template!
33271         var tpl = new Roo.Template(
33272             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
33273             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
33274             '<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>',
33275             "</tbody></table></td><td>",
33276             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
33277             '<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>',
33278             "</tbody></table></td></tr></table>"
33279         );
33280         var btn = tpl.append(renderTo, [this.text, this.type], true);
33281         var btnEl = btn.child("button");
33282         if(this.cls){
33283             btn.addClass(this.cls);
33284         }
33285         if(this.icon){
33286             btnEl.setStyle('background-image', 'url(' +this.icon +')');
33287         }
33288         if(this.iconCls){
33289             btnEl.addClass(this.iconCls);
33290             if(!this.cls){
33291                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
33292             }
33293         }
33294         this.el = btn;
33295         if(this.handleMouseEvents){
33296             btn.on("mouseover", this.onMouseOver, this);
33297             btn.on("mouseout", this.onMouseOut, this);
33298             btn.on("mousedown", this.onMouseDown, this);
33299             btn.on("mouseup", this.onMouseUp, this);
33300         }
33301         btn.on(this.clickEvent, this.onClick, this);
33302         if(this.tooltip){
33303             if(typeof this.tooltip == 'object'){
33304                 Roo.QuickTips.tips(Roo.apply({
33305                       target: btnEl.id
33306                 }, this.tooltip));
33307             } else {
33308                 btnEl.dom[this.tooltipType] = this.tooltip;
33309             }
33310         }
33311         if(this.arrowTooltip){
33312             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
33313         }
33314         if(this.hidden){
33315             this.hide();
33316         }
33317         if(this.disabled){
33318             this.disable();
33319         }
33320         if(this.pressed){
33321             this.el.addClass("x-btn-pressed");
33322         }
33323         if(Roo.isIE && !Roo.isIE7){
33324             this.autoWidth.defer(1, this);
33325         }else{
33326             this.autoWidth();
33327         }
33328         if(this.menu){
33329             this.menu.on("show", this.onMenuShow, this);
33330             this.menu.on("hide", this.onMenuHide, this);
33331         }
33332         this.fireEvent('render', this);
33333     },
33334
33335     // private
33336     autoWidth : function(){
33337         if(this.el){
33338             var tbl = this.el.child("table:first");
33339             var tbl2 = this.el.child("table:last");
33340             this.el.setWidth("auto");
33341             tbl.setWidth("auto");
33342             if(Roo.isIE7 && Roo.isStrict){
33343                 var ib = this.el.child('button:first');
33344                 if(ib && ib.getWidth() > 20){
33345                     ib.clip();
33346                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
33347                 }
33348             }
33349             if(this.minWidth){
33350                 if(this.hidden){
33351                     this.el.beginMeasure();
33352                 }
33353                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
33354                     tbl.setWidth(this.minWidth-tbl2.getWidth());
33355                 }
33356                 if(this.hidden){
33357                     this.el.endMeasure();
33358                 }
33359             }
33360             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
33361         } 
33362     },
33363     /**
33364      * Sets this button's click handler
33365      * @param {Function} handler The function to call when the button is clicked
33366      * @param {Object} scope (optional) Scope for the function passed above
33367      */
33368     setHandler : function(handler, scope){
33369         this.handler = handler;
33370         this.scope = scope;  
33371     },
33372     
33373     /**
33374      * Sets this button's arrow click handler
33375      * @param {Function} handler The function to call when the arrow is clicked
33376      * @param {Object} scope (optional) Scope for the function passed above
33377      */
33378     setArrowHandler : function(handler, scope){
33379         this.arrowHandler = handler;
33380         this.scope = scope;  
33381     },
33382     
33383     /**
33384      * Focus the button
33385      */
33386     focus : function(){
33387         if(this.el){
33388             this.el.child("button:first").focus();
33389         }
33390     },
33391
33392     // private
33393     onClick : function(e){
33394         e.preventDefault();
33395         if(!this.disabled){
33396             if(e.getTarget(".x-btn-menu-arrow-wrap")){
33397                 if(this.menu && !this.menu.isVisible()){
33398                     this.menu.show(this.el, this.menuAlign);
33399                 }
33400                 this.fireEvent("arrowclick", this, e);
33401                 if(this.arrowHandler){
33402                     this.arrowHandler.call(this.scope || this, this, e);
33403                 }
33404             }else{
33405                 this.fireEvent("click", this, e);
33406                 if(this.handler){
33407                     this.handler.call(this.scope || this, this, e);
33408                 }
33409             }
33410         }
33411     },
33412     // private
33413     onMouseDown : function(e){
33414         if(!this.disabled){
33415             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
33416         }
33417     },
33418     // private
33419     onMouseUp : function(e){
33420         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
33421     }   
33422 });
33423
33424
33425 // backwards compat
33426 Roo.MenuButton = Roo.SplitButton;/*
33427  * Based on:
33428  * Ext JS Library 1.1.1
33429  * Copyright(c) 2006-2007, Ext JS, LLC.
33430  *
33431  * Originally Released Under LGPL - original licence link has changed is not relivant.
33432  *
33433  * Fork - LGPL
33434  * <script type="text/javascript">
33435  */
33436
33437 /**
33438  * @class Roo.Toolbar
33439  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field 
33440  * Basic Toolbar class.
33441  * @constructor
33442  * Creates a new Toolbar
33443  * @param {Object} container The config object
33444  */ 
33445 Roo.Toolbar = function(container, buttons, config)
33446 {
33447     /// old consturctor format still supported..
33448     if(container instanceof Array){ // omit the container for later rendering
33449         buttons = container;
33450         config = buttons;
33451         container = null;
33452     }
33453     if (typeof(container) == 'object' && container.xtype) {
33454         config = container;
33455         container = config.container;
33456         buttons = config.buttons || []; // not really - use items!!
33457     }
33458     var xitems = [];
33459     if (config && config.items) {
33460         xitems = config.items;
33461         delete config.items;
33462     }
33463     Roo.apply(this, config);
33464     this.buttons = buttons;
33465     
33466     if(container){
33467         this.render(container);
33468     }
33469     this.xitems = xitems;
33470     Roo.each(xitems, function(b) {
33471         this.add(b);
33472     }, this);
33473     
33474 };
33475
33476 Roo.Toolbar.prototype = {
33477     /**
33478      * @cfg {Array} items
33479      * array of button configs or elements to add (will be converted to a MixedCollection)
33480      */
33481     items: false,
33482     /**
33483      * @cfg {String/HTMLElement/Element} container
33484      * The id or element that will contain the toolbar
33485      */
33486     // private
33487     render : function(ct){
33488         this.el = Roo.get(ct);
33489         if(this.cls){
33490             this.el.addClass(this.cls);
33491         }
33492         // using a table allows for vertical alignment
33493         // 100% width is needed by Safari...
33494         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
33495         this.tr = this.el.child("tr", true);
33496         var autoId = 0;
33497         this.items = new Roo.util.MixedCollection(false, function(o){
33498             return o.id || ("item" + (++autoId));
33499         });
33500         if(this.buttons){
33501             this.add.apply(this, this.buttons);
33502             delete this.buttons;
33503         }
33504     },
33505
33506     /**
33507      * Adds element(s) to the toolbar -- this function takes a variable number of 
33508      * arguments of mixed type and adds them to the toolbar.
33509      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
33510      * <ul>
33511      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
33512      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
33513      * <li>Field: Any form field (equivalent to {@link #addField})</li>
33514      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
33515      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
33516      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
33517      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
33518      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
33519      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
33520      * </ul>
33521      * @param {Mixed} arg2
33522      * @param {Mixed} etc.
33523      */
33524     add : function(){
33525         var a = arguments, l = a.length;
33526         for(var i = 0; i < l; i++){
33527             this._add(a[i]);
33528         }
33529     },
33530     // private..
33531     _add : function(el) {
33532         
33533         if (el.xtype) {
33534             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
33535         }
33536         
33537         if (el.applyTo){ // some kind of form field
33538             return this.addField(el);
33539         } 
33540         if (el.render){ // some kind of Toolbar.Item
33541             return this.addItem(el);
33542         }
33543         if (typeof el == "string"){ // string
33544             if(el == "separator" || el == "-"){
33545                 return this.addSeparator();
33546             }
33547             if (el == " "){
33548                 return this.addSpacer();
33549             }
33550             if(el == "->"){
33551                 return this.addFill();
33552             }
33553             return this.addText(el);
33554             
33555         }
33556         if(el.tagName){ // element
33557             return this.addElement(el);
33558         }
33559         if(typeof el == "object"){ // must be button config?
33560             return this.addButton(el);
33561         }
33562         // and now what?!?!
33563         return false;
33564         
33565     },
33566     
33567     /**
33568      * Add an Xtype element
33569      * @param {Object} xtype Xtype Object
33570      * @return {Object} created Object
33571      */
33572     addxtype : function(e){
33573         return this.add(e);  
33574     },
33575     
33576     /**
33577      * Returns the Element for this toolbar.
33578      * @return {Roo.Element}
33579      */
33580     getEl : function(){
33581         return this.el;  
33582     },
33583     
33584     /**
33585      * Adds a separator
33586      * @return {Roo.Toolbar.Item} The separator item
33587      */
33588     addSeparator : function(){
33589         return this.addItem(new Roo.Toolbar.Separator());
33590     },
33591
33592     /**
33593      * Adds a spacer element
33594      * @return {Roo.Toolbar.Spacer} The spacer item
33595      */
33596     addSpacer : function(){
33597         return this.addItem(new Roo.Toolbar.Spacer());
33598     },
33599
33600     /**
33601      * Adds a fill element that forces subsequent additions to the right side of the toolbar
33602      * @return {Roo.Toolbar.Fill} The fill item
33603      */
33604     addFill : function(){
33605         return this.addItem(new Roo.Toolbar.Fill());
33606     },
33607
33608     /**
33609      * Adds any standard HTML element to the toolbar
33610      * @param {String/HTMLElement/Element} el The element or id of the element to add
33611      * @return {Roo.Toolbar.Item} The element's item
33612      */
33613     addElement : function(el){
33614         return this.addItem(new Roo.Toolbar.Item(el));
33615     },
33616     /**
33617      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
33618      * @type Roo.util.MixedCollection  
33619      */
33620     items : false,
33621      
33622     /**
33623      * Adds any Toolbar.Item or subclass
33624      * @param {Roo.Toolbar.Item} item
33625      * @return {Roo.Toolbar.Item} The item
33626      */
33627     addItem : function(item){
33628         var td = this.nextBlock();
33629         item.render(td);
33630         this.items.add(item);
33631         return item;
33632     },
33633     
33634     /**
33635      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
33636      * @param {Object/Array} config A button config or array of configs
33637      * @return {Roo.Toolbar.Button/Array}
33638      */
33639     addButton : function(config){
33640         if(config instanceof Array){
33641             var buttons = [];
33642             for(var i = 0, len = config.length; i < len; i++) {
33643                 buttons.push(this.addButton(config[i]));
33644             }
33645             return buttons;
33646         }
33647         var b = config;
33648         if(!(config instanceof Roo.Toolbar.Button)){
33649             b = config.split ?
33650                 new Roo.Toolbar.SplitButton(config) :
33651                 new Roo.Toolbar.Button(config);
33652         }
33653         var td = this.nextBlock();
33654         b.render(td);
33655         this.items.add(b);
33656         return b;
33657     },
33658     
33659     /**
33660      * Adds text to the toolbar
33661      * @param {String} text The text to add
33662      * @return {Roo.Toolbar.Item} The element's item
33663      */
33664     addText : function(text){
33665         return this.addItem(new Roo.Toolbar.TextItem(text));
33666     },
33667     
33668     /**
33669      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
33670      * @param {Number} index The index where the item is to be inserted
33671      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
33672      * @return {Roo.Toolbar.Button/Item}
33673      */
33674     insertButton : function(index, item){
33675         if(item instanceof Array){
33676             var buttons = [];
33677             for(var i = 0, len = item.length; i < len; i++) {
33678                buttons.push(this.insertButton(index + i, item[i]));
33679             }
33680             return buttons;
33681         }
33682         if (!(item instanceof Roo.Toolbar.Button)){
33683            item = new Roo.Toolbar.Button(item);
33684         }
33685         var td = document.createElement("td");
33686         this.tr.insertBefore(td, this.tr.childNodes[index]);
33687         item.render(td);
33688         this.items.insert(index, item);
33689         return item;
33690     },
33691     
33692     /**
33693      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
33694      * @param {Object} config
33695      * @return {Roo.Toolbar.Item} The element's item
33696      */
33697     addDom : function(config, returnEl){
33698         var td = this.nextBlock();
33699         Roo.DomHelper.overwrite(td, config);
33700         var ti = new Roo.Toolbar.Item(td.firstChild);
33701         ti.render(td);
33702         this.items.add(ti);
33703         return ti;
33704     },
33705
33706     /**
33707      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
33708      * @type Roo.util.MixedCollection  
33709      */
33710     fields : false,
33711     
33712     /**
33713      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
33714      * Note: the field should not have been rendered yet. For a field that has already been
33715      * rendered, use {@link #addElement}.
33716      * @param {Roo.form.Field} field
33717      * @return {Roo.ToolbarItem}
33718      */
33719      
33720       
33721     addField : function(field) {
33722         if (!this.fields) {
33723             var autoId = 0;
33724             this.fields = new Roo.util.MixedCollection(false, function(o){
33725                 return o.id || ("item" + (++autoId));
33726             });
33727
33728         }
33729         
33730         var td = this.nextBlock();
33731         field.render(td);
33732         var ti = new Roo.Toolbar.Item(td.firstChild);
33733         ti.render(td);
33734         this.items.add(ti);
33735         this.fields.add(field);
33736         return ti;
33737     },
33738     /**
33739      * Hide the toolbar
33740      * @method hide
33741      */
33742      
33743       
33744     hide : function()
33745     {
33746         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
33747         this.el.child('div').hide();
33748     },
33749     /**
33750      * Show the toolbar
33751      * @method show
33752      */
33753     show : function()
33754     {
33755         this.el.child('div').show();
33756     },
33757       
33758     // private
33759     nextBlock : function(){
33760         var td = document.createElement("td");
33761         this.tr.appendChild(td);
33762         return td;
33763     },
33764
33765     // private
33766     destroy : function(){
33767         if(this.items){ // rendered?
33768             Roo.destroy.apply(Roo, this.items.items);
33769         }
33770         if(this.fields){ // rendered?
33771             Roo.destroy.apply(Roo, this.fields.items);
33772         }
33773         Roo.Element.uncache(this.el, this.tr);
33774     }
33775 };
33776
33777 /**
33778  * @class Roo.Toolbar.Item
33779  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
33780  * @constructor
33781  * Creates a new Item
33782  * @param {HTMLElement} el 
33783  */
33784 Roo.Toolbar.Item = function(el){
33785     var cfg = {};
33786     if (typeof (el.xtype) != 'undefined') {
33787         cfg = el;
33788         el = cfg.el;
33789     }
33790     
33791     this.el = Roo.getDom(el);
33792     this.id = Roo.id(this.el);
33793     this.hidden = false;
33794     
33795     this.addEvents({
33796          /**
33797              * @event render
33798              * Fires when the button is rendered
33799              * @param {Button} this
33800              */
33801         'render': true
33802     });
33803     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
33804 };
33805 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
33806 //Roo.Toolbar.Item.prototype = {
33807     
33808     /**
33809      * Get this item's HTML Element
33810      * @return {HTMLElement}
33811      */
33812     getEl : function(){
33813        return this.el;  
33814     },
33815
33816     // private
33817     render : function(td){
33818         
33819          this.td = td;
33820         td.appendChild(this.el);
33821         
33822         this.fireEvent('render', this);
33823     },
33824     
33825     /**
33826      * Removes and destroys this item.
33827      */
33828     destroy : function(){
33829         this.td.parentNode.removeChild(this.td);
33830     },
33831     
33832     /**
33833      * Shows this item.
33834      */
33835     show: function(){
33836         this.hidden = false;
33837         this.td.style.display = "";
33838     },
33839     
33840     /**
33841      * Hides this item.
33842      */
33843     hide: function(){
33844         this.hidden = true;
33845         this.td.style.display = "none";
33846     },
33847     
33848     /**
33849      * Convenience function for boolean show/hide.
33850      * @param {Boolean} visible true to show/false to hide
33851      */
33852     setVisible: function(visible){
33853         if(visible) {
33854             this.show();
33855         }else{
33856             this.hide();
33857         }
33858     },
33859     
33860     /**
33861      * Try to focus this item.
33862      */
33863     focus : function(){
33864         Roo.fly(this.el).focus();
33865     },
33866     
33867     /**
33868      * Disables this item.
33869      */
33870     disable : function(){
33871         Roo.fly(this.td).addClass("x-item-disabled");
33872         this.disabled = true;
33873         this.el.disabled = true;
33874     },
33875     
33876     /**
33877      * Enables this item.
33878      */
33879     enable : function(){
33880         Roo.fly(this.td).removeClass("x-item-disabled");
33881         this.disabled = false;
33882         this.el.disabled = false;
33883     }
33884 });
33885
33886
33887 /**
33888  * @class Roo.Toolbar.Separator
33889  * @extends Roo.Toolbar.Item
33890  * A simple toolbar separator class
33891  * @constructor
33892  * Creates a new Separator
33893  */
33894 Roo.Toolbar.Separator = function(cfg){
33895     
33896     var s = document.createElement("span");
33897     s.className = "ytb-sep";
33898     if (cfg) {
33899         cfg.el = s;
33900     }
33901     
33902     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
33903 };
33904 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
33905     enable:Roo.emptyFn,
33906     disable:Roo.emptyFn,
33907     focus:Roo.emptyFn
33908 });
33909
33910 /**
33911  * @class Roo.Toolbar.Spacer
33912  * @extends Roo.Toolbar.Item
33913  * A simple element that adds extra horizontal space to a toolbar.
33914  * @constructor
33915  * Creates a new Spacer
33916  */
33917 Roo.Toolbar.Spacer = function(cfg){
33918     var s = document.createElement("div");
33919     s.className = "ytb-spacer";
33920     if (cfg) {
33921         cfg.el = s;
33922     }
33923     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
33924 };
33925 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
33926     enable:Roo.emptyFn,
33927     disable:Roo.emptyFn,
33928     focus:Roo.emptyFn
33929 });
33930
33931 /**
33932  * @class Roo.Toolbar.Fill
33933  * @extends Roo.Toolbar.Spacer
33934  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
33935  * @constructor
33936  * Creates a new Spacer
33937  */
33938 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
33939     // private
33940     render : function(td){
33941         td.style.width = '100%';
33942         Roo.Toolbar.Fill.superclass.render.call(this, td);
33943     }
33944 });
33945
33946 /**
33947  * @class Roo.Toolbar.TextItem
33948  * @extends Roo.Toolbar.Item
33949  * A simple class that renders text directly into a toolbar.
33950  * @constructor
33951  * Creates a new TextItem
33952  * @cfg {string} text 
33953  */
33954 Roo.Toolbar.TextItem = function(cfg){
33955     var  text = cfg || "";
33956     if (typeof(cfg) == 'object') {
33957         text = cfg.text || "";
33958     }  else {
33959         cfg = null;
33960     }
33961     var s = document.createElement("span");
33962     s.className = "ytb-text";
33963     s.innerHTML = text;
33964     if (cfg) {
33965         cfg.el  = s;
33966     }
33967     
33968     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
33969 };
33970 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
33971     
33972      
33973     enable:Roo.emptyFn,
33974     disable:Roo.emptyFn,
33975     focus:Roo.emptyFn,
33976      /**
33977      * Shows this button
33978      */
33979     show: function(){
33980         this.hidden = false;
33981         this.el.style.display = "";
33982     },
33983     
33984     /**
33985      * Hides this button
33986      */
33987     hide: function(){
33988         this.hidden = true;
33989         this.el.style.display = "none";
33990     }
33991     
33992 });
33993
33994 /**
33995  * @class Roo.Toolbar.Button
33996  * @extends Roo.Button
33997  * A button that renders into a toolbar.
33998  * @constructor
33999  * Creates a new Button
34000  * @param {Object} config A standard {@link Roo.Button} config object
34001  */
34002 Roo.Toolbar.Button = function(config){
34003     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
34004 };
34005 Roo.extend(Roo.Toolbar.Button, Roo.Button,
34006 {
34007     
34008     
34009     render : function(td){
34010         this.td = td;
34011         Roo.Toolbar.Button.superclass.render.call(this, td);
34012     },
34013     
34014     /**
34015      * Removes and destroys this button
34016      */
34017     destroy : function(){
34018         Roo.Toolbar.Button.superclass.destroy.call(this);
34019         this.td.parentNode.removeChild(this.td);
34020     },
34021     
34022     /**
34023      * Shows this button
34024      */
34025     show: function(){
34026         this.hidden = false;
34027         this.td.style.display = "";
34028     },
34029     
34030     /**
34031      * Hides this button
34032      */
34033     hide: function(){
34034         this.hidden = true;
34035         this.td.style.display = "none";
34036     },
34037
34038     /**
34039      * Disables this item
34040      */
34041     disable : function(){
34042         Roo.fly(this.td).addClass("x-item-disabled");
34043         this.disabled = true;
34044     },
34045
34046     /**
34047      * Enables this item
34048      */
34049     enable : function(){
34050         Roo.fly(this.td).removeClass("x-item-disabled");
34051         this.disabled = false;
34052     }
34053 });
34054 // backwards compat
34055 Roo.ToolbarButton = Roo.Toolbar.Button;
34056
34057 /**
34058  * @class Roo.Toolbar.SplitButton
34059  * @extends Roo.SplitButton
34060  * A menu button that renders into a toolbar.
34061  * @constructor
34062  * Creates a new SplitButton
34063  * @param {Object} config A standard {@link Roo.SplitButton} config object
34064  */
34065 Roo.Toolbar.SplitButton = function(config){
34066     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
34067 };
34068 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
34069     render : function(td){
34070         this.td = td;
34071         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
34072     },
34073     
34074     /**
34075      * Removes and destroys this button
34076      */
34077     destroy : function(){
34078         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
34079         this.td.parentNode.removeChild(this.td);
34080     },
34081     
34082     /**
34083      * Shows this button
34084      */
34085     show: function(){
34086         this.hidden = false;
34087         this.td.style.display = "";
34088     },
34089     
34090     /**
34091      * Hides this button
34092      */
34093     hide: function(){
34094         this.hidden = true;
34095         this.td.style.display = "none";
34096     }
34097 });
34098
34099 // backwards compat
34100 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
34101  * Based on:
34102  * Ext JS Library 1.1.1
34103  * Copyright(c) 2006-2007, Ext JS, LLC.
34104  *
34105  * Originally Released Under LGPL - original licence link has changed is not relivant.
34106  *
34107  * Fork - LGPL
34108  * <script type="text/javascript">
34109  */
34110  
34111 /**
34112  * @class Roo.PagingToolbar
34113  * @extends Roo.Toolbar
34114  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field
34115  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
34116  * @constructor
34117  * Create a new PagingToolbar
34118  * @param {Object} config The config object
34119  */
34120 Roo.PagingToolbar = function(el, ds, config)
34121 {
34122     // old args format still supported... - xtype is prefered..
34123     if (typeof(el) == 'object' && el.xtype) {
34124         // created from xtype...
34125         config = el;
34126         ds = el.dataSource;
34127         el = config.container;
34128     }
34129     var items = [];
34130     if (config.items) {
34131         items = config.items;
34132         config.items = [];
34133     }
34134     
34135     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
34136     this.ds = ds;
34137     this.cursor = 0;
34138     this.renderButtons(this.el);
34139     this.bind(ds);
34140     
34141     // supprot items array.
34142    
34143     Roo.each(items, function(e) {
34144         this.add(Roo.factory(e));
34145     },this);
34146     
34147 };
34148
34149 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
34150    
34151     /**
34152      * @cfg {String/HTMLElement/Element} container
34153      * container The id or element that will contain the toolbar
34154      */
34155     /**
34156      * @cfg {Boolean} displayInfo
34157      * True to display the displayMsg (defaults to false)
34158      */
34159     
34160     
34161     /**
34162      * @cfg {Number} pageSize
34163      * The number of records to display per page (defaults to 20)
34164      */
34165     pageSize: 20,
34166     /**
34167      * @cfg {String} displayMsg
34168      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
34169      */
34170     displayMsg : 'Displaying {0} - {1} of {2}',
34171     /**
34172      * @cfg {String} emptyMsg
34173      * The message to display when no records are found (defaults to "No data to display")
34174      */
34175     emptyMsg : 'No data to display',
34176     /**
34177      * Customizable piece of the default paging text (defaults to "Page")
34178      * @type String
34179      */
34180     beforePageText : "Page",
34181     /**
34182      * Customizable piece of the default paging text (defaults to "of %0")
34183      * @type String
34184      */
34185     afterPageText : "of {0}",
34186     /**
34187      * Customizable piece of the default paging text (defaults to "First Page")
34188      * @type String
34189      */
34190     firstText : "First Page",
34191     /**
34192      * Customizable piece of the default paging text (defaults to "Previous Page")
34193      * @type String
34194      */
34195     prevText : "Previous Page",
34196     /**
34197      * Customizable piece of the default paging text (defaults to "Next Page")
34198      * @type String
34199      */
34200     nextText : "Next Page",
34201     /**
34202      * Customizable piece of the default paging text (defaults to "Last Page")
34203      * @type String
34204      */
34205     lastText : "Last Page",
34206     /**
34207      * Customizable piece of the default paging text (defaults to "Refresh")
34208      * @type String
34209      */
34210     refreshText : "Refresh",
34211
34212     // private
34213     renderButtons : function(el){
34214         Roo.PagingToolbar.superclass.render.call(this, el);
34215         this.first = this.addButton({
34216             tooltip: this.firstText,
34217             cls: "x-btn-icon x-grid-page-first",
34218             disabled: true,
34219             handler: this.onClick.createDelegate(this, ["first"])
34220         });
34221         this.prev = this.addButton({
34222             tooltip: this.prevText,
34223             cls: "x-btn-icon x-grid-page-prev",
34224             disabled: true,
34225             handler: this.onClick.createDelegate(this, ["prev"])
34226         });
34227         //this.addSeparator();
34228         this.add(this.beforePageText);
34229         this.field = Roo.get(this.addDom({
34230            tag: "input",
34231            type: "text",
34232            size: "3",
34233            value: "1",
34234            cls: "x-grid-page-number"
34235         }).el);
34236         this.field.on("keydown", this.onPagingKeydown, this);
34237         this.field.on("focus", function(){this.dom.select();});
34238         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
34239         this.field.setHeight(18);
34240         //this.addSeparator();
34241         this.next = this.addButton({
34242             tooltip: this.nextText,
34243             cls: "x-btn-icon x-grid-page-next",
34244             disabled: true,
34245             handler: this.onClick.createDelegate(this, ["next"])
34246         });
34247         this.last = this.addButton({
34248             tooltip: this.lastText,
34249             cls: "x-btn-icon x-grid-page-last",
34250             disabled: true,
34251             handler: this.onClick.createDelegate(this, ["last"])
34252         });
34253         //this.addSeparator();
34254         this.loading = this.addButton({
34255             tooltip: this.refreshText,
34256             cls: "x-btn-icon x-grid-loading",
34257             handler: this.onClick.createDelegate(this, ["refresh"])
34258         });
34259
34260         if(this.displayInfo){
34261             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
34262         }
34263     },
34264
34265     // private
34266     updateInfo : function(){
34267         if(this.displayEl){
34268             var count = this.ds.getCount();
34269             var msg = count == 0 ?
34270                 this.emptyMsg :
34271                 String.format(
34272                     this.displayMsg,
34273                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
34274                 );
34275             this.displayEl.update(msg);
34276         }
34277     },
34278
34279     // private
34280     onLoad : function(ds, r, o){
34281        this.cursor = o.params ? o.params.start : 0;
34282        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
34283
34284        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
34285        this.field.dom.value = ap;
34286        this.first.setDisabled(ap == 1);
34287        this.prev.setDisabled(ap == 1);
34288        this.next.setDisabled(ap == ps);
34289        this.last.setDisabled(ap == ps);
34290        this.loading.enable();
34291        this.updateInfo();
34292     },
34293
34294     // private
34295     getPageData : function(){
34296         var total = this.ds.getTotalCount();
34297         return {
34298             total : total,
34299             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
34300             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
34301         };
34302     },
34303
34304     // private
34305     onLoadError : function(){
34306         this.loading.enable();
34307     },
34308
34309     // private
34310     onPagingKeydown : function(e){
34311         var k = e.getKey();
34312         var d = this.getPageData();
34313         if(k == e.RETURN){
34314             var v = this.field.dom.value, pageNum;
34315             if(!v || isNaN(pageNum = parseInt(v, 10))){
34316                 this.field.dom.value = d.activePage;
34317                 return;
34318             }
34319             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
34320             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
34321             e.stopEvent();
34322         }
34323         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))
34324         {
34325           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
34326           this.field.dom.value = pageNum;
34327           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
34328           e.stopEvent();
34329         }
34330         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
34331         {
34332           var v = this.field.dom.value, pageNum; 
34333           var increment = (e.shiftKey) ? 10 : 1;
34334           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
34335             increment *= -1;
34336           }
34337           if(!v || isNaN(pageNum = parseInt(v, 10))) {
34338             this.field.dom.value = d.activePage;
34339             return;
34340           }
34341           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
34342           {
34343             this.field.dom.value = parseInt(v, 10) + increment;
34344             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
34345             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
34346           }
34347           e.stopEvent();
34348         }
34349     },
34350
34351     // private
34352     beforeLoad : function(){
34353         if(this.loading){
34354             this.loading.disable();
34355         }
34356     },
34357     /**
34358      * event that occurs when you click on the navigation buttons - can be used to trigger load of a grid.
34359      * @param {String} which (first|prev|next|last|refresh)  which button to press.
34360      *
34361      */
34362     // private
34363     onClick : function(which){
34364         var ds = this.ds;
34365         switch(which){
34366             case "first":
34367                 ds.load({params:{start: 0, limit: this.pageSize}});
34368             break;
34369             case "prev":
34370                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
34371             break;
34372             case "next":
34373                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
34374             break;
34375             case "last":
34376                 var total = ds.getTotalCount();
34377                 var extra = total % this.pageSize;
34378                 var lastStart = extra ? (total - extra) : total-this.pageSize;
34379                 ds.load({params:{start: lastStart, limit: this.pageSize}});
34380             break;
34381             case "refresh":
34382                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
34383             break;
34384         }
34385     },
34386
34387     /**
34388      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
34389      * @param {Roo.data.Store} store The data store to unbind
34390      */
34391     unbind : function(ds){
34392         ds.un("beforeload", this.beforeLoad, this);
34393         ds.un("load", this.onLoad, this);
34394         ds.un("loadexception", this.onLoadError, this);
34395         ds.un("remove", this.updateInfo, this);
34396         ds.un("add", this.updateInfo, this);
34397         this.ds = undefined;
34398     },
34399
34400     /**
34401      * Binds the paging toolbar to the specified {@link Roo.data.Store}
34402      * @param {Roo.data.Store} store The data store to bind
34403      */
34404     bind : function(ds){
34405         ds.on("beforeload", this.beforeLoad, this);
34406         ds.on("load", this.onLoad, this);
34407         ds.on("loadexception", this.onLoadError, this);
34408         ds.on("remove", this.updateInfo, this);
34409         ds.on("add", this.updateInfo, this);
34410         this.ds = ds;
34411     }
34412 });/*
34413  * Based on:
34414  * Ext JS Library 1.1.1
34415  * Copyright(c) 2006-2007, Ext JS, LLC.
34416  *
34417  * Originally Released Under LGPL - original licence link has changed is not relivant.
34418  *
34419  * Fork - LGPL
34420  * <script type="text/javascript">
34421  */
34422
34423 /**
34424  * @class Roo.Resizable
34425  * @extends Roo.util.Observable
34426  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
34427  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
34428  * 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
34429  * the element will be wrapped for you automatically.</p>
34430  * <p>Here is the list of valid resize handles:</p>
34431  * <pre>
34432 Value   Description
34433 ------  -------------------
34434  'n'     north
34435  's'     south
34436  'e'     east
34437  'w'     west
34438  'nw'    northwest
34439  'sw'    southwest
34440  'se'    southeast
34441  'ne'    northeast
34442  'hd'    horizontal drag
34443  'all'   all
34444 </pre>
34445  * <p>Here's an example showing the creation of a typical Resizable:</p>
34446  * <pre><code>
34447 var resizer = new Roo.Resizable("element-id", {
34448     handles: 'all',
34449     minWidth: 200,
34450     minHeight: 100,
34451     maxWidth: 500,
34452     maxHeight: 400,
34453     pinned: true
34454 });
34455 resizer.on("resize", myHandler);
34456 </code></pre>
34457  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
34458  * resizer.east.setDisplayed(false);</p>
34459  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
34460  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
34461  * resize operation's new size (defaults to [0, 0])
34462  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
34463  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
34464  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
34465  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
34466  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
34467  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
34468  * @cfg {Number} width The width of the element in pixels (defaults to null)
34469  * @cfg {Number} height The height of the element in pixels (defaults to null)
34470  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
34471  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
34472  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
34473  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
34474  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
34475  * in favor of the handles config option (defaults to false)
34476  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
34477  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
34478  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
34479  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
34480  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
34481  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
34482  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
34483  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
34484  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
34485  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
34486  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
34487  * @constructor
34488  * Create a new resizable component
34489  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
34490  * @param {Object} config configuration options
34491   */
34492 Roo.Resizable = function(el, config)
34493 {
34494     this.el = Roo.get(el);
34495
34496     if(config && config.wrap){
34497         config.resizeChild = this.el;
34498         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
34499         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
34500         this.el.setStyle("overflow", "hidden");
34501         this.el.setPositioning(config.resizeChild.getPositioning());
34502         config.resizeChild.clearPositioning();
34503         if(!config.width || !config.height){
34504             var csize = config.resizeChild.getSize();
34505             this.el.setSize(csize.width, csize.height);
34506         }
34507         if(config.pinned && !config.adjustments){
34508             config.adjustments = "auto";
34509         }
34510     }
34511
34512     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
34513     this.proxy.unselectable();
34514     this.proxy.enableDisplayMode('block');
34515
34516     Roo.apply(this, config);
34517
34518     if(this.pinned){
34519         this.disableTrackOver = true;
34520         this.el.addClass("x-resizable-pinned");
34521     }
34522     // if the element isn't positioned, make it relative
34523     var position = this.el.getStyle("position");
34524     if(position != "absolute" && position != "fixed"){
34525         this.el.setStyle("position", "relative");
34526     }
34527     if(!this.handles){ // no handles passed, must be legacy style
34528         this.handles = 's,e,se';
34529         if(this.multiDirectional){
34530             this.handles += ',n,w';
34531         }
34532     }
34533     if(this.handles == "all"){
34534         this.handles = "n s e w ne nw se sw";
34535     }
34536     var hs = this.handles.split(/\s*?[,;]\s*?| /);
34537     var ps = Roo.Resizable.positions;
34538     for(var i = 0, len = hs.length; i < len; i++){
34539         if(hs[i] && ps[hs[i]]){
34540             var pos = ps[hs[i]];
34541             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
34542         }
34543     }
34544     // legacy
34545     this.corner = this.southeast;
34546     
34547     // updateBox = the box can move..
34548     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
34549         this.updateBox = true;
34550     }
34551
34552     this.activeHandle = null;
34553
34554     if(this.resizeChild){
34555         if(typeof this.resizeChild == "boolean"){
34556             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
34557         }else{
34558             this.resizeChild = Roo.get(this.resizeChild, true);
34559         }
34560     }
34561     
34562     if(this.adjustments == "auto"){
34563         var rc = this.resizeChild;
34564         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
34565         if(rc && (hw || hn)){
34566             rc.position("relative");
34567             rc.setLeft(hw ? hw.el.getWidth() : 0);
34568             rc.setTop(hn ? hn.el.getHeight() : 0);
34569         }
34570         this.adjustments = [
34571             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
34572             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
34573         ];
34574     }
34575
34576     if(this.draggable){
34577         this.dd = this.dynamic ?
34578             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
34579         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
34580     }
34581
34582     // public events
34583     this.addEvents({
34584         /**
34585          * @event beforeresize
34586          * Fired before resize is allowed. Set enabled to false to cancel resize.
34587          * @param {Roo.Resizable} this
34588          * @param {Roo.EventObject} e The mousedown event
34589          */
34590         "beforeresize" : true,
34591         /**
34592          * @event resizing
34593          * Fired a resizing.
34594          * @param {Roo.Resizable} this
34595          * @param {Number} x The new x position
34596          * @param {Number} y The new y position
34597          * @param {Number} w The new w width
34598          * @param {Number} h The new h hight
34599          * @param {Roo.EventObject} e The mouseup event
34600          */
34601         "resizing" : true,
34602         /**
34603          * @event resize
34604          * Fired after a resize.
34605          * @param {Roo.Resizable} this
34606          * @param {Number} width The new width
34607          * @param {Number} height The new height
34608          * @param {Roo.EventObject} e The mouseup event
34609          */
34610         "resize" : true
34611     });
34612
34613     if(this.width !== null && this.height !== null){
34614         this.resizeTo(this.width, this.height);
34615     }else{
34616         this.updateChildSize();
34617     }
34618     if(Roo.isIE){
34619         this.el.dom.style.zoom = 1;
34620     }
34621     Roo.Resizable.superclass.constructor.call(this);
34622 };
34623
34624 Roo.extend(Roo.Resizable, Roo.util.Observable, {
34625         resizeChild : false,
34626         adjustments : [0, 0],
34627         minWidth : 5,
34628         minHeight : 5,
34629         maxWidth : 10000,
34630         maxHeight : 10000,
34631         enabled : true,
34632         animate : false,
34633         duration : .35,
34634         dynamic : false,
34635         handles : false,
34636         multiDirectional : false,
34637         disableTrackOver : false,
34638         easing : 'easeOutStrong',
34639         widthIncrement : 0,
34640         heightIncrement : 0,
34641         pinned : false,
34642         width : null,
34643         height : null,
34644         preserveRatio : false,
34645         transparent: false,
34646         minX: 0,
34647         minY: 0,
34648         draggable: false,
34649
34650         /**
34651          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
34652          */
34653         constrainTo: undefined,
34654         /**
34655          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
34656          */
34657         resizeRegion: undefined,
34658
34659
34660     /**
34661      * Perform a manual resize
34662      * @param {Number} width
34663      * @param {Number} height
34664      */
34665     resizeTo : function(width, height){
34666         this.el.setSize(width, height);
34667         this.updateChildSize();
34668         this.fireEvent("resize", this, width, height, null);
34669     },
34670
34671     // private
34672     startSizing : function(e, handle){
34673         this.fireEvent("beforeresize", this, e);
34674         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
34675
34676             if(!this.overlay){
34677                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
34678                 this.overlay.unselectable();
34679                 this.overlay.enableDisplayMode("block");
34680                 this.overlay.on("mousemove", this.onMouseMove, this);
34681                 this.overlay.on("mouseup", this.onMouseUp, this);
34682             }
34683             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
34684
34685             this.resizing = true;
34686             this.startBox = this.el.getBox();
34687             this.startPoint = e.getXY();
34688             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
34689                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
34690
34691             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34692             this.overlay.show();
34693
34694             if(this.constrainTo) {
34695                 var ct = Roo.get(this.constrainTo);
34696                 this.resizeRegion = ct.getRegion().adjust(
34697                     ct.getFrameWidth('t'),
34698                     ct.getFrameWidth('l'),
34699                     -ct.getFrameWidth('b'),
34700                     -ct.getFrameWidth('r')
34701                 );
34702             }
34703
34704             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
34705             this.proxy.show();
34706             this.proxy.setBox(this.startBox);
34707             if(!this.dynamic){
34708                 this.proxy.setStyle('visibility', 'visible');
34709             }
34710         }
34711     },
34712
34713     // private
34714     onMouseDown : function(handle, e){
34715         if(this.enabled){
34716             e.stopEvent();
34717             this.activeHandle = handle;
34718             this.startSizing(e, handle);
34719         }
34720     },
34721
34722     // private
34723     onMouseUp : function(e){
34724         var size = this.resizeElement();
34725         this.resizing = false;
34726         this.handleOut();
34727         this.overlay.hide();
34728         this.proxy.hide();
34729         this.fireEvent("resize", this, size.width, size.height, e);
34730     },
34731
34732     // private
34733     updateChildSize : function(){
34734         
34735         if(this.resizeChild){
34736             var el = this.el;
34737             var child = this.resizeChild;
34738             var adj = this.adjustments;
34739             if(el.dom.offsetWidth){
34740                 var b = el.getSize(true);
34741                 child.setSize(b.width+adj[0], b.height+adj[1]);
34742             }
34743             // Second call here for IE
34744             // The first call enables instant resizing and
34745             // the second call corrects scroll bars if they
34746             // exist
34747             if(Roo.isIE){
34748                 setTimeout(function(){
34749                     if(el.dom.offsetWidth){
34750                         var b = el.getSize(true);
34751                         child.setSize(b.width+adj[0], b.height+adj[1]);
34752                     }
34753                 }, 10);
34754             }
34755         }
34756     },
34757
34758     // private
34759     snap : function(value, inc, min){
34760         if(!inc || !value) {
34761             return value;
34762         }
34763         var newValue = value;
34764         var m = value % inc;
34765         if(m > 0){
34766             if(m > (inc/2)){
34767                 newValue = value + (inc-m);
34768             }else{
34769                 newValue = value - m;
34770             }
34771         }
34772         return Math.max(min, newValue);
34773     },
34774
34775     // private
34776     resizeElement : function(){
34777         var box = this.proxy.getBox();
34778         if(this.updateBox){
34779             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
34780         }else{
34781             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
34782         }
34783         this.updateChildSize();
34784         if(!this.dynamic){
34785             this.proxy.hide();
34786         }
34787         return box;
34788     },
34789
34790     // private
34791     constrain : function(v, diff, m, mx){
34792         if(v - diff < m){
34793             diff = v - m;
34794         }else if(v - diff > mx){
34795             diff = mx - v;
34796         }
34797         return diff;
34798     },
34799
34800     // private
34801     onMouseMove : function(e){
34802         
34803         if(this.enabled){
34804             try{// try catch so if something goes wrong the user doesn't get hung
34805
34806             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
34807                 return;
34808             }
34809
34810             //var curXY = this.startPoint;
34811             var curSize = this.curSize || this.startBox;
34812             var x = this.startBox.x, y = this.startBox.y;
34813             var ox = x, oy = y;
34814             var w = curSize.width, h = curSize.height;
34815             var ow = w, oh = h;
34816             var mw = this.minWidth, mh = this.minHeight;
34817             var mxw = this.maxWidth, mxh = this.maxHeight;
34818             var wi = this.widthIncrement;
34819             var hi = this.heightIncrement;
34820
34821             var eventXY = e.getXY();
34822             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
34823             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
34824
34825             var pos = this.activeHandle.position;
34826
34827             switch(pos){
34828                 case "east":
34829                     w += diffX;
34830                     w = Math.min(Math.max(mw, w), mxw);
34831                     break;
34832              
34833                 case "south":
34834                     h += diffY;
34835                     h = Math.min(Math.max(mh, h), mxh);
34836                     break;
34837                 case "southeast":
34838                     w += diffX;
34839                     h += diffY;
34840                     w = Math.min(Math.max(mw, w), mxw);
34841                     h = Math.min(Math.max(mh, h), mxh);
34842                     break;
34843                 case "north":
34844                     diffY = this.constrain(h, diffY, mh, mxh);
34845                     y += diffY;
34846                     h -= diffY;
34847                     break;
34848                 case "hdrag":
34849                     
34850                     if (wi) {
34851                         var adiffX = Math.abs(diffX);
34852                         var sub = (adiffX % wi); // how much 
34853                         if (sub > (wi/2)) { // far enough to snap
34854                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
34855                         } else {
34856                             // remove difference.. 
34857                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
34858                         }
34859                     }
34860                     x += diffX;
34861                     x = Math.max(this.minX, x);
34862                     break;
34863                 case "west":
34864                     diffX = this.constrain(w, diffX, mw, mxw);
34865                     x += diffX;
34866                     w -= diffX;
34867                     break;
34868                 case "northeast":
34869                     w += diffX;
34870                     w = Math.min(Math.max(mw, w), mxw);
34871                     diffY = this.constrain(h, diffY, mh, mxh);
34872                     y += diffY;
34873                     h -= diffY;
34874                     break;
34875                 case "northwest":
34876                     diffX = this.constrain(w, diffX, mw, mxw);
34877                     diffY = this.constrain(h, diffY, mh, mxh);
34878                     y += diffY;
34879                     h -= diffY;
34880                     x += diffX;
34881                     w -= diffX;
34882                     break;
34883                case "southwest":
34884                     diffX = this.constrain(w, diffX, mw, mxw);
34885                     h += diffY;
34886                     h = Math.min(Math.max(mh, h), mxh);
34887                     x += diffX;
34888                     w -= diffX;
34889                     break;
34890             }
34891
34892             var sw = this.snap(w, wi, mw);
34893             var sh = this.snap(h, hi, mh);
34894             if(sw != w || sh != h){
34895                 switch(pos){
34896                     case "northeast":
34897                         y -= sh - h;
34898                     break;
34899                     case "north":
34900                         y -= sh - h;
34901                         break;
34902                     case "southwest":
34903                         x -= sw - w;
34904                     break;
34905                     case "west":
34906                         x -= sw - w;
34907                         break;
34908                     case "northwest":
34909                         x -= sw - w;
34910                         y -= sh - h;
34911                     break;
34912                 }
34913                 w = sw;
34914                 h = sh;
34915             }
34916
34917             if(this.preserveRatio){
34918                 switch(pos){
34919                     case "southeast":
34920                     case "east":
34921                         h = oh * (w/ow);
34922                         h = Math.min(Math.max(mh, h), mxh);
34923                         w = ow * (h/oh);
34924                        break;
34925                     case "south":
34926                         w = ow * (h/oh);
34927                         w = Math.min(Math.max(mw, w), mxw);
34928                         h = oh * (w/ow);
34929                         break;
34930                     case "northeast":
34931                         w = ow * (h/oh);
34932                         w = Math.min(Math.max(mw, w), mxw);
34933                         h = oh * (w/ow);
34934                     break;
34935                     case "north":
34936                         var tw = w;
34937                         w = ow * (h/oh);
34938                         w = Math.min(Math.max(mw, w), mxw);
34939                         h = oh * (w/ow);
34940                         x += (tw - w) / 2;
34941                         break;
34942                     case "southwest":
34943                         h = oh * (w/ow);
34944                         h = Math.min(Math.max(mh, h), mxh);
34945                         var tw = w;
34946                         w = ow * (h/oh);
34947                         x += tw - w;
34948                         break;
34949                     case "west":
34950                         var th = h;
34951                         h = oh * (w/ow);
34952                         h = Math.min(Math.max(mh, h), mxh);
34953                         y += (th - h) / 2;
34954                         var tw = w;
34955                         w = ow * (h/oh);
34956                         x += tw - w;
34957                        break;
34958                     case "northwest":
34959                         var tw = w;
34960                         var th = h;
34961                         h = oh * (w/ow);
34962                         h = Math.min(Math.max(mh, h), mxh);
34963                         w = ow * (h/oh);
34964                         y += th - h;
34965                         x += tw - w;
34966                        break;
34967
34968                 }
34969             }
34970             if (pos == 'hdrag') {
34971                 w = ow;
34972             }
34973             this.proxy.setBounds(x, y, w, h);
34974             if(this.dynamic){
34975                 this.resizeElement();
34976             }
34977             }catch(e){}
34978         }
34979         this.fireEvent("resizing", this, x, y, w, h, e);
34980     },
34981
34982     // private
34983     handleOver : function(){
34984         if(this.enabled){
34985             this.el.addClass("x-resizable-over");
34986         }
34987     },
34988
34989     // private
34990     handleOut : function(){
34991         if(!this.resizing){
34992             this.el.removeClass("x-resizable-over");
34993         }
34994     },
34995
34996     /**
34997      * Returns the element this component is bound to.
34998      * @return {Roo.Element}
34999      */
35000     getEl : function(){
35001         return this.el;
35002     },
35003
35004     /**
35005      * Returns the resizeChild element (or null).
35006      * @return {Roo.Element}
35007      */
35008     getResizeChild : function(){
35009         return this.resizeChild;
35010     },
35011     groupHandler : function()
35012     {
35013         
35014     },
35015     /**
35016      * Destroys this resizable. If the element was wrapped and
35017      * removeEl is not true then the element remains.
35018      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
35019      */
35020     destroy : function(removeEl){
35021         this.proxy.remove();
35022         if(this.overlay){
35023             this.overlay.removeAllListeners();
35024             this.overlay.remove();
35025         }
35026         var ps = Roo.Resizable.positions;
35027         for(var k in ps){
35028             if(typeof ps[k] != "function" && this[ps[k]]){
35029                 var h = this[ps[k]];
35030                 h.el.removeAllListeners();
35031                 h.el.remove();
35032             }
35033         }
35034         if(removeEl){
35035             this.el.update("");
35036             this.el.remove();
35037         }
35038     }
35039 });
35040
35041 // private
35042 // hash to map config positions to true positions
35043 Roo.Resizable.positions = {
35044     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
35045     hd: "hdrag"
35046 };
35047
35048 // private
35049 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
35050     if(!this.tpl){
35051         // only initialize the template if resizable is used
35052         var tpl = Roo.DomHelper.createTemplate(
35053             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
35054         );
35055         tpl.compile();
35056         Roo.Resizable.Handle.prototype.tpl = tpl;
35057     }
35058     this.position = pos;
35059     this.rz = rz;
35060     // show north drag fro topdra
35061     var handlepos = pos == 'hdrag' ? 'north' : pos;
35062     
35063     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
35064     if (pos == 'hdrag') {
35065         this.el.setStyle('cursor', 'pointer');
35066     }
35067     this.el.unselectable();
35068     if(transparent){
35069         this.el.setOpacity(0);
35070     }
35071     this.el.on("mousedown", this.onMouseDown, this);
35072     if(!disableTrackOver){
35073         this.el.on("mouseover", this.onMouseOver, this);
35074         this.el.on("mouseout", this.onMouseOut, this);
35075     }
35076 };
35077
35078 // private
35079 Roo.Resizable.Handle.prototype = {
35080     afterResize : function(rz){
35081         Roo.log('after?');
35082         // do nothing
35083     },
35084     // private
35085     onMouseDown : function(e){
35086         this.rz.onMouseDown(this, e);
35087     },
35088     // private
35089     onMouseOver : function(e){
35090         this.rz.handleOver(this, e);
35091     },
35092     // private
35093     onMouseOut : function(e){
35094         this.rz.handleOut(this, e);
35095     }
35096 };/*
35097  * Based on:
35098  * Ext JS Library 1.1.1
35099  * Copyright(c) 2006-2007, Ext JS, LLC.
35100  *
35101  * Originally Released Under LGPL - original licence link has changed is not relivant.
35102  *
35103  * Fork - LGPL
35104  * <script type="text/javascript">
35105  */
35106
35107 /**
35108  * @class Roo.Editor
35109  * @extends Roo.Component
35110  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
35111  * @constructor
35112  * Create a new Editor
35113  * @param {Roo.form.Field} field The Field object (or descendant)
35114  * @param {Object} config The config object
35115  */
35116 Roo.Editor = function(field, config){
35117     Roo.Editor.superclass.constructor.call(this, config);
35118     this.field = field;
35119     this.addEvents({
35120         /**
35121              * @event beforestartedit
35122              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
35123              * false from the handler of this event.
35124              * @param {Editor} this
35125              * @param {Roo.Element} boundEl The underlying element bound to this editor
35126              * @param {Mixed} value The field value being set
35127              */
35128         "beforestartedit" : true,
35129         /**
35130              * @event startedit
35131              * Fires when this editor is displayed
35132              * @param {Roo.Element} boundEl The underlying element bound to this editor
35133              * @param {Mixed} value The starting field value
35134              */
35135         "startedit" : true,
35136         /**
35137              * @event beforecomplete
35138              * Fires after a change has been made to the field, but before the change is reflected in the underlying
35139              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
35140              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
35141              * event will not fire since no edit actually occurred.
35142              * @param {Editor} this
35143              * @param {Mixed} value The current field value
35144              * @param {Mixed} startValue The original field value
35145              */
35146         "beforecomplete" : true,
35147         /**
35148              * @event complete
35149              * Fires after editing is complete and any changed value has been written to the underlying field.
35150              * @param {Editor} this
35151              * @param {Mixed} value The current field value
35152              * @param {Mixed} startValue The original field value
35153              */
35154         "complete" : true,
35155         /**
35156          * @event specialkey
35157          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
35158          * {@link Roo.EventObject#getKey} to determine which key was pressed.
35159          * @param {Roo.form.Field} this
35160          * @param {Roo.EventObject} e The event object
35161          */
35162         "specialkey" : true
35163     });
35164 };
35165
35166 Roo.extend(Roo.Editor, Roo.Component, {
35167     /**
35168      * @cfg {Boolean/String} autosize
35169      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
35170      * or "height" to adopt the height only (defaults to false)
35171      */
35172     /**
35173      * @cfg {Boolean} revertInvalid
35174      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
35175      * validation fails (defaults to true)
35176      */
35177     /**
35178      * @cfg {Boolean} ignoreNoChange
35179      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
35180      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
35181      * will never be ignored.
35182      */
35183     /**
35184      * @cfg {Boolean} hideEl
35185      * False to keep the bound element visible while the editor is displayed (defaults to true)
35186      */
35187     /**
35188      * @cfg {Mixed} value
35189      * The data value of the underlying field (defaults to "")
35190      */
35191     value : "",
35192     /**
35193      * @cfg {String} alignment
35194      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
35195      */
35196     alignment: "c-c?",
35197     /**
35198      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
35199      * for bottom-right shadow (defaults to "frame")
35200      */
35201     shadow : "frame",
35202     /**
35203      * @cfg {Boolean} constrain True to constrain the editor to the viewport
35204      */
35205     constrain : false,
35206     /**
35207      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
35208      */
35209     completeOnEnter : false,
35210     /**
35211      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
35212      */
35213     cancelOnEsc : false,
35214     /**
35215      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
35216      */
35217     updateEl : false,
35218
35219     // private
35220     onRender : function(ct, position){
35221         this.el = new Roo.Layer({
35222             shadow: this.shadow,
35223             cls: "x-editor",
35224             parentEl : ct,
35225             shim : this.shim,
35226             shadowOffset:4,
35227             id: this.id,
35228             constrain: this.constrain
35229         });
35230         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
35231         if(this.field.msgTarget != 'title'){
35232             this.field.msgTarget = 'qtip';
35233         }
35234         this.field.render(this.el);
35235         if(Roo.isGecko){
35236             this.field.el.dom.setAttribute('autocomplete', 'off');
35237         }
35238         this.field.on("specialkey", this.onSpecialKey, this);
35239         if(this.swallowKeys){
35240             this.field.el.swallowEvent(['keydown','keypress']);
35241         }
35242         this.field.show();
35243         this.field.on("blur", this.onBlur, this);
35244         if(this.field.grow){
35245             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
35246         }
35247     },
35248
35249     onSpecialKey : function(field, e)
35250     {
35251         //Roo.log('editor onSpecialKey');
35252         if(this.completeOnEnter && e.getKey() == e.ENTER){
35253             e.stopEvent();
35254             this.completeEdit();
35255             return;
35256         }
35257         // do not fire special key otherwise it might hide close the editor...
35258         if(e.getKey() == e.ENTER){    
35259             return;
35260         }
35261         if(this.cancelOnEsc && e.getKey() == e.ESC){
35262             this.cancelEdit();
35263             return;
35264         } 
35265         this.fireEvent('specialkey', field, e);
35266     
35267     },
35268
35269     /**
35270      * Starts the editing process and shows the editor.
35271      * @param {String/HTMLElement/Element} el The element to edit
35272      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
35273       * to the innerHTML of el.
35274      */
35275     startEdit : function(el, value){
35276         if(this.editing){
35277             this.completeEdit();
35278         }
35279         this.boundEl = Roo.get(el);
35280         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
35281         if(!this.rendered){
35282             this.render(this.parentEl || document.body);
35283         }
35284         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
35285             return;
35286         }
35287         this.startValue = v;
35288         this.field.setValue(v);
35289         if(this.autoSize){
35290             var sz = this.boundEl.getSize();
35291             switch(this.autoSize){
35292                 case "width":
35293                 this.setSize(sz.width,  "");
35294                 break;
35295                 case "height":
35296                 this.setSize("",  sz.height);
35297                 break;
35298                 default:
35299                 this.setSize(sz.width,  sz.height);
35300             }
35301         }
35302         this.el.alignTo(this.boundEl, this.alignment);
35303         this.editing = true;
35304         if(Roo.QuickTips){
35305             Roo.QuickTips.disable();
35306         }
35307         this.show();
35308     },
35309
35310     /**
35311      * Sets the height and width of this editor.
35312      * @param {Number} width The new width
35313      * @param {Number} height The new height
35314      */
35315     setSize : function(w, h){
35316         this.field.setSize(w, h);
35317         if(this.el){
35318             this.el.sync();
35319         }
35320     },
35321
35322     /**
35323      * Realigns the editor to the bound field based on the current alignment config value.
35324      */
35325     realign : function(){
35326         this.el.alignTo(this.boundEl, this.alignment);
35327     },
35328
35329     /**
35330      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
35331      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
35332      */
35333     completeEdit : function(remainVisible){
35334         if(!this.editing){
35335             return;
35336         }
35337         var v = this.getValue();
35338         if(this.revertInvalid !== false && !this.field.isValid()){
35339             v = this.startValue;
35340             this.cancelEdit(true);
35341         }
35342         if(String(v) === String(this.startValue) && this.ignoreNoChange){
35343             this.editing = false;
35344             this.hide();
35345             return;
35346         }
35347         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
35348             this.editing = false;
35349             if(this.updateEl && this.boundEl){
35350                 this.boundEl.update(v);
35351             }
35352             if(remainVisible !== true){
35353                 this.hide();
35354             }
35355             this.fireEvent("complete", this, v, this.startValue);
35356         }
35357     },
35358
35359     // private
35360     onShow : function(){
35361         this.el.show();
35362         if(this.hideEl !== false){
35363             this.boundEl.hide();
35364         }
35365         this.field.show();
35366         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
35367             this.fixIEFocus = true;
35368             this.deferredFocus.defer(50, this);
35369         }else{
35370             this.field.focus();
35371         }
35372         this.fireEvent("startedit", this.boundEl, this.startValue);
35373     },
35374
35375     deferredFocus : function(){
35376         if(this.editing){
35377             this.field.focus();
35378         }
35379     },
35380
35381     /**
35382      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
35383      * reverted to the original starting value.
35384      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
35385      * cancel (defaults to false)
35386      */
35387     cancelEdit : function(remainVisible){
35388         if(this.editing){
35389             this.setValue(this.startValue);
35390             if(remainVisible !== true){
35391                 this.hide();
35392             }
35393         }
35394     },
35395
35396     // private
35397     onBlur : function(){
35398         if(this.allowBlur !== true && this.editing){
35399             this.completeEdit();
35400         }
35401     },
35402
35403     // private
35404     onHide : function(){
35405         if(this.editing){
35406             this.completeEdit();
35407             return;
35408         }
35409         this.field.blur();
35410         if(this.field.collapse){
35411             this.field.collapse();
35412         }
35413         this.el.hide();
35414         if(this.hideEl !== false){
35415             this.boundEl.show();
35416         }
35417         if(Roo.QuickTips){
35418             Roo.QuickTips.enable();
35419         }
35420     },
35421
35422     /**
35423      * Sets the data value of the editor
35424      * @param {Mixed} value Any valid value supported by the underlying field
35425      */
35426     setValue : function(v){
35427         this.field.setValue(v);
35428     },
35429
35430     /**
35431      * Gets the data value of the editor
35432      * @return {Mixed} The data value
35433      */
35434     getValue : function(){
35435         return this.field.getValue();
35436     }
35437 });/*
35438  * Based on:
35439  * Ext JS Library 1.1.1
35440  * Copyright(c) 2006-2007, Ext JS, LLC.
35441  *
35442  * Originally Released Under LGPL - original licence link has changed is not relivant.
35443  *
35444  * Fork - LGPL
35445  * <script type="text/javascript">
35446  */
35447  
35448 /**
35449  * @class Roo.BasicDialog
35450  * @extends Roo.util.Observable
35451  * @parent none builder
35452  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
35453  * <pre><code>
35454 var dlg = new Roo.BasicDialog("my-dlg", {
35455     height: 200,
35456     width: 300,
35457     minHeight: 100,
35458     minWidth: 150,
35459     modal: true,
35460     proxyDrag: true,
35461     shadow: true
35462 });
35463 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
35464 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
35465 dlg.addButton('Cancel', dlg.hide, dlg);
35466 dlg.show();
35467 </code></pre>
35468   <b>A Dialog should always be a direct child of the body element.</b>
35469  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
35470  * @cfg {String} title Default text to display in the title bar (defaults to null)
35471  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
35472  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
35473  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
35474  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
35475  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
35476  * (defaults to null with no animation)
35477  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
35478  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
35479  * property for valid values (defaults to 'all')
35480  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
35481  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
35482  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
35483  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
35484  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
35485  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
35486  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
35487  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
35488  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
35489  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
35490  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
35491  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
35492  * draggable = true (defaults to false)
35493  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
35494  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
35495  * shadow (defaults to false)
35496  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
35497  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
35498  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
35499  * @cfg {Array} buttons Array of buttons
35500  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
35501  * @constructor
35502  * Create a new BasicDialog.
35503  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
35504  * @param {Object} config Configuration options
35505  */
35506 Roo.BasicDialog = function(el, config){
35507     this.el = Roo.get(el);
35508     var dh = Roo.DomHelper;
35509     if(!this.el && config && config.autoCreate){
35510         if(typeof config.autoCreate == "object"){
35511             if(!config.autoCreate.id){
35512                 config.autoCreate.id = el;
35513             }
35514             this.el = dh.append(document.body,
35515                         config.autoCreate, true);
35516         }else{
35517             this.el = dh.append(document.body,
35518                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
35519         }
35520     }
35521     el = this.el;
35522     el.setDisplayed(true);
35523     el.hide = this.hideAction;
35524     this.id = el.id;
35525     el.addClass("x-dlg");
35526
35527     Roo.apply(this, config);
35528
35529     this.proxy = el.createProxy("x-dlg-proxy");
35530     this.proxy.hide = this.hideAction;
35531     this.proxy.setOpacity(.5);
35532     this.proxy.hide();
35533
35534     if(config.width){
35535         el.setWidth(config.width);
35536     }
35537     if(config.height){
35538         el.setHeight(config.height);
35539     }
35540     this.size = el.getSize();
35541     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
35542         this.xy = [config.x,config.y];
35543     }else{
35544         this.xy = el.getCenterXY(true);
35545     }
35546     /** The header element @type Roo.Element */
35547     this.header = el.child("> .x-dlg-hd");
35548     /** The body element @type Roo.Element */
35549     this.body = el.child("> .x-dlg-bd");
35550     /** The footer element @type Roo.Element */
35551     this.footer = el.child("> .x-dlg-ft");
35552
35553     if(!this.header){
35554         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
35555     }
35556     if(!this.body){
35557         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
35558     }
35559
35560     this.header.unselectable();
35561     if(this.title){
35562         this.header.update(this.title);
35563     }
35564     // this element allows the dialog to be focused for keyboard event
35565     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
35566     this.focusEl.swallowEvent("click", true);
35567
35568     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
35569
35570     // wrap the body and footer for special rendering
35571     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
35572     if(this.footer){
35573         this.bwrap.dom.appendChild(this.footer.dom);
35574     }
35575
35576     this.bg = this.el.createChild({
35577         tag: "div", cls:"x-dlg-bg",
35578         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
35579     });
35580     this.centerBg = this.bg.child("div.x-dlg-bg-center");
35581
35582
35583     if(this.autoScroll !== false && !this.autoTabs){
35584         this.body.setStyle("overflow", "auto");
35585     }
35586
35587     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
35588
35589     if(this.closable !== false){
35590         this.el.addClass("x-dlg-closable");
35591         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
35592         this.close.on("click", this.closeClick, this);
35593         this.close.addClassOnOver("x-dlg-close-over");
35594     }
35595     if(this.collapsible !== false){
35596         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
35597         this.collapseBtn.on("click", this.collapseClick, this);
35598         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
35599         this.header.on("dblclick", this.collapseClick, this);
35600     }
35601     if(this.resizable !== false){
35602         this.el.addClass("x-dlg-resizable");
35603         this.resizer = new Roo.Resizable(el, {
35604             minWidth: this.minWidth || 80,
35605             minHeight:this.minHeight || 80,
35606             handles: this.resizeHandles || "all",
35607             pinned: true
35608         });
35609         this.resizer.on("beforeresize", this.beforeResize, this);
35610         this.resizer.on("resize", this.onResize, this);
35611     }
35612     if(this.draggable !== false){
35613         el.addClass("x-dlg-draggable");
35614         if (!this.proxyDrag) {
35615             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
35616         }
35617         else {
35618             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
35619         }
35620         dd.setHandleElId(this.header.id);
35621         dd.endDrag = this.endMove.createDelegate(this);
35622         dd.startDrag = this.startMove.createDelegate(this);
35623         dd.onDrag = this.onDrag.createDelegate(this);
35624         dd.scroll = false;
35625         this.dd = dd;
35626     }
35627     if(this.modal){
35628         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
35629         this.mask.enableDisplayMode("block");
35630         this.mask.hide();
35631         this.el.addClass("x-dlg-modal");
35632     }
35633     if(this.shadow){
35634         this.shadow = new Roo.Shadow({
35635             mode : typeof this.shadow == "string" ? this.shadow : "sides",
35636             offset : this.shadowOffset
35637         });
35638     }else{
35639         this.shadowOffset = 0;
35640     }
35641     if(Roo.useShims && this.shim !== false){
35642         this.shim = this.el.createShim();
35643         this.shim.hide = this.hideAction;
35644         this.shim.hide();
35645     }else{
35646         this.shim = false;
35647     }
35648     if(this.autoTabs){
35649         this.initTabs();
35650     }
35651     if (this.buttons) { 
35652         var bts= this.buttons;
35653         this.buttons = [];
35654         Roo.each(bts, function(b) {
35655             this.addButton(b);
35656         }, this);
35657     }
35658     
35659     
35660     this.addEvents({
35661         /**
35662          * @event keydown
35663          * Fires when a key is pressed
35664          * @param {Roo.BasicDialog} this
35665          * @param {Roo.EventObject} e
35666          */
35667         "keydown" : true,
35668         /**
35669          * @event move
35670          * Fires when this dialog is moved by the user.
35671          * @param {Roo.BasicDialog} this
35672          * @param {Number} x The new page X
35673          * @param {Number} y The new page Y
35674          */
35675         "move" : true,
35676         /**
35677          * @event resize
35678          * Fires when this dialog is resized by the user.
35679          * @param {Roo.BasicDialog} this
35680          * @param {Number} width The new width
35681          * @param {Number} height The new height
35682          */
35683         "resize" : true,
35684         /**
35685          * @event beforehide
35686          * Fires before this dialog is hidden.
35687          * @param {Roo.BasicDialog} this
35688          */
35689         "beforehide" : true,
35690         /**
35691          * @event hide
35692          * Fires when this dialog is hidden.
35693          * @param {Roo.BasicDialog} this
35694          */
35695         "hide" : true,
35696         /**
35697          * @event beforeshow
35698          * Fires before this dialog is shown.
35699          * @param {Roo.BasicDialog} this
35700          */
35701         "beforeshow" : true,
35702         /**
35703          * @event show
35704          * Fires when this dialog is shown.
35705          * @param {Roo.BasicDialog} this
35706          */
35707         "show" : true
35708     });
35709     el.on("keydown", this.onKeyDown, this);
35710     el.on("mousedown", this.toFront, this);
35711     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
35712     this.el.hide();
35713     Roo.DialogManager.register(this);
35714     Roo.BasicDialog.superclass.constructor.call(this);
35715 };
35716
35717 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
35718     shadowOffset: Roo.isIE ? 6 : 5,
35719     minHeight: 80,
35720     minWidth: 200,
35721     minButtonWidth: 75,
35722     defaultButton: null,
35723     buttonAlign: "right",
35724     tabTag: 'div',
35725     firstShow: true,
35726
35727     /**
35728      * Sets the dialog title text
35729      * @param {String} text The title text to display
35730      * @return {Roo.BasicDialog} this
35731      */
35732     setTitle : function(text){
35733         this.header.update(text);
35734         return this;
35735     },
35736
35737     // private
35738     closeClick : function(){
35739         this.hide();
35740     },
35741
35742     // private
35743     collapseClick : function(){
35744         this[this.collapsed ? "expand" : "collapse"]();
35745     },
35746
35747     /**
35748      * Collapses the dialog to its minimized state (only the title bar is visible).
35749      * Equivalent to the user clicking the collapse dialog button.
35750      */
35751     collapse : function(){
35752         if(!this.collapsed){
35753             this.collapsed = true;
35754             this.el.addClass("x-dlg-collapsed");
35755             this.restoreHeight = this.el.getHeight();
35756             this.resizeTo(this.el.getWidth(), this.header.getHeight());
35757         }
35758     },
35759
35760     /**
35761      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
35762      * clicking the expand dialog button.
35763      */
35764     expand : function(){
35765         if(this.collapsed){
35766             this.collapsed = false;
35767             this.el.removeClass("x-dlg-collapsed");
35768             this.resizeTo(this.el.getWidth(), this.restoreHeight);
35769         }
35770     },
35771
35772     /**
35773      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
35774      * @return {Roo.panel.Tab} The tabs component
35775      */
35776     initTabs : function(){
35777         var tabs = this.getTabs();
35778         while(tabs.getTab(0)){
35779             tabs.removeTab(0);
35780         }
35781         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
35782             var dom = el.dom;
35783             tabs.addTab(Roo.id(dom), dom.title);
35784             dom.title = "";
35785         });
35786         tabs.activate(0);
35787         return tabs;
35788     },
35789
35790     // private
35791     beforeResize : function(){
35792         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
35793     },
35794
35795     // private
35796     onResize : function(){
35797         this.refreshSize();
35798         this.syncBodyHeight();
35799         this.adjustAssets();
35800         this.focus();
35801         this.fireEvent("resize", this, this.size.width, this.size.height);
35802     },
35803
35804     // private
35805     onKeyDown : function(e){
35806         if(this.isVisible()){
35807             this.fireEvent("keydown", this, e);
35808         }
35809     },
35810
35811     /**
35812      * Resizes the dialog.
35813      * @param {Number} width
35814      * @param {Number} height
35815      * @return {Roo.BasicDialog} this
35816      */
35817     resizeTo : function(width, height){
35818         this.el.setSize(width, height);
35819         this.size = {width: width, height: height};
35820         this.syncBodyHeight();
35821         if(this.fixedcenter){
35822             this.center();
35823         }
35824         if(this.isVisible()){
35825             this.constrainXY();
35826             this.adjustAssets();
35827         }
35828         this.fireEvent("resize", this, width, height);
35829         return this;
35830     },
35831
35832
35833     /**
35834      * Resizes the dialog to fit the specified content size.
35835      * @param {Number} width
35836      * @param {Number} height
35837      * @return {Roo.BasicDialog} this
35838      */
35839     setContentSize : function(w, h){
35840         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
35841         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
35842         //if(!this.el.isBorderBox()){
35843             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
35844             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
35845         //}
35846         if(this.tabs){
35847             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
35848             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
35849         }
35850         this.resizeTo(w, h);
35851         return this;
35852     },
35853
35854     /**
35855      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
35856      * executed in response to a particular key being pressed while the dialog is active.
35857      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
35858      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
35859      * @param {Function} fn The function to call
35860      * @param {Object} scope (optional) The scope of the function
35861      * @return {Roo.BasicDialog} this
35862      */
35863     addKeyListener : function(key, fn, scope){
35864         var keyCode, shift, ctrl, alt;
35865         if(typeof key == "object" && !(key instanceof Array)){
35866             keyCode = key["key"];
35867             shift = key["shift"];
35868             ctrl = key["ctrl"];
35869             alt = key["alt"];
35870         }else{
35871             keyCode = key;
35872         }
35873         var handler = function(dlg, e){
35874             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
35875                 var k = e.getKey();
35876                 if(keyCode instanceof Array){
35877                     for(var i = 0, len = keyCode.length; i < len; i++){
35878                         if(keyCode[i] == k){
35879                           fn.call(scope || window, dlg, k, e);
35880                           return;
35881                         }
35882                     }
35883                 }else{
35884                     if(k == keyCode){
35885                         fn.call(scope || window, dlg, k, e);
35886                     }
35887                 }
35888             }
35889         };
35890         this.on("keydown", handler);
35891         return this;
35892     },
35893
35894     /**
35895      * Returns the panel.Tab component (creates it if it doesn't exist).
35896      * Note: If you wish to simply check for the existence of tabs without creating them,
35897      * check for a null 'tabs' property.
35898      * @return {Roo.panel.Tab} The tabs component
35899      */
35900     getTabs : function(){
35901         if(!this.tabs){
35902             this.el.addClass("x-dlg-auto-tabs");
35903             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
35904             this.tabs = new Roo.panel.Tab(this.body.dom, this.tabPosition == "bottom");
35905         }
35906         return this.tabs;
35907     },
35908
35909     /**
35910      * Adds a button to the footer section of the dialog.
35911      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
35912      * object or a valid Roo.DomHelper element config
35913      * @param {Function} handler The function called when the button is clicked
35914      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
35915      * @return {Roo.Button} The new button
35916      */
35917     addButton : function(config, handler, scope){
35918         var dh = Roo.DomHelper;
35919         if(!this.footer){
35920             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
35921         }
35922         if(!this.btnContainer){
35923             var tb = this.footer.createChild({
35924
35925                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
35926                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
35927             }, null, true);
35928             this.btnContainer = tb.firstChild.firstChild.firstChild;
35929         }
35930         var bconfig = {
35931             handler: handler,
35932             scope: scope,
35933             minWidth: this.minButtonWidth,
35934             hideParent:true
35935         };
35936         if(typeof config == "string"){
35937             bconfig.text = config;
35938         }else{
35939             if(config.tag){
35940                 bconfig.dhconfig = config;
35941             }else{
35942                 Roo.apply(bconfig, config);
35943             }
35944         }
35945         var fc = false;
35946         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
35947             bconfig.position = Math.max(0, bconfig.position);
35948             fc = this.btnContainer.childNodes[bconfig.position];
35949         }
35950          
35951         var btn = new Roo.Button(
35952             fc ? 
35953                 this.btnContainer.insertBefore(document.createElement("td"),fc)
35954                 : this.btnContainer.appendChild(document.createElement("td")),
35955             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
35956             bconfig
35957         );
35958         this.syncBodyHeight();
35959         if(!this.buttons){
35960             /**
35961              * Array of all the buttons that have been added to this dialog via addButton
35962              * @type Array
35963              */
35964             this.buttons = [];
35965         }
35966         this.buttons.push(btn);
35967         return btn;
35968     },
35969
35970     /**
35971      * Sets the default button to be focused when the dialog is displayed.
35972      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
35973      * @return {Roo.BasicDialog} this
35974      */
35975     setDefaultButton : function(btn){
35976         this.defaultButton = btn;
35977         return this;
35978     },
35979
35980     // private
35981     getHeaderFooterHeight : function(safe){
35982         var height = 0;
35983         if(this.header){
35984            height += this.header.getHeight();
35985         }
35986         if(this.footer){
35987            var fm = this.footer.getMargins();
35988             height += (this.footer.getHeight()+fm.top+fm.bottom);
35989         }
35990         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
35991         height += this.centerBg.getPadding("tb");
35992         return height;
35993     },
35994
35995     // private
35996     syncBodyHeight : function()
35997     {
35998         var bd = this.body, // the text
35999             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
36000             bw = this.bwrap;
36001         var height = this.size.height - this.getHeaderFooterHeight(false);
36002         bd.setHeight(height-bd.getMargins("tb"));
36003         var hh = this.header.getHeight();
36004         var h = this.size.height-hh;
36005         cb.setHeight(h);
36006         
36007         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
36008         bw.setHeight(h-cb.getPadding("tb"));
36009         
36010         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
36011         bd.setWidth(bw.getWidth(true));
36012         if(this.tabs){
36013             this.tabs.syncHeight();
36014             if(Roo.isIE){
36015                 this.tabs.el.repaint();
36016             }
36017         }
36018     },
36019
36020     /**
36021      * Restores the previous state of the dialog if Roo.state is configured.
36022      * @return {Roo.BasicDialog} this
36023      */
36024     restoreState : function(){
36025         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
36026         if(box && box.width){
36027             this.xy = [box.x, box.y];
36028             this.resizeTo(box.width, box.height);
36029         }
36030         return this;
36031     },
36032
36033     // private
36034     beforeShow : function(){
36035         this.expand();
36036         if(this.fixedcenter){
36037             this.xy = this.el.getCenterXY(true);
36038         }
36039         if(this.modal){
36040             Roo.get(document.body).addClass("x-body-masked");
36041             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36042             this.mask.show();
36043         }
36044         this.constrainXY();
36045     },
36046
36047     // private
36048     animShow : function(){
36049         var b = Roo.get(this.animateTarget).getBox();
36050         this.proxy.setSize(b.width, b.height);
36051         this.proxy.setLocation(b.x, b.y);
36052         this.proxy.show();
36053         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
36054                     true, .35, this.showEl.createDelegate(this));
36055     },
36056
36057     /**
36058      * Shows the dialog.
36059      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
36060      * @return {Roo.BasicDialog} this
36061      */
36062     show : function(animateTarget){
36063         if (this.fireEvent("beforeshow", this) === false){
36064             return;
36065         }
36066         if(this.syncHeightBeforeShow){
36067             this.syncBodyHeight();
36068         }else if(this.firstShow){
36069             this.firstShow = false;
36070             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
36071         }
36072         this.animateTarget = animateTarget || this.animateTarget;
36073         if(!this.el.isVisible()){
36074             this.beforeShow();
36075             if(this.animateTarget && Roo.get(this.animateTarget)){
36076                 this.animShow();
36077             }else{
36078                 this.showEl();
36079             }
36080         }
36081         return this;
36082     },
36083
36084     // private
36085     showEl : function(){
36086         this.proxy.hide();
36087         this.el.setXY(this.xy);
36088         this.el.show();
36089         this.adjustAssets(true);
36090         this.toFront();
36091         this.focus();
36092         // IE peekaboo bug - fix found by Dave Fenwick
36093         if(Roo.isIE){
36094             this.el.repaint();
36095         }
36096         this.fireEvent("show", this);
36097     },
36098
36099     /**
36100      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
36101      * dialog itself will receive focus.
36102      */
36103     focus : function(){
36104         if(this.defaultButton){
36105             this.defaultButton.focus();
36106         }else{
36107             this.focusEl.focus();
36108         }
36109     },
36110
36111     // private
36112     constrainXY : function(){
36113         if(this.constraintoviewport !== false){
36114             if(!this.viewSize){
36115                 if(this.container){
36116                     var s = this.container.getSize();
36117                     this.viewSize = [s.width, s.height];
36118                 }else{
36119                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
36120                 }
36121             }
36122             var s = Roo.get(this.container||document).getScroll();
36123
36124             var x = this.xy[0], y = this.xy[1];
36125             var w = this.size.width, h = this.size.height;
36126             var vw = this.viewSize[0], vh = this.viewSize[1];
36127             // only move it if it needs it
36128             var moved = false;
36129             // first validate right/bottom
36130             if(x + w > vw+s.left){
36131                 x = vw - w;
36132                 moved = true;
36133             }
36134             if(y + h > vh+s.top){
36135                 y = vh - h;
36136                 moved = true;
36137             }
36138             // then make sure top/left isn't negative
36139             if(x < s.left){
36140                 x = s.left;
36141                 moved = true;
36142             }
36143             if(y < s.top){
36144                 y = s.top;
36145                 moved = true;
36146             }
36147             if(moved){
36148                 // cache xy
36149                 this.xy = [x, y];
36150                 if(this.isVisible()){
36151                     this.el.setLocation(x, y);
36152                     this.adjustAssets();
36153                 }
36154             }
36155         }
36156     },
36157
36158     // private
36159     onDrag : function(){
36160         if(!this.proxyDrag){
36161             this.xy = this.el.getXY();
36162             this.adjustAssets();
36163         }
36164     },
36165
36166     // private
36167     adjustAssets : function(doShow){
36168         var x = this.xy[0], y = this.xy[1];
36169         var w = this.size.width, h = this.size.height;
36170         if(doShow === true){
36171             if(this.shadow){
36172                 this.shadow.show(this.el);
36173             }
36174             if(this.shim){
36175                 this.shim.show();
36176             }
36177         }
36178         if(this.shadow && this.shadow.isVisible()){
36179             this.shadow.show(this.el);
36180         }
36181         if(this.shim && this.shim.isVisible()){
36182             this.shim.setBounds(x, y, w, h);
36183         }
36184     },
36185
36186     // private
36187     adjustViewport : function(w, h){
36188         if(!w || !h){
36189             w = Roo.lib.Dom.getViewWidth();
36190             h = Roo.lib.Dom.getViewHeight();
36191         }
36192         // cache the size
36193         this.viewSize = [w, h];
36194         if(this.modal && this.mask.isVisible()){
36195             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
36196             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36197         }
36198         if(this.isVisible()){
36199             this.constrainXY();
36200         }
36201     },
36202
36203     /**
36204      * Destroys this dialog and all its supporting elements (including any tabs, shim,
36205      * shadow, proxy, mask, etc.)  Also removes all event listeners.
36206      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
36207      */
36208     destroy : function(removeEl){
36209         if(this.isVisible()){
36210             this.animateTarget = null;
36211             this.hide();
36212         }
36213         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
36214         if(this.tabs){
36215             this.tabs.destroy(removeEl);
36216         }
36217         Roo.destroy(
36218              this.shim,
36219              this.proxy,
36220              this.resizer,
36221              this.close,
36222              this.mask
36223         );
36224         if(this.dd){
36225             this.dd.unreg();
36226         }
36227         if(this.buttons){
36228            for(var i = 0, len = this.buttons.length; i < len; i++){
36229                this.buttons[i].destroy();
36230            }
36231         }
36232         this.el.removeAllListeners();
36233         if(removeEl === true){
36234             this.el.update("");
36235             this.el.remove();
36236         }
36237         Roo.DialogManager.unregister(this);
36238     },
36239
36240     // private
36241     startMove : function(){
36242         if(this.proxyDrag){
36243             this.proxy.show();
36244         }
36245         if(this.constraintoviewport !== false){
36246             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
36247         }
36248     },
36249
36250     // private
36251     endMove : function(){
36252         if(!this.proxyDrag){
36253             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
36254         }else{
36255             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
36256             this.proxy.hide();
36257         }
36258         this.refreshSize();
36259         this.adjustAssets();
36260         this.focus();
36261         this.fireEvent("move", this, this.xy[0], this.xy[1]);
36262     },
36263
36264     /**
36265      * Brings this dialog to the front of any other visible dialogs
36266      * @return {Roo.BasicDialog} this
36267      */
36268     toFront : function(){
36269         Roo.DialogManager.bringToFront(this);
36270         return this;
36271     },
36272
36273     /**
36274      * Sends this dialog to the back (under) of any other visible dialogs
36275      * @return {Roo.BasicDialog} this
36276      */
36277     toBack : function(){
36278         Roo.DialogManager.sendToBack(this);
36279         return this;
36280     },
36281
36282     /**
36283      * Centers this dialog in the viewport
36284      * @return {Roo.BasicDialog} this
36285      */
36286     center : function(){
36287         var xy = this.el.getCenterXY(true);
36288         this.moveTo(xy[0], xy[1]);
36289         return this;
36290     },
36291
36292     /**
36293      * Moves the dialog's top-left corner to the specified point
36294      * @param {Number} x
36295      * @param {Number} y
36296      * @return {Roo.BasicDialog} this
36297      */
36298     moveTo : function(x, y){
36299         this.xy = [x,y];
36300         if(this.isVisible()){
36301             this.el.setXY(this.xy);
36302             this.adjustAssets();
36303         }
36304         return this;
36305     },
36306
36307     /**
36308      * Aligns the dialog to the specified element
36309      * @param {String/HTMLElement/Roo.Element} element The element to align to.
36310      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
36311      * @param {Array} offsets (optional) Offset the positioning by [x, y]
36312      * @return {Roo.BasicDialog} this
36313      */
36314     alignTo : function(element, position, offsets){
36315         this.xy = this.el.getAlignToXY(element, position, offsets);
36316         if(this.isVisible()){
36317             this.el.setXY(this.xy);
36318             this.adjustAssets();
36319         }
36320         return this;
36321     },
36322
36323     /**
36324      * Anchors an element to another element and realigns it when the window is resized.
36325      * @param {String/HTMLElement/Roo.Element} element The element to align to.
36326      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
36327      * @param {Array} offsets (optional) Offset the positioning by [x, y]
36328      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
36329      * is a number, it is used as the buffer delay (defaults to 50ms).
36330      * @return {Roo.BasicDialog} this
36331      */
36332     anchorTo : function(el, alignment, offsets, monitorScroll){
36333         var action = function(){
36334             this.alignTo(el, alignment, offsets);
36335         };
36336         Roo.EventManager.onWindowResize(action, this);
36337         var tm = typeof monitorScroll;
36338         if(tm != 'undefined'){
36339             Roo.EventManager.on(window, 'scroll', action, this,
36340                 {buffer: tm == 'number' ? monitorScroll : 50});
36341         }
36342         action.call(this);
36343         return this;
36344     },
36345
36346     /**
36347      * Returns true if the dialog is visible
36348      * @return {Boolean}
36349      */
36350     isVisible : function(){
36351         return this.el.isVisible();
36352     },
36353
36354     // private
36355     animHide : function(callback){
36356         var b = Roo.get(this.animateTarget).getBox();
36357         this.proxy.show();
36358         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
36359         this.el.hide();
36360         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
36361                     this.hideEl.createDelegate(this, [callback]));
36362     },
36363
36364     /**
36365      * Hides the dialog.
36366      * @param {Function} callback (optional) Function to call when the dialog is hidden
36367      * @return {Roo.BasicDialog} this
36368      */
36369     hide : function(callback){
36370         if (this.fireEvent("beforehide", this) === false){
36371             return;
36372         }
36373         if(this.shadow){
36374             this.shadow.hide();
36375         }
36376         if(this.shim) {
36377           this.shim.hide();
36378         }
36379         // sometimes animateTarget seems to get set.. causing problems...
36380         // this just double checks..
36381         if(this.animateTarget && Roo.get(this.animateTarget)) {
36382            this.animHide(callback);
36383         }else{
36384             this.el.hide();
36385             this.hideEl(callback);
36386         }
36387         return this;
36388     },
36389
36390     // private
36391     hideEl : function(callback){
36392         this.proxy.hide();
36393         if(this.modal){
36394             this.mask.hide();
36395             Roo.get(document.body).removeClass("x-body-masked");
36396         }
36397         this.fireEvent("hide", this);
36398         if(typeof callback == "function"){
36399             callback();
36400         }
36401     },
36402
36403     // private
36404     hideAction : function(){
36405         this.setLeft("-10000px");
36406         this.setTop("-10000px");
36407         this.setStyle("visibility", "hidden");
36408     },
36409
36410     // private
36411     refreshSize : function(){
36412         this.size = this.el.getSize();
36413         this.xy = this.el.getXY();
36414         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
36415     },
36416
36417     // private
36418     // z-index is managed by the DialogManager and may be overwritten at any time
36419     setZIndex : function(index){
36420         if(this.modal){
36421             this.mask.setStyle("z-index", index);
36422         }
36423         if(this.shim){
36424             this.shim.setStyle("z-index", ++index);
36425         }
36426         if(this.shadow){
36427             this.shadow.setZIndex(++index);
36428         }
36429         this.el.setStyle("z-index", ++index);
36430         if(this.proxy){
36431             this.proxy.setStyle("z-index", ++index);
36432         }
36433         if(this.resizer){
36434             this.resizer.proxy.setStyle("z-index", ++index);
36435         }
36436
36437         this.lastZIndex = index;
36438     },
36439
36440     /**
36441      * Returns the element for this dialog
36442      * @return {Roo.Element} The underlying dialog Element
36443      */
36444     getEl : function(){
36445         return this.el;
36446     }
36447 });
36448
36449 /**
36450  * @class Roo.DialogManager
36451  * Provides global access to BasicDialogs that have been created and
36452  * support for z-indexing (layering) multiple open dialogs.
36453  */
36454 Roo.DialogManager = function(){
36455     var list = {};
36456     var accessList = [];
36457     var front = null;
36458
36459     // private
36460     var sortDialogs = function(d1, d2){
36461         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
36462     };
36463
36464     // private
36465     var orderDialogs = function(){
36466         accessList.sort(sortDialogs);
36467         var seed = Roo.DialogManager.zseed;
36468         for(var i = 0, len = accessList.length; i < len; i++){
36469             var dlg = accessList[i];
36470             if(dlg){
36471                 dlg.setZIndex(seed + (i*10));
36472             }
36473         }
36474     };
36475
36476     return {
36477         /**
36478          * The starting z-index for BasicDialogs (defaults to 9000)
36479          * @type Number The z-index value
36480          */
36481         zseed : 9000,
36482
36483         // private
36484         register : function(dlg){
36485             list[dlg.id] = dlg;
36486             accessList.push(dlg);
36487         },
36488
36489         // private
36490         unregister : function(dlg){
36491             delete list[dlg.id];
36492             var i=0;
36493             var len=0;
36494             if(!accessList.indexOf){
36495                 for(  i = 0, len = accessList.length; i < len; i++){
36496                     if(accessList[i] == dlg){
36497                         accessList.splice(i, 1);
36498                         return;
36499                     }
36500                 }
36501             }else{
36502                  i = accessList.indexOf(dlg);
36503                 if(i != -1){
36504                     accessList.splice(i, 1);
36505                 }
36506             }
36507         },
36508
36509         /**
36510          * Gets a registered dialog by id
36511          * @param {String/Object} id The id of the dialog or a dialog
36512          * @return {Roo.BasicDialog} this
36513          */
36514         get : function(id){
36515             return typeof id == "object" ? id : list[id];
36516         },
36517
36518         /**
36519          * Brings the specified dialog to the front
36520          * @param {String/Object} dlg The id of the dialog or a dialog
36521          * @return {Roo.BasicDialog} this
36522          */
36523         bringToFront : function(dlg){
36524             dlg = this.get(dlg);
36525             if(dlg != front){
36526                 front = dlg;
36527                 dlg._lastAccess = new Date().getTime();
36528                 orderDialogs();
36529             }
36530             return dlg;
36531         },
36532
36533         /**
36534          * Sends the specified dialog to the back
36535          * @param {String/Object} dlg The id of the dialog or a dialog
36536          * @return {Roo.BasicDialog} this
36537          */
36538         sendToBack : function(dlg){
36539             dlg = this.get(dlg);
36540             dlg._lastAccess = -(new Date().getTime());
36541             orderDialogs();
36542             return dlg;
36543         },
36544
36545         /**
36546          * Hides all dialogs
36547          */
36548         hideAll : function(){
36549             for(var id in list){
36550                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
36551                     list[id].hide();
36552                 }
36553             }
36554         }
36555     };
36556 }();
36557
36558 /**
36559  * @class Roo.LayoutDialog
36560  * @extends Roo.BasicDialog
36561  * @children Roo.panel.Content
36562  * @parent builder none
36563  * Dialog which provides adjustments for working with a layout in a Dialog.
36564  * Add your necessary layout config options to the dialog's config.<br>
36565  * Example usage (including a nested layout):
36566  * <pre><code>
36567 if(!dialog){
36568     dialog = new Roo.LayoutDialog("download-dlg", {
36569         modal: true,
36570         width:600,
36571         height:450,
36572         shadow:true,
36573         minWidth:500,
36574         minHeight:350,
36575         autoTabs:true,
36576         proxyDrag:true,
36577         // layout config merges with the dialog config
36578         center:{
36579             tabPosition: "top",
36580             alwaysShowTabs: true
36581         }
36582     });
36583     dialog.addKeyListener(27, dialog.hide, dialog);
36584     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
36585     dialog.addButton("Build It!", this.getDownload, this);
36586
36587     // we can even add nested layouts
36588     var innerLayout = new Roo.layout.Border("dl-inner", {
36589         east: {
36590             initialSize: 200,
36591             autoScroll:true,
36592             split:true
36593         },
36594         center: {
36595             autoScroll:true
36596         }
36597     });
36598     innerLayout.beginUpdate();
36599     innerLayout.add("east", new Roo.panel.Content("dl-details"));
36600     innerLayout.add("center", new Roo.panel.Content("selection-panel"));
36601     innerLayout.endUpdate(true);
36602
36603     var layout = dialog.getLayout();
36604     layout.beginUpdate();
36605     layout.add("center", new Roo.panel.Content("standard-panel",
36606                         {title: "Download the Source", fitToFrame:true}));
36607     layout.add("center", new Roo.panel.NestedLayout(innerLayout,
36608                {title: "Build your own roo.js"}));
36609     layout.getRegion("center").showPanel(sp);
36610     layout.endUpdate();
36611 }
36612 </code></pre>
36613     * @constructor
36614     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
36615     * @param {Object} config configuration options
36616   */
36617 Roo.LayoutDialog = function(el, cfg){
36618     
36619     var config=  cfg;
36620     if (typeof(cfg) == 'undefined') {
36621         config = Roo.apply({}, el);
36622         // not sure why we use documentElement here.. - it should always be body.
36623         // IE7 borks horribly if we use documentElement.
36624         // webkit also does not like documentElement - it creates a body element...
36625         el = Roo.get( document.body || document.documentElement ).createChild();
36626         //config.autoCreate = true;
36627     }
36628     
36629     
36630     config.autoTabs = false;
36631     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
36632     this.body.setStyle({overflow:"hidden", position:"relative"});
36633     this.layout = new Roo.layout.Border(this.body.dom, config);
36634     this.layout.monitorWindowResize = false;
36635     this.el.addClass("x-dlg-auto-layout");
36636     // fix case when center region overwrites center function
36637     this.center = Roo.BasicDialog.prototype.center;
36638     this.on("show", this.layout.layout, this.layout, true);
36639     if (config.items) {
36640         var xitems = config.items;
36641         delete config.items;
36642         Roo.each(xitems, this.addxtype, this);
36643     }
36644     
36645     
36646 };
36647 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
36648     
36649     
36650     /**
36651      * @cfg {Roo.layout.Region} east  
36652      */
36653     /**
36654      * @cfg {Roo.layout.Region} west
36655      */
36656     /**
36657      * @cfg {Roo.layout.Region} south
36658      */
36659     /**
36660      * @cfg {Roo.layout.Region} north
36661      */
36662     /**
36663      * @cfg {Roo.layout.Region} center
36664      */
36665     /**
36666      * @cfg {Roo.Button} buttons[]  Bottom buttons..
36667      */
36668     
36669     
36670     /**
36671      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
36672      * @deprecated
36673      */
36674     endUpdate : function(){
36675         this.layout.endUpdate();
36676     },
36677
36678     /**
36679      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
36680      *  @deprecated
36681      */
36682     beginUpdate : function(){
36683         this.layout.beginUpdate();
36684     },
36685
36686     /**
36687      * Get the BorderLayout for this dialog
36688      * @return {Roo.layout.Border}
36689      */
36690     getLayout : function(){
36691         return this.layout;
36692     },
36693
36694     showEl : function(){
36695         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
36696         if(Roo.isIE7){
36697             this.layout.layout();
36698         }
36699     },
36700
36701     // private
36702     // Use the syncHeightBeforeShow config option to control this automatically
36703     syncBodyHeight : function(){
36704         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
36705         if(this.layout){this.layout.layout();}
36706     },
36707     
36708       /**
36709      * Add an xtype element (actually adds to the layout.)
36710      * @return {Object} xdata xtype object data.
36711      */
36712     
36713     addxtype : function(c) {
36714         return this.layout.addxtype(c);
36715     }
36716 });/*
36717  * Based on:
36718  * Ext JS Library 1.1.1
36719  * Copyright(c) 2006-2007, Ext JS, LLC.
36720  *
36721  * Originally Released Under LGPL - original licence link has changed is not relivant.
36722  *
36723  * Fork - LGPL
36724  * <script type="text/javascript">
36725  */
36726  
36727 /**
36728  * @class Roo.MessageBox
36729  * @static
36730  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
36731  * Example usage:
36732  *<pre><code>
36733 // Basic alert:
36734 Roo.Msg.alert('Status', 'Changes saved successfully.');
36735
36736 // Prompt for user data:
36737 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
36738     if (btn == 'ok'){
36739         // process text value...
36740     }
36741 });
36742
36743 // Show a dialog using config options:
36744 Roo.Msg.show({
36745    title:'Save Changes?',
36746    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
36747    buttons: Roo.Msg.YESNOCANCEL,
36748    fn: processResult,
36749    animEl: 'elId'
36750 });
36751 </code></pre>
36752  * @static
36753  */
36754 Roo.MessageBox = function(){
36755     var dlg, opt, mask, waitTimer;
36756     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
36757     var buttons, activeTextEl, bwidth;
36758
36759     // private
36760     var handleButton = function(button){
36761         dlg.hide();
36762         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
36763     };
36764
36765     // private
36766     var handleHide = function(){
36767         if(opt && opt.cls){
36768             dlg.el.removeClass(opt.cls);
36769         }
36770         if(waitTimer){
36771             Roo.TaskMgr.stop(waitTimer);
36772             waitTimer = null;
36773         }
36774     };
36775
36776     // private
36777     var updateButtons = function(b){
36778         var width = 0;
36779         if(!b){
36780             buttons["ok"].hide();
36781             buttons["cancel"].hide();
36782             buttons["yes"].hide();
36783             buttons["no"].hide();
36784             dlg.footer.dom.style.display = 'none';
36785             return width;
36786         }
36787         dlg.footer.dom.style.display = '';
36788         for(var k in buttons){
36789             if(typeof buttons[k] != "function"){
36790                 if(b[k]){
36791                     buttons[k].show();
36792                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
36793                     width += buttons[k].el.getWidth()+15;
36794                 }else{
36795                     buttons[k].hide();
36796                 }
36797             }
36798         }
36799         return width;
36800     };
36801
36802     // private
36803     var handleEsc = function(d, k, e){
36804         if(opt && opt.closable !== false){
36805             dlg.hide();
36806         }
36807         if(e){
36808             e.stopEvent();
36809         }
36810     };
36811
36812     return {
36813         /**
36814          * Returns a reference to the underlying {@link Roo.BasicDialog} element
36815          * @return {Roo.BasicDialog} The BasicDialog element
36816          */
36817         getDialog : function(){
36818            if(!dlg){
36819                 dlg = new Roo.BasicDialog("x-msg-box", {
36820                     autoCreate : true,
36821                     shadow: true,
36822                     draggable: true,
36823                     resizable:false,
36824                     constraintoviewport:false,
36825                     fixedcenter:true,
36826                     collapsible : false,
36827                     shim:true,
36828                     modal: true,
36829                     width:400, height:100,
36830                     buttonAlign:"center",
36831                     closeClick : function(){
36832                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
36833                             handleButton("no");
36834                         }else{
36835                             handleButton("cancel");
36836                         }
36837                     }
36838                 });
36839               
36840                 dlg.on("hide", handleHide);
36841                 mask = dlg.mask;
36842                 dlg.addKeyListener(27, handleEsc);
36843                 buttons = {};
36844                 var bt = this.buttonText;
36845                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
36846                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
36847                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
36848                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
36849                 bodyEl = dlg.body.createChild({
36850
36851                     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>'
36852                 });
36853                 msgEl = bodyEl.dom.firstChild;
36854                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
36855                 textboxEl.enableDisplayMode();
36856                 textboxEl.addKeyListener([10,13], function(){
36857                     if(dlg.isVisible() && opt && opt.buttons){
36858                         if(opt.buttons.ok){
36859                             handleButton("ok");
36860                         }else if(opt.buttons.yes){
36861                             handleButton("yes");
36862                         }
36863                     }
36864                 });
36865                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
36866                 textareaEl.enableDisplayMode();
36867                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
36868                 progressEl.enableDisplayMode();
36869                 var pf = progressEl.dom.firstChild;
36870                 if (pf) {
36871                     pp = Roo.get(pf.firstChild);
36872                     pp.setHeight(pf.offsetHeight);
36873                 }
36874                 
36875             }
36876             return dlg;
36877         },
36878
36879         /**
36880          * Updates the message box body text
36881          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
36882          * the XHTML-compliant non-breaking space character '&amp;#160;')
36883          * @return {Roo.MessageBox} This message box
36884          */
36885         updateText : function(text){
36886             if(!dlg.isVisible() && !opt.width){
36887                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
36888             }
36889             msgEl.innerHTML = text || '&#160;';
36890       
36891             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
36892             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
36893             var w = Math.max(
36894                     Math.min(opt.width || cw , this.maxWidth), 
36895                     Math.max(opt.minWidth || this.minWidth, bwidth)
36896             );
36897             if(opt.prompt){
36898                 activeTextEl.setWidth(w);
36899             }
36900             if(dlg.isVisible()){
36901                 dlg.fixedcenter = false;
36902             }
36903             // to big, make it scroll. = But as usual stupid IE does not support
36904             // !important..
36905             
36906             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
36907                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
36908                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
36909             } else {
36910                 bodyEl.dom.style.height = '';
36911                 bodyEl.dom.style.overflowY = '';
36912             }
36913             if (cw > w) {
36914                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
36915             } else {
36916                 bodyEl.dom.style.overflowX = '';
36917             }
36918             
36919             dlg.setContentSize(w, bodyEl.getHeight());
36920             if(dlg.isVisible()){
36921                 dlg.fixedcenter = true;
36922             }
36923             return this;
36924         },
36925
36926         /**
36927          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
36928          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
36929          * @param {Number} value Any number between 0 and 1 (e.g., .5)
36930          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
36931          * @return {Roo.MessageBox} This message box
36932          */
36933         updateProgress : function(value, text){
36934             if(text){
36935                 this.updateText(text);
36936             }
36937             if (pp) { // weird bug on my firefox - for some reason this is not defined
36938                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
36939             }
36940             return this;
36941         },        
36942
36943         /**
36944          * Returns true if the message box is currently displayed
36945          * @return {Boolean} True if the message box is visible, else false
36946          */
36947         isVisible : function(){
36948             return dlg && dlg.isVisible();  
36949         },
36950
36951         /**
36952          * Hides the message box if it is displayed
36953          */
36954         hide : function(){
36955             if(this.isVisible()){
36956                 dlg.hide();
36957             }  
36958         },
36959
36960         /**
36961          * Displays a new message box, or reinitializes an existing message box, based on the config options
36962          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
36963          * The following config object properties are supported:
36964          * <pre>
36965 Property    Type             Description
36966 ----------  ---------------  ------------------------------------------------------------------------------------
36967 animEl            String/Element   An id or Element from which the message box should animate as it opens and
36968                                    closes (defaults to undefined)
36969 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
36970                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
36971 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
36972                                    progress and wait dialogs will ignore this property and always hide the
36973                                    close button as they can only be closed programmatically.
36974 cls               String           A custom CSS class to apply to the message box element
36975 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
36976                                    displayed (defaults to 75)
36977 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
36978                                    function will be btn (the name of the button that was clicked, if applicable,
36979                                    e.g. "ok"), and text (the value of the active text field, if applicable).
36980                                    Progress and wait dialogs will ignore this option since they do not respond to
36981                                    user actions and can only be closed programmatically, so any required function
36982                                    should be called by the same code after it closes the dialog.
36983 icon              String           A CSS class that provides a background image to be used as an icon for
36984                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
36985 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
36986 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
36987 modal             Boolean          False to allow user interaction with the page while the message box is
36988                                    displayed (defaults to true)
36989 msg               String           A string that will replace the existing message box body text (defaults
36990                                    to the XHTML-compliant non-breaking space character '&#160;')
36991 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
36992 progress          Boolean          True to display a progress bar (defaults to false)
36993 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
36994 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
36995 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
36996 title             String           The title text
36997 value             String           The string value to set into the active textbox element if displayed
36998 wait              Boolean          True to display a progress bar (defaults to false)
36999 width             Number           The width of the dialog in pixels
37000 </pre>
37001          *
37002          * Example usage:
37003          * <pre><code>
37004 Roo.Msg.show({
37005    title: 'Address',
37006    msg: 'Please enter your address:',
37007    width: 300,
37008    buttons: Roo.MessageBox.OKCANCEL,
37009    multiline: true,
37010    fn: saveAddress,
37011    animEl: 'addAddressBtn'
37012 });
37013 </code></pre>
37014          * @param {Object} config Configuration options
37015          * @return {Roo.MessageBox} This message box
37016          */
37017         show : function(options)
37018         {
37019             
37020             // this causes nightmares if you show one dialog after another
37021             // especially on callbacks..
37022              
37023             if(this.isVisible()){
37024                 
37025                 this.hide();
37026                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
37027                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
37028                 Roo.log("New Dialog Message:" +  options.msg )
37029                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
37030                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
37031                 
37032             }
37033             var d = this.getDialog();
37034             opt = options;
37035             d.setTitle(opt.title || "&#160;");
37036             d.close.setDisplayed(opt.closable !== false);
37037             activeTextEl = textboxEl;
37038             opt.prompt = opt.prompt || (opt.multiline ? true : false);
37039             if(opt.prompt){
37040                 if(opt.multiline){
37041                     textboxEl.hide();
37042                     textareaEl.show();
37043                     textareaEl.setHeight(typeof opt.multiline == "number" ?
37044                         opt.multiline : this.defaultTextHeight);
37045                     activeTextEl = textareaEl;
37046                 }else{
37047                     textboxEl.show();
37048                     textareaEl.hide();
37049                 }
37050             }else{
37051                 textboxEl.hide();
37052                 textareaEl.hide();
37053             }
37054             progressEl.setDisplayed(opt.progress === true);
37055             this.updateProgress(0);
37056             activeTextEl.dom.value = opt.value || "";
37057             if(opt.prompt){
37058                 dlg.setDefaultButton(activeTextEl);
37059             }else{
37060                 var bs = opt.buttons;
37061                 var db = null;
37062                 if(bs && bs.ok){
37063                     db = buttons["ok"];
37064                 }else if(bs && bs.yes){
37065                     db = buttons["yes"];
37066                 }
37067                 dlg.setDefaultButton(db);
37068             }
37069             bwidth = updateButtons(opt.buttons);
37070             this.updateText(opt.msg);
37071             if(opt.cls){
37072                 d.el.addClass(opt.cls);
37073             }
37074             d.proxyDrag = opt.proxyDrag === true;
37075             d.modal = opt.modal !== false;
37076             d.mask = opt.modal !== false ? mask : false;
37077             if(!d.isVisible()){
37078                 // force it to the end of the z-index stack so it gets a cursor in FF
37079                 document.body.appendChild(dlg.el.dom);
37080                 d.animateTarget = null;
37081                 d.show(options.animEl);
37082             }
37083             dlg.toFront();
37084             return this;
37085         },
37086
37087         /**
37088          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
37089          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
37090          * and closing the message box when the process is complete.
37091          * @param {String} title The title bar text
37092          * @param {String} msg The message box body text
37093          * @return {Roo.MessageBox} This message box
37094          */
37095         progress : function(title, msg){
37096             this.show({
37097                 title : title,
37098                 msg : msg,
37099                 buttons: false,
37100                 progress:true,
37101                 closable:false,
37102                 minWidth: this.minProgressWidth,
37103                 modal : true
37104             });
37105             return this;
37106         },
37107
37108         /**
37109          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
37110          * If a callback function is passed it will be called after the user clicks the button, and the
37111          * id of the button that was clicked will be passed as the only parameter to the callback
37112          * (could also be the top-right close button).
37113          * @param {String} title The title bar text
37114          * @param {String} msg The message box body text
37115          * @param {Function} fn (optional) The callback function invoked after the message box is closed
37116          * @param {Object} scope (optional) The scope of the callback function
37117          * @return {Roo.MessageBox} This message box
37118          */
37119         alert : function(title, msg, fn, scope){
37120             this.show({
37121                 title : title,
37122                 msg : msg,
37123                 buttons: this.OK,
37124                 fn: fn,
37125                 scope : scope,
37126                 modal : true
37127             });
37128             return this;
37129         },
37130
37131         /**
37132          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
37133          * interaction while waiting for a long-running process to complete that does not have defined intervals.
37134          * You are responsible for closing the message box when the process is complete.
37135          * @param {String} msg The message box body text
37136          * @param {String} title (optional) The title bar text
37137          * @return {Roo.MessageBox} This message box
37138          */
37139         wait : function(msg, title){
37140             this.show({
37141                 title : title,
37142                 msg : msg,
37143                 buttons: false,
37144                 closable:false,
37145                 progress:true,
37146                 modal:true,
37147                 width:300,
37148                 wait:true
37149             });
37150             waitTimer = Roo.TaskMgr.start({
37151                 run: function(i){
37152                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
37153                 },
37154                 interval: 1000
37155             });
37156             return this;
37157         },
37158
37159         /**
37160          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
37161          * If a callback function is passed it will be called after the user clicks either button, and the id of the
37162          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
37163          * @param {String} title The title bar text
37164          * @param {String} msg The message box body text
37165          * @param {Function} fn (optional) The callback function invoked after the message box is closed
37166          * @param {Object} scope (optional) The scope of the callback function
37167          * @return {Roo.MessageBox} This message box
37168          */
37169         confirm : function(title, msg, fn, scope){
37170             this.show({
37171                 title : title,
37172                 msg : msg,
37173                 buttons: this.YESNO,
37174                 fn: fn,
37175                 scope : scope,
37176                 modal : true
37177             });
37178             return this;
37179         },
37180
37181         /**
37182          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
37183          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
37184          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
37185          * (could also be the top-right close button) and the text that was entered will be passed as the two
37186          * parameters to the callback.
37187          * @param {String} title The title bar text
37188          * @param {String} msg The message box body text
37189          * @param {Function} fn (optional) The callback function invoked after the message box is closed
37190          * @param {Object} scope (optional) The scope of the callback function
37191          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
37192          * property, or the height in pixels to create the textbox (defaults to false / single-line)
37193          * @return {Roo.MessageBox} This message box
37194          */
37195         prompt : function(title, msg, fn, scope, multiline){
37196             this.show({
37197                 title : title,
37198                 msg : msg,
37199                 buttons: this.OKCANCEL,
37200                 fn: fn,
37201                 minWidth:250,
37202                 scope : scope,
37203                 prompt:true,
37204                 multiline: multiline,
37205                 modal : true
37206             });
37207             return this;
37208         },
37209
37210         /**
37211          * Button config that displays a single OK button
37212          * @type Object
37213          */
37214         OK : {ok:true},
37215         /**
37216          * Button config that displays Yes and No buttons
37217          * @type Object
37218          */
37219         YESNO : {yes:true, no:true},
37220         /**
37221          * Button config that displays OK and Cancel buttons
37222          * @type Object
37223          */
37224         OKCANCEL : {ok:true, cancel:true},
37225         /**
37226          * Button config that displays Yes, No and Cancel buttons
37227          * @type Object
37228          */
37229         YESNOCANCEL : {yes:true, no:true, cancel:true},
37230
37231         /**
37232          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
37233          * @type Number
37234          */
37235         defaultTextHeight : 75,
37236         /**
37237          * The maximum width in pixels of the message box (defaults to 600)
37238          * @type Number
37239          */
37240         maxWidth : 600,
37241         /**
37242          * The minimum width in pixels of the message box (defaults to 100)
37243          * @type Number
37244          */
37245         minWidth : 100,
37246         /**
37247          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
37248          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
37249          * @type Number
37250          */
37251         minProgressWidth : 250,
37252         /**
37253          * An object containing the default button text strings that can be overriden for localized language support.
37254          * Supported properties are: ok, cancel, yes and no.
37255          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
37256          * @type Object
37257          */
37258         buttonText : {
37259             ok : "OK",
37260             cancel : "Cancel",
37261             yes : "Yes",
37262             no : "No"
37263         }
37264     };
37265 }();
37266
37267 /**
37268  * Shorthand for {@link Roo.MessageBox}
37269  */
37270 Roo.Msg = Roo.MessageBox;/*
37271  * Based on:
37272  * Ext JS Library 1.1.1
37273  * Copyright(c) 2006-2007, Ext JS, LLC.
37274  *
37275  * Originally Released Under LGPL - original licence link has changed is not relivant.
37276  *
37277  * Fork - LGPL
37278  * <script type="text/javascript">
37279  */
37280 /**
37281  * @class Roo.QuickTips
37282  * Provides attractive and customizable tooltips for any element.
37283  * @static
37284  */
37285 Roo.QuickTips = function(){
37286     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
37287     var ce, bd, xy, dd;
37288     var visible = false, disabled = true, inited = false;
37289     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
37290     
37291     var onOver = function(e){
37292         if(disabled){
37293             return;
37294         }
37295         var t = e.getTarget();
37296         if(!t || t.nodeType !== 1 || t == document || t == document.body){
37297             return;
37298         }
37299         if(ce && t == ce.el){
37300             clearTimeout(hideProc);
37301             return;
37302         }
37303         if(t && tagEls[t.id]){
37304             tagEls[t.id].el = t;
37305             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
37306             return;
37307         }
37308         var ttp, et = Roo.fly(t);
37309         var ns = cfg.namespace;
37310         if(tm.interceptTitles && t.title){
37311             ttp = t.title;
37312             t.qtip = ttp;
37313             t.removeAttribute("title");
37314             e.preventDefault();
37315         }else{
37316             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
37317         }
37318         if(ttp){
37319             showProc = show.defer(tm.showDelay, tm, [{
37320                 el: t, 
37321                 text: ttp.replace(/\\n/g,'<br/>'),
37322                 width: et.getAttributeNS(ns, cfg.width),
37323                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
37324                 title: et.getAttributeNS(ns, cfg.title),
37325                     cls: et.getAttributeNS(ns, cfg.cls)
37326             }]);
37327         }
37328     };
37329     
37330     var onOut = function(e){
37331         clearTimeout(showProc);
37332         var t = e.getTarget();
37333         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
37334             hideProc = setTimeout(hide, tm.hideDelay);
37335         }
37336     };
37337     
37338     var onMove = function(e){
37339         if(disabled){
37340             return;
37341         }
37342         xy = e.getXY();
37343         xy[1] += 18;
37344         if(tm.trackMouse && ce){
37345             el.setXY(xy);
37346         }
37347     };
37348     
37349     var onDown = function(e){
37350         clearTimeout(showProc);
37351         clearTimeout(hideProc);
37352         if(!e.within(el)){
37353             if(tm.hideOnClick){
37354                 hide();
37355                 tm.disable();
37356                 tm.enable.defer(100, tm);
37357             }
37358         }
37359     };
37360     
37361     var getPad = function(){
37362         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
37363     };
37364
37365     var show = function(o){
37366         if(disabled){
37367             return;
37368         }
37369         clearTimeout(dismissProc);
37370         ce = o;
37371         if(removeCls){ // in case manually hidden
37372             el.removeClass(removeCls);
37373             removeCls = null;
37374         }
37375         if(ce.cls){
37376             el.addClass(ce.cls);
37377             removeCls = ce.cls;
37378         }
37379         if(ce.title){
37380             tipTitle.update(ce.title);
37381             tipTitle.show();
37382         }else{
37383             tipTitle.update('');
37384             tipTitle.hide();
37385         }
37386         el.dom.style.width  = tm.maxWidth+'px';
37387         //tipBody.dom.style.width = '';
37388         tipBodyText.update(o.text);
37389         var p = getPad(), w = ce.width;
37390         if(!w){
37391             var td = tipBodyText.dom;
37392             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
37393             if(aw > tm.maxWidth){
37394                 w = tm.maxWidth;
37395             }else if(aw < tm.minWidth){
37396                 w = tm.minWidth;
37397             }else{
37398                 w = aw;
37399             }
37400         }
37401         //tipBody.setWidth(w);
37402         el.setWidth(parseInt(w, 10) + p);
37403         if(ce.autoHide === false){
37404             close.setDisplayed(true);
37405             if(dd){
37406                 dd.unlock();
37407             }
37408         }else{
37409             close.setDisplayed(false);
37410             if(dd){
37411                 dd.lock();
37412             }
37413         }
37414         if(xy){
37415             el.avoidY = xy[1]-18;
37416             el.setXY(xy);
37417         }
37418         if(tm.animate){
37419             el.setOpacity(.1);
37420             el.setStyle("visibility", "visible");
37421             el.fadeIn({callback: afterShow});
37422         }else{
37423             afterShow();
37424         }
37425     };
37426     
37427     var afterShow = function(){
37428         if(ce){
37429             el.show();
37430             esc.enable();
37431             if(tm.autoDismiss && ce.autoHide !== false){
37432                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
37433             }
37434         }
37435     };
37436     
37437     var hide = function(noanim){
37438         clearTimeout(dismissProc);
37439         clearTimeout(hideProc);
37440         ce = null;
37441         if(el.isVisible()){
37442             esc.disable();
37443             if(noanim !== true && tm.animate){
37444                 el.fadeOut({callback: afterHide});
37445             }else{
37446                 afterHide();
37447             } 
37448         }
37449     };
37450     
37451     var afterHide = function(){
37452         el.hide();
37453         if(removeCls){
37454             el.removeClass(removeCls);
37455             removeCls = null;
37456         }
37457     };
37458     
37459     return {
37460         /**
37461         * @cfg {Number} minWidth
37462         * The minimum width of the quick tip (defaults to 40)
37463         */
37464        minWidth : 40,
37465         /**
37466         * @cfg {Number} maxWidth
37467         * The maximum width of the quick tip (defaults to 300)
37468         */
37469        maxWidth : 300,
37470         /**
37471         * @cfg {Boolean} interceptTitles
37472         * True to automatically use the element's DOM title value if available (defaults to false)
37473         */
37474        interceptTitles : false,
37475         /**
37476         * @cfg {Boolean} trackMouse
37477         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
37478         */
37479        trackMouse : false,
37480         /**
37481         * @cfg {Boolean} hideOnClick
37482         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
37483         */
37484        hideOnClick : true,
37485         /**
37486         * @cfg {Number} showDelay
37487         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
37488         */
37489        showDelay : 500,
37490         /**
37491         * @cfg {Number} hideDelay
37492         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
37493         */
37494        hideDelay : 200,
37495         /**
37496         * @cfg {Boolean} autoHide
37497         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
37498         * Used in conjunction with hideDelay.
37499         */
37500        autoHide : true,
37501         /**
37502         * @cfg {Boolean}
37503         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
37504         * (defaults to true).  Used in conjunction with autoDismissDelay.
37505         */
37506        autoDismiss : true,
37507         /**
37508         * @cfg {Number}
37509         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
37510         */
37511        autoDismissDelay : 5000,
37512        /**
37513         * @cfg {Boolean} animate
37514         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
37515         */
37516        animate : false,
37517
37518        /**
37519         * @cfg {String} title
37520         * Title text to display (defaults to '').  This can be any valid HTML markup.
37521         */
37522         title: '',
37523        /**
37524         * @cfg {String} text
37525         * Body text to display (defaults to '').  This can be any valid HTML markup.
37526         */
37527         text : '',
37528        /**
37529         * @cfg {String} cls
37530         * A CSS class to apply to the base quick tip element (defaults to '').
37531         */
37532         cls : '',
37533        /**
37534         * @cfg {Number} width
37535         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
37536         * minWidth or maxWidth.
37537         */
37538         width : null,
37539
37540     /**
37541      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
37542      * or display QuickTips in a page.
37543      */
37544        init : function(){
37545           tm = Roo.QuickTips;
37546           cfg = tm.tagConfig;
37547           if(!inited){
37548               if(!Roo.isReady){ // allow calling of init() before onReady
37549                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
37550                   return;
37551               }
37552               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
37553               el.fxDefaults = {stopFx: true};
37554               // maximum custom styling
37555               //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>');
37556               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>');              
37557               tipTitle = el.child('h3');
37558               tipTitle.enableDisplayMode("block");
37559               tipBody = el.child('div.x-tip-bd');
37560               tipBodyText = el.child('div.x-tip-bd-inner');
37561               //bdLeft = el.child('div.x-tip-bd-left');
37562               //bdRight = el.child('div.x-tip-bd-right');
37563               close = el.child('div.x-tip-close');
37564               close.enableDisplayMode("block");
37565               close.on("click", hide);
37566               var d = Roo.get(document);
37567               d.on("mousedown", onDown);
37568               d.on("mouseover", onOver);
37569               d.on("mouseout", onOut);
37570               d.on("mousemove", onMove);
37571               esc = d.addKeyListener(27, hide);
37572               esc.disable();
37573               if(Roo.dd.DD){
37574                   dd = el.initDD("default", null, {
37575                       onDrag : function(){
37576                           el.sync();  
37577                       }
37578                   });
37579                   dd.setHandleElId(tipTitle.id);
37580                   dd.lock();
37581               }
37582               inited = true;
37583           }
37584           this.enable(); 
37585        },
37586
37587     /**
37588      * Configures a new quick tip instance and assigns it to a target element.  The following config options
37589      * are supported:
37590      * <pre>
37591 Property    Type                   Description
37592 ----------  ---------------------  ------------------------------------------------------------------------
37593 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
37594      * </ul>
37595      * @param {Object} config The config object
37596      */
37597        register : function(config){
37598            var cs = config instanceof Array ? config : arguments;
37599            for(var i = 0, len = cs.length; i < len; i++) {
37600                var c = cs[i];
37601                var target = c.target;
37602                if(target){
37603                    if(target instanceof Array){
37604                        for(var j = 0, jlen = target.length; j < jlen; j++){
37605                            tagEls[target[j]] = c;
37606                        }
37607                    }else{
37608                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
37609                    }
37610                }
37611            }
37612        },
37613
37614     /**
37615      * Removes this quick tip from its element and destroys it.
37616      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
37617      */
37618        unregister : function(el){
37619            delete tagEls[Roo.id(el)];
37620        },
37621
37622     /**
37623      * Enable this quick tip.
37624      */
37625        enable : function(){
37626            if(inited && disabled){
37627                locks.pop();
37628                if(locks.length < 1){
37629                    disabled = false;
37630                }
37631            }
37632        },
37633
37634     /**
37635      * Disable this quick tip.
37636      */
37637        disable : function(){
37638           disabled = true;
37639           clearTimeout(showProc);
37640           clearTimeout(hideProc);
37641           clearTimeout(dismissProc);
37642           if(ce){
37643               hide(true);
37644           }
37645           locks.push(1);
37646        },
37647
37648     /**
37649      * Returns true if the quick tip is enabled, else false.
37650      */
37651        isEnabled : function(){
37652             return !disabled;
37653        },
37654
37655         // private
37656        tagConfig : {
37657            namespace : "roo", // was ext?? this may break..
37658            alt_namespace : "ext",
37659            attribute : "qtip",
37660            width : "width",
37661            target : "target",
37662            title : "qtitle",
37663            hide : "hide",
37664            cls : "qclass"
37665        }
37666    };
37667 }();
37668
37669 // backwards compat
37670 Roo.QuickTips.tips = Roo.QuickTips.register;/*
37671  * Based on:
37672  * Ext JS Library 1.1.1
37673  * Copyright(c) 2006-2007, Ext JS, LLC.
37674  *
37675  * Originally Released Under LGPL - original licence link has changed is not relivant.
37676  *
37677  * Fork - LGPL
37678  * <script type="text/javascript">
37679  */
37680  
37681
37682 /**
37683  * @class Roo.tree.TreePanel
37684  * @extends Roo.data.Tree
37685  * @cfg {Roo.tree.TreeNode} root The root node
37686  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
37687  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
37688  * @cfg {Boolean} enableDD true to enable drag and drop
37689  * @cfg {Boolean} enableDrag true to enable just drag
37690  * @cfg {Boolean} enableDrop true to enable just drop
37691  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
37692  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
37693  * @cfg {String} ddGroup The DD group this TreePanel belongs to
37694  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
37695  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
37696  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
37697  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
37698  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
37699  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
37700  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
37701  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
37702  * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
37703  * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
37704  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
37705  * @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>
37706  * @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>
37707  * 
37708  * @constructor
37709  * @param {String/HTMLElement/Element} el The container element
37710  * @param {Object} config
37711  */
37712 Roo.tree.TreePanel = function(el, config){
37713     var root = false;
37714     var loader = false;
37715     if (config.root) {
37716         root = config.root;
37717         delete config.root;
37718     }
37719     if (config.loader) {
37720         loader = config.loader;
37721         delete config.loader;
37722     }
37723     
37724     Roo.apply(this, config);
37725     Roo.tree.TreePanel.superclass.constructor.call(this);
37726     this.el = Roo.get(el);
37727     this.el.addClass('x-tree');
37728     //console.log(root);
37729     if (root) {
37730         this.setRootNode( Roo.factory(root, Roo.tree));
37731     }
37732     if (loader) {
37733         this.loader = Roo.factory(loader, Roo.tree);
37734     }
37735    /**
37736     * Read-only. The id of the container element becomes this TreePanel's id.
37737     */
37738     this.id = this.el.id;
37739     this.addEvents({
37740         /**
37741         * @event beforeload
37742         * Fires before a node is loaded, return false to cancel
37743         * @param {Node} node The node being loaded
37744         */
37745         "beforeload" : true,
37746         /**
37747         * @event load
37748         * Fires when a node is loaded
37749         * @param {Node} node The node that was loaded
37750         */
37751         "load" : true,
37752         /**
37753         * @event textchange
37754         * Fires when the text for a node is changed
37755         * @param {Node} node The node
37756         * @param {String} text The new text
37757         * @param {String} oldText The old text
37758         */
37759         "textchange" : true,
37760         /**
37761         * @event beforeexpand
37762         * Fires before a node is expanded, return false to cancel.
37763         * @param {Node} node The node
37764         * @param {Boolean} deep
37765         * @param {Boolean} anim
37766         */
37767         "beforeexpand" : true,
37768         /**
37769         * @event beforecollapse
37770         * Fires before a node is collapsed, return false to cancel.
37771         * @param {Node} node The node
37772         * @param {Boolean} deep
37773         * @param {Boolean} anim
37774         */
37775         "beforecollapse" : true,
37776         /**
37777         * @event expand
37778         * Fires when a node is expanded
37779         * @param {Node} node The node
37780         */
37781         "expand" : true,
37782         /**
37783         * @event disabledchange
37784         * Fires when the disabled status of a node changes
37785         * @param {Node} node The node
37786         * @param {Boolean} disabled
37787         */
37788         "disabledchange" : true,
37789         /**
37790         * @event collapse
37791         * Fires when a node is collapsed
37792         * @param {Node} node The node
37793         */
37794         "collapse" : true,
37795         /**
37796         * @event beforeclick
37797         * Fires before click processing on a node. Return false to cancel the default action.
37798         * @param {Node} node The node
37799         * @param {Roo.EventObject} e The event object
37800         */
37801         "beforeclick":true,
37802         /**
37803         * @event checkchange
37804         * Fires when a node with a checkbox's checked property changes
37805         * @param {Node} this This node
37806         * @param {Boolean} checked
37807         */
37808         "checkchange":true,
37809         /**
37810         * @event click
37811         * Fires when a node is clicked
37812         * @param {Node} node The node
37813         * @param {Roo.EventObject} e The event object
37814         */
37815         "click":true,
37816         /**
37817         * @event dblclick
37818         * Fires when a node is double clicked
37819         * @param {Node} node The node
37820         * @param {Roo.EventObject} e The event object
37821         */
37822         "dblclick":true,
37823         /**
37824         * @event contextmenu
37825         * Fires when a node is right clicked
37826         * @param {Node} node The node
37827         * @param {Roo.EventObject} e The event object
37828         */
37829         "contextmenu":true,
37830         /**
37831         * @event beforechildrenrendered
37832         * Fires right before the child nodes for a node are rendered
37833         * @param {Node} node The node
37834         */
37835         "beforechildrenrendered":true,
37836         /**
37837         * @event startdrag
37838         * Fires when a node starts being dragged
37839         * @param {Roo.tree.TreePanel} this
37840         * @param {Roo.tree.TreeNode} node
37841         * @param {event} e The raw browser event
37842         */ 
37843        "startdrag" : true,
37844        /**
37845         * @event enddrag
37846         * Fires when a drag operation is complete
37847         * @param {Roo.tree.TreePanel} this
37848         * @param {Roo.tree.TreeNode} node
37849         * @param {event} e The raw browser event
37850         */
37851        "enddrag" : true,
37852        /**
37853         * @event dragdrop
37854         * Fires when a dragged node is dropped on a valid DD target
37855         * @param {Roo.tree.TreePanel} this
37856         * @param {Roo.tree.TreeNode} node
37857         * @param {DD} dd The dd it was dropped on
37858         * @param {event} e The raw browser event
37859         */
37860        "dragdrop" : true,
37861        /**
37862         * @event beforenodedrop
37863         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
37864         * passed to handlers has the following properties:<br />
37865         * <ul style="padding:5px;padding-left:16px;">
37866         * <li>tree - The TreePanel</li>
37867         * <li>target - The node being targeted for the drop</li>
37868         * <li>data - The drag data from the drag source</li>
37869         * <li>point - The point of the drop - append, above or below</li>
37870         * <li>source - The drag source</li>
37871         * <li>rawEvent - Raw mouse event</li>
37872         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
37873         * to be inserted by setting them on this object.</li>
37874         * <li>cancel - Set this to true to cancel the drop.</li>
37875         * </ul>
37876         * @param {Object} dropEvent
37877         */
37878        "beforenodedrop" : true,
37879        /**
37880         * @event nodedrop
37881         * Fires after a DD object is dropped on a node in this tree. The dropEvent
37882         * passed to handlers has the following properties:<br />
37883         * <ul style="padding:5px;padding-left:16px;">
37884         * <li>tree - The TreePanel</li>
37885         * <li>target - The node being targeted for the drop</li>
37886         * <li>data - The drag data from the drag source</li>
37887         * <li>point - The point of the drop - append, above or below</li>
37888         * <li>source - The drag source</li>
37889         * <li>rawEvent - Raw mouse event</li>
37890         * <li>dropNode - Dropped node(s).</li>
37891         * </ul>
37892         * @param {Object} dropEvent
37893         */
37894        "nodedrop" : true,
37895         /**
37896         * @event nodedragover
37897         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
37898         * passed to handlers has the following properties:<br />
37899         * <ul style="padding:5px;padding-left:16px;">
37900         * <li>tree - The TreePanel</li>
37901         * <li>target - The node being targeted for the drop</li>
37902         * <li>data - The drag data from the drag source</li>
37903         * <li>point - The point of the drop - append, above or below</li>
37904         * <li>source - The drag source</li>
37905         * <li>rawEvent - Raw mouse event</li>
37906         * <li>dropNode - Drop node(s) provided by the source.</li>
37907         * <li>cancel - Set this to true to signal drop not allowed.</li>
37908         * </ul>
37909         * @param {Object} dragOverEvent
37910         */
37911        "nodedragover" : true,
37912        /**
37913         * @event appendnode
37914         * Fires when append node to the tree
37915         * @param {Roo.tree.TreePanel} this
37916         * @param {Roo.tree.TreeNode} node
37917         * @param {Number} index The index of the newly appended node
37918         */
37919        "appendnode" : true
37920         
37921     });
37922     if(this.singleExpand){
37923        this.on("beforeexpand", this.restrictExpand, this);
37924     }
37925     if (this.editor) {
37926         this.editor.tree = this;
37927         this.editor = Roo.factory(this.editor, Roo.tree);
37928     }
37929     
37930     if (this.selModel) {
37931         this.selModel = Roo.factory(this.selModel, Roo.tree);
37932     }
37933    
37934 };
37935 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
37936     rootVisible : true,
37937     animate: Roo.enableFx,
37938     lines : true,
37939     enableDD : false,
37940     hlDrop : Roo.enableFx,
37941   
37942     renderer: false,
37943     
37944     rendererTip: false,
37945     // private
37946     restrictExpand : function(node){
37947         var p = node.parentNode;
37948         if(p){
37949             if(p.expandedChild && p.expandedChild.parentNode == p){
37950                 p.expandedChild.collapse();
37951             }
37952             p.expandedChild = node;
37953         }
37954     },
37955
37956     // private override
37957     setRootNode : function(node){
37958         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
37959         if(!this.rootVisible){
37960             node.ui = new Roo.tree.RootTreeNodeUI(node);
37961         }
37962         return node;
37963     },
37964
37965     /**
37966      * Returns the container element for this TreePanel
37967      */
37968     getEl : function(){
37969         return this.el;
37970     },
37971
37972     /**
37973      * Returns the default TreeLoader for this TreePanel
37974      */
37975     getLoader : function(){
37976         return this.loader;
37977     },
37978
37979     /**
37980      * Expand all nodes
37981      */
37982     expandAll : function(){
37983         this.root.expand(true);
37984     },
37985
37986     /**
37987      * Collapse all nodes
37988      */
37989     collapseAll : function(){
37990         this.root.collapse(true);
37991     },
37992
37993     /**
37994      * Returns the selection model used by this TreePanel
37995      */
37996     getSelectionModel : function(){
37997         if(!this.selModel){
37998             this.selModel = new Roo.tree.DefaultSelectionModel();
37999         }
38000         return this.selModel;
38001     },
38002
38003     /**
38004      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
38005      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
38006      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
38007      * @return {Array}
38008      */
38009     getChecked : function(a, startNode){
38010         startNode = startNode || this.root;
38011         var r = [];
38012         var f = function(){
38013             if(this.attributes.checked){
38014                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
38015             }
38016         }
38017         startNode.cascade(f);
38018         return r;
38019     },
38020
38021     /**
38022      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
38023      * @param {String} path
38024      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
38025      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
38026      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
38027      */
38028     expandPath : function(path, attr, callback){
38029         attr = attr || "id";
38030         var keys = path.split(this.pathSeparator);
38031         var curNode = this.root;
38032         if(curNode.attributes[attr] != keys[1]){ // invalid root
38033             if(callback){
38034                 callback(false, null);
38035             }
38036             return;
38037         }
38038         var index = 1;
38039         var f = function(){
38040             if(++index == keys.length){
38041                 if(callback){
38042                     callback(true, curNode);
38043                 }
38044                 return;
38045             }
38046             var c = curNode.findChild(attr, keys[index]);
38047             if(!c){
38048                 if(callback){
38049                     callback(false, curNode);
38050                 }
38051                 return;
38052             }
38053             curNode = c;
38054             c.expand(false, false, f);
38055         };
38056         curNode.expand(false, false, f);
38057     },
38058
38059     /**
38060      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
38061      * @param {String} path
38062      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
38063      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
38064      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
38065      */
38066     selectPath : function(path, attr, callback){
38067         attr = attr || "id";
38068         var keys = path.split(this.pathSeparator);
38069         var v = keys.pop();
38070         if(keys.length > 0){
38071             var f = function(success, node){
38072                 if(success && node){
38073                     var n = node.findChild(attr, v);
38074                     if(n){
38075                         n.select();
38076                         if(callback){
38077                             callback(true, n);
38078                         }
38079                     }else if(callback){
38080                         callback(false, n);
38081                     }
38082                 }else{
38083                     if(callback){
38084                         callback(false, n);
38085                     }
38086                 }
38087             };
38088             this.expandPath(keys.join(this.pathSeparator), attr, f);
38089         }else{
38090             this.root.select();
38091             if(callback){
38092                 callback(true, this.root);
38093             }
38094         }
38095     },
38096
38097     getTreeEl : function(){
38098         return this.el;
38099     },
38100
38101     /**
38102      * Trigger rendering of this TreePanel
38103      */
38104     render : function(){
38105         if (this.innerCt) {
38106             return this; // stop it rendering more than once!!
38107         }
38108         
38109         this.innerCt = this.el.createChild({tag:"ul",
38110                cls:"x-tree-root-ct " +
38111                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
38112
38113         if(this.containerScroll){
38114             Roo.dd.ScrollManager.register(this.el);
38115         }
38116         if((this.enableDD || this.enableDrop) && !this.dropZone){
38117            /**
38118             * The dropZone used by this tree if drop is enabled
38119             * @type Roo.tree.TreeDropZone
38120             */
38121              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
38122                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
38123            });
38124         }
38125         if((this.enableDD || this.enableDrag) && !this.dragZone){
38126            /**
38127             * The dragZone used by this tree if drag is enabled
38128             * @type Roo.tree.TreeDragZone
38129             */
38130             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
38131                ddGroup: this.ddGroup || "TreeDD",
38132                scroll: this.ddScroll
38133            });
38134         }
38135         this.getSelectionModel().init(this);
38136         if (!this.root) {
38137             Roo.log("ROOT not set in tree");
38138             return this;
38139         }
38140         this.root.render();
38141         if(!this.rootVisible){
38142             this.root.renderChildren();
38143         }
38144         return this;
38145     }
38146 });/*
38147  * Based on:
38148  * Ext JS Library 1.1.1
38149  * Copyright(c) 2006-2007, Ext JS, LLC.
38150  *
38151  * Originally Released Under LGPL - original licence link has changed is not relivant.
38152  *
38153  * Fork - LGPL
38154  * <script type="text/javascript">
38155  */
38156  
38157
38158 /**
38159  * @class Roo.tree.DefaultSelectionModel
38160  * @extends Roo.util.Observable
38161  * The default single selection for a TreePanel.
38162  * @param {Object} cfg Configuration
38163  */
38164 Roo.tree.DefaultSelectionModel = function(cfg){
38165    this.selNode = null;
38166    
38167    
38168    
38169    this.addEvents({
38170        /**
38171         * @event selectionchange
38172         * Fires when the selected node changes
38173         * @param {DefaultSelectionModel} this
38174         * @param {TreeNode} node the new selection
38175         */
38176        "selectionchange" : true,
38177
38178        /**
38179         * @event beforeselect
38180         * Fires before the selected node changes, return false to cancel the change
38181         * @param {DefaultSelectionModel} this
38182         * @param {TreeNode} node the new selection
38183         * @param {TreeNode} node the old selection
38184         */
38185        "beforeselect" : true
38186    });
38187    
38188     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
38189 };
38190
38191 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
38192     init : function(tree){
38193         this.tree = tree;
38194         tree.getTreeEl().on("keydown", this.onKeyDown, this);
38195         tree.on("click", this.onNodeClick, this);
38196     },
38197     
38198     onNodeClick : function(node, e){
38199         if (e.ctrlKey && this.selNode == node)  {
38200             this.unselect(node);
38201             return;
38202         }
38203         this.select(node);
38204     },
38205     
38206     /**
38207      * Select a node.
38208      * @param {TreeNode} node The node to select
38209      * @return {TreeNode} The selected node
38210      */
38211     select : function(node){
38212         var last = this.selNode;
38213         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
38214             if(last){
38215                 last.ui.onSelectedChange(false);
38216             }
38217             this.selNode = node;
38218             node.ui.onSelectedChange(true);
38219             this.fireEvent("selectionchange", this, node, last);
38220         }
38221         return node;
38222     },
38223     
38224     /**
38225      * Deselect a node.
38226      * @param {TreeNode} node The node to unselect
38227      */
38228     unselect : function(node){
38229         if(this.selNode == node){
38230             this.clearSelections();
38231         }    
38232     },
38233     
38234     /**
38235      * Clear all selections
38236      */
38237     clearSelections : function(){
38238         var n = this.selNode;
38239         if(n){
38240             n.ui.onSelectedChange(false);
38241             this.selNode = null;
38242             this.fireEvent("selectionchange", this, null);
38243         }
38244         return n;
38245     },
38246     
38247     /**
38248      * Get the selected node
38249      * @return {TreeNode} The selected node
38250      */
38251     getSelectedNode : function(){
38252         return this.selNode;    
38253     },
38254     
38255     /**
38256      * Returns true if the node is selected
38257      * @param {TreeNode} node The node to check
38258      * @return {Boolean}
38259      */
38260     isSelected : function(node){
38261         return this.selNode == node;  
38262     },
38263
38264     /**
38265      * Selects the node above the selected node in the tree, intelligently walking the nodes
38266      * @return TreeNode The new selection
38267      */
38268     selectPrevious : function(){
38269         var s = this.selNode || this.lastSelNode;
38270         if(!s){
38271             return null;
38272         }
38273         var ps = s.previousSibling;
38274         if(ps){
38275             if(!ps.isExpanded() || ps.childNodes.length < 1){
38276                 return this.select(ps);
38277             } else{
38278                 var lc = ps.lastChild;
38279                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
38280                     lc = lc.lastChild;
38281                 }
38282                 return this.select(lc);
38283             }
38284         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
38285             return this.select(s.parentNode);
38286         }
38287         return null;
38288     },
38289
38290     /**
38291      * Selects the node above the selected node in the tree, intelligently walking the nodes
38292      * @return TreeNode The new selection
38293      */
38294     selectNext : function(){
38295         var s = this.selNode || this.lastSelNode;
38296         if(!s){
38297             return null;
38298         }
38299         if(s.firstChild && s.isExpanded()){
38300              return this.select(s.firstChild);
38301          }else if(s.nextSibling){
38302              return this.select(s.nextSibling);
38303          }else if(s.parentNode){
38304             var newS = null;
38305             s.parentNode.bubble(function(){
38306                 if(this.nextSibling){
38307                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
38308                     return false;
38309                 }
38310             });
38311             return newS;
38312          }
38313         return null;
38314     },
38315
38316     onKeyDown : function(e){
38317         var s = this.selNode || this.lastSelNode;
38318         // undesirable, but required
38319         var sm = this;
38320         if(!s){
38321             return;
38322         }
38323         var k = e.getKey();
38324         switch(k){
38325              case e.DOWN:
38326                  e.stopEvent();
38327                  this.selectNext();
38328              break;
38329              case e.UP:
38330                  e.stopEvent();
38331                  this.selectPrevious();
38332              break;
38333              case e.RIGHT:
38334                  e.preventDefault();
38335                  if(s.hasChildNodes()){
38336                      if(!s.isExpanded()){
38337                          s.expand();
38338                      }else if(s.firstChild){
38339                          this.select(s.firstChild, e);
38340                      }
38341                  }
38342              break;
38343              case e.LEFT:
38344                  e.preventDefault();
38345                  if(s.hasChildNodes() && s.isExpanded()){
38346                      s.collapse();
38347                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
38348                      this.select(s.parentNode, e);
38349                  }
38350              break;
38351         };
38352     }
38353 });
38354
38355 /**
38356  * @class Roo.tree.MultiSelectionModel
38357  * @extends Roo.util.Observable
38358  * Multi selection for a TreePanel.
38359  * @param {Object} cfg Configuration
38360  */
38361 Roo.tree.MultiSelectionModel = function(){
38362    this.selNodes = [];
38363    this.selMap = {};
38364    this.addEvents({
38365        /**
38366         * @event selectionchange
38367         * Fires when the selected nodes change
38368         * @param {MultiSelectionModel} this
38369         * @param {Array} nodes Array of the selected nodes
38370         */
38371        "selectionchange" : true
38372    });
38373    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
38374    
38375 };
38376
38377 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
38378     init : function(tree){
38379         this.tree = tree;
38380         tree.getTreeEl().on("keydown", this.onKeyDown, this);
38381         tree.on("click", this.onNodeClick, this);
38382     },
38383     
38384     onNodeClick : function(node, e){
38385         this.select(node, e, e.ctrlKey);
38386     },
38387     
38388     /**
38389      * Select a node.
38390      * @param {TreeNode} node The node to select
38391      * @param {EventObject} e (optional) An event associated with the selection
38392      * @param {Boolean} keepExisting True to retain existing selections
38393      * @return {TreeNode} The selected node
38394      */
38395     select : function(node, e, keepExisting){
38396         if(keepExisting !== true){
38397             this.clearSelections(true);
38398         }
38399         if(this.isSelected(node)){
38400             this.lastSelNode = node;
38401             return node;
38402         }
38403         this.selNodes.push(node);
38404         this.selMap[node.id] = node;
38405         this.lastSelNode = node;
38406         node.ui.onSelectedChange(true);
38407         this.fireEvent("selectionchange", this, this.selNodes);
38408         return node;
38409     },
38410     
38411     /**
38412      * Deselect a node.
38413      * @param {TreeNode} node The node to unselect
38414      */
38415     unselect : function(node){
38416         if(this.selMap[node.id]){
38417             node.ui.onSelectedChange(false);
38418             var sn = this.selNodes;
38419             var index = -1;
38420             if(sn.indexOf){
38421                 index = sn.indexOf(node);
38422             }else{
38423                 for(var i = 0, len = sn.length; i < len; i++){
38424                     if(sn[i] == node){
38425                         index = i;
38426                         break;
38427                     }
38428                 }
38429             }
38430             if(index != -1){
38431                 this.selNodes.splice(index, 1);
38432             }
38433             delete this.selMap[node.id];
38434             this.fireEvent("selectionchange", this, this.selNodes);
38435         }
38436     },
38437     
38438     /**
38439      * Clear all selections
38440      */
38441     clearSelections : function(suppressEvent){
38442         var sn = this.selNodes;
38443         if(sn.length > 0){
38444             for(var i = 0, len = sn.length; i < len; i++){
38445                 sn[i].ui.onSelectedChange(false);
38446             }
38447             this.selNodes = [];
38448             this.selMap = {};
38449             if(suppressEvent !== true){
38450                 this.fireEvent("selectionchange", this, this.selNodes);
38451             }
38452         }
38453     },
38454     
38455     /**
38456      * Returns true if the node is selected
38457      * @param {TreeNode} node The node to check
38458      * @return {Boolean}
38459      */
38460     isSelected : function(node){
38461         return this.selMap[node.id] ? true : false;  
38462     },
38463     
38464     /**
38465      * Returns an array of the selected nodes
38466      * @return {Array}
38467      */
38468     getSelectedNodes : function(){
38469         return this.selNodes;    
38470     },
38471
38472     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
38473
38474     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
38475
38476     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
38477 });/*
38478  * Based on:
38479  * Ext JS Library 1.1.1
38480  * Copyright(c) 2006-2007, Ext JS, LLC.
38481  *
38482  * Originally Released Under LGPL - original licence link has changed is not relivant.
38483  *
38484  * Fork - LGPL
38485  * <script type="text/javascript">
38486  */
38487  
38488 /**
38489  * @class Roo.tree.TreeNode
38490  * @extends Roo.data.Node
38491  * @cfg {String} text The text for this node
38492  * @cfg {Boolean} expanded true to start the node expanded
38493  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
38494  * @cfg {Boolean} allowDrop false if this node cannot be drop on
38495  * @cfg {Boolean} disabled true to start the node disabled
38496  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
38497  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
38498  * @cfg {String} cls A css class to be added to the node
38499  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
38500  * @cfg {String} href URL of the link used for the node (defaults to #)
38501  * @cfg {String} hrefTarget target frame for the link
38502  * @cfg {String} qtip An Ext QuickTip for the node
38503  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
38504  * @cfg {Boolean} singleClickExpand True for single click expand on this node
38505  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
38506  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
38507  * (defaults to undefined with no checkbox rendered)
38508  * @constructor
38509  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
38510  */
38511 Roo.tree.TreeNode = function(attributes){
38512     attributes = attributes || {};
38513     if(typeof attributes == "string"){
38514         attributes = {text: attributes};
38515     }
38516     this.childrenRendered = false;
38517     this.rendered = false;
38518     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
38519     this.expanded = attributes.expanded === true;
38520     this.isTarget = attributes.isTarget !== false;
38521     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
38522     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
38523
38524     /**
38525      * Read-only. The text for this node. To change it use setText().
38526      * @type String
38527      */
38528     this.text = attributes.text;
38529     /**
38530      * True if this node is disabled.
38531      * @type Boolean
38532      */
38533     this.disabled = attributes.disabled === true;
38534
38535     this.addEvents({
38536         /**
38537         * @event textchange
38538         * Fires when the text for this node is changed
38539         * @param {Node} this This node
38540         * @param {String} text The new text
38541         * @param {String} oldText The old text
38542         */
38543         "textchange" : true,
38544         /**
38545         * @event beforeexpand
38546         * Fires before this node is expanded, return false to cancel.
38547         * @param {Node} this This node
38548         * @param {Boolean} deep
38549         * @param {Boolean} anim
38550         */
38551         "beforeexpand" : true,
38552         /**
38553         * @event beforecollapse
38554         * Fires before this node is collapsed, return false to cancel.
38555         * @param {Node} this This node
38556         * @param {Boolean} deep
38557         * @param {Boolean} anim
38558         */
38559         "beforecollapse" : true,
38560         /**
38561         * @event expand
38562         * Fires when this node is expanded
38563         * @param {Node} this This node
38564         */
38565         "expand" : true,
38566         /**
38567         * @event disabledchange
38568         * Fires when the disabled status of this node changes
38569         * @param {Node} this This node
38570         * @param {Boolean} disabled
38571         */
38572         "disabledchange" : true,
38573         /**
38574         * @event collapse
38575         * Fires when this node is collapsed
38576         * @param {Node} this This node
38577         */
38578         "collapse" : true,
38579         /**
38580         * @event beforeclick
38581         * Fires before click processing. Return false to cancel the default action.
38582         * @param {Node} this This node
38583         * @param {Roo.EventObject} e The event object
38584         */
38585         "beforeclick":true,
38586         /**
38587         * @event checkchange
38588         * Fires when a node with a checkbox's checked property changes
38589         * @param {Node} this This node
38590         * @param {Boolean} checked
38591         */
38592         "checkchange":true,
38593         /**
38594         * @event click
38595         * Fires when this node is clicked
38596         * @param {Node} this This node
38597         * @param {Roo.EventObject} e The event object
38598         */
38599         "click":true,
38600         /**
38601         * @event dblclick
38602         * Fires when this node is double clicked
38603         * @param {Node} this This node
38604         * @param {Roo.EventObject} e The event object
38605         */
38606         "dblclick":true,
38607         /**
38608         * @event contextmenu
38609         * Fires when this node is right clicked
38610         * @param {Node} this This node
38611         * @param {Roo.EventObject} e The event object
38612         */
38613         "contextmenu":true,
38614         /**
38615         * @event beforechildrenrendered
38616         * Fires right before the child nodes for this node are rendered
38617         * @param {Node} this This node
38618         */
38619         "beforechildrenrendered":true
38620     });
38621
38622     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
38623
38624     /**
38625      * Read-only. The UI for this node
38626      * @type TreeNodeUI
38627      */
38628     this.ui = new uiClass(this);
38629     
38630     // finally support items[]
38631     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
38632         return;
38633     }
38634     
38635     
38636     Roo.each(this.attributes.items, function(c) {
38637         this.appendChild(Roo.factory(c,Roo.Tree));
38638     }, this);
38639     delete this.attributes.items;
38640     
38641     
38642     
38643 };
38644 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
38645     preventHScroll: true,
38646     /**
38647      * Returns true if this node is expanded
38648      * @return {Boolean}
38649      */
38650     isExpanded : function(){
38651         return this.expanded;
38652     },
38653
38654     /**
38655      * Returns the UI object for this node
38656      * @return {TreeNodeUI}
38657      */
38658     getUI : function(){
38659         return this.ui;
38660     },
38661
38662     // private override
38663     setFirstChild : function(node){
38664         var of = this.firstChild;
38665         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
38666         if(this.childrenRendered && of && node != of){
38667             of.renderIndent(true, true);
38668         }
38669         if(this.rendered){
38670             this.renderIndent(true, true);
38671         }
38672     },
38673
38674     // private override
38675     setLastChild : function(node){
38676         var ol = this.lastChild;
38677         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
38678         if(this.childrenRendered && ol && node != ol){
38679             ol.renderIndent(true, true);
38680         }
38681         if(this.rendered){
38682             this.renderIndent(true, true);
38683         }
38684     },
38685
38686     // these methods are overridden to provide lazy rendering support
38687     // private override
38688     appendChild : function()
38689     {
38690         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
38691         if(node && this.childrenRendered){
38692             node.render();
38693         }
38694         this.ui.updateExpandIcon();
38695         return node;
38696     },
38697
38698     // private override
38699     removeChild : function(node){
38700         this.ownerTree.getSelectionModel().unselect(node);
38701         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
38702         // if it's been rendered remove dom node
38703         if(this.childrenRendered){
38704             node.ui.remove();
38705         }
38706         if(this.childNodes.length < 1){
38707             this.collapse(false, false);
38708         }else{
38709             this.ui.updateExpandIcon();
38710         }
38711         if(!this.firstChild) {
38712             this.childrenRendered = false;
38713         }
38714         return node;
38715     },
38716
38717     // private override
38718     insertBefore : function(node, refNode){
38719         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
38720         if(newNode && refNode && this.childrenRendered){
38721             node.render();
38722         }
38723         this.ui.updateExpandIcon();
38724         return newNode;
38725     },
38726
38727     /**
38728      * Sets the text for this node
38729      * @param {String} text
38730      */
38731     setText : function(text){
38732         var oldText = this.text;
38733         this.text = text;
38734         this.attributes.text = text;
38735         if(this.rendered){ // event without subscribing
38736             this.ui.onTextChange(this, text, oldText);
38737         }
38738         this.fireEvent("textchange", this, text, oldText);
38739     },
38740
38741     /**
38742      * Triggers selection of this node
38743      */
38744     select : function(){
38745         this.getOwnerTree().getSelectionModel().select(this);
38746     },
38747
38748     /**
38749      * Triggers deselection of this node
38750      */
38751     unselect : function(){
38752         this.getOwnerTree().getSelectionModel().unselect(this);
38753     },
38754
38755     /**
38756      * Returns true if this node is selected
38757      * @return {Boolean}
38758      */
38759     isSelected : function(){
38760         return this.getOwnerTree().getSelectionModel().isSelected(this);
38761     },
38762
38763     /**
38764      * Expand this node.
38765      * @param {Boolean} deep (optional) True to expand all children as well
38766      * @param {Boolean} anim (optional) false to cancel the default animation
38767      * @param {Function} callback (optional) A callback to be called when
38768      * expanding this node completes (does not wait for deep expand to complete).
38769      * Called with 1 parameter, this node.
38770      */
38771     expand : function(deep, anim, callback){
38772         if(!this.expanded){
38773             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
38774                 return;
38775             }
38776             if(!this.childrenRendered){
38777                 this.renderChildren();
38778             }
38779             this.expanded = true;
38780             
38781             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
38782                 this.ui.animExpand(function(){
38783                     this.fireEvent("expand", this);
38784                     if(typeof callback == "function"){
38785                         callback(this);
38786                     }
38787                     if(deep === true){
38788                         this.expandChildNodes(true);
38789                     }
38790                 }.createDelegate(this));
38791                 return;
38792             }else{
38793                 this.ui.expand();
38794                 this.fireEvent("expand", this);
38795                 if(typeof callback == "function"){
38796                     callback(this);
38797                 }
38798             }
38799         }else{
38800            if(typeof callback == "function"){
38801                callback(this);
38802            }
38803         }
38804         if(deep === true){
38805             this.expandChildNodes(true);
38806         }
38807     },
38808
38809     isHiddenRoot : function(){
38810         return this.isRoot && !this.getOwnerTree().rootVisible;
38811     },
38812
38813     /**
38814      * Collapse this node.
38815      * @param {Boolean} deep (optional) True to collapse all children as well
38816      * @param {Boolean} anim (optional) false to cancel the default animation
38817      */
38818     collapse : function(deep, anim){
38819         if(this.expanded && !this.isHiddenRoot()){
38820             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
38821                 return;
38822             }
38823             this.expanded = false;
38824             if((this.getOwnerTree().animate && anim !== false) || anim){
38825                 this.ui.animCollapse(function(){
38826                     this.fireEvent("collapse", this);
38827                     if(deep === true){
38828                         this.collapseChildNodes(true);
38829                     }
38830                 }.createDelegate(this));
38831                 return;
38832             }else{
38833                 this.ui.collapse();
38834                 this.fireEvent("collapse", this);
38835             }
38836         }
38837         if(deep === true){
38838             var cs = this.childNodes;
38839             for(var i = 0, len = cs.length; i < len; i++) {
38840                 cs[i].collapse(true, false);
38841             }
38842         }
38843     },
38844
38845     // private
38846     delayedExpand : function(delay){
38847         if(!this.expandProcId){
38848             this.expandProcId = this.expand.defer(delay, this);
38849         }
38850     },
38851
38852     // private
38853     cancelExpand : function(){
38854         if(this.expandProcId){
38855             clearTimeout(this.expandProcId);
38856         }
38857         this.expandProcId = false;
38858     },
38859
38860     /**
38861      * Toggles expanded/collapsed state of the node
38862      */
38863     toggle : function(){
38864         if(this.expanded){
38865             this.collapse();
38866         }else{
38867             this.expand();
38868         }
38869     },
38870
38871     /**
38872      * Ensures all parent nodes are expanded
38873      */
38874     ensureVisible : function(callback){
38875         var tree = this.getOwnerTree();
38876         tree.expandPath(this.parentNode.getPath(), false, function(){
38877             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
38878             Roo.callback(callback);
38879         }.createDelegate(this));
38880     },
38881
38882     /**
38883      * Expand all child nodes
38884      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
38885      */
38886     expandChildNodes : function(deep){
38887         var cs = this.childNodes;
38888         for(var i = 0, len = cs.length; i < len; i++) {
38889                 cs[i].expand(deep);
38890         }
38891     },
38892
38893     /**
38894      * Collapse all child nodes
38895      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
38896      */
38897     collapseChildNodes : function(deep){
38898         var cs = this.childNodes;
38899         for(var i = 0, len = cs.length; i < len; i++) {
38900                 cs[i].collapse(deep);
38901         }
38902     },
38903
38904     /**
38905      * Disables this node
38906      */
38907     disable : function(){
38908         this.disabled = true;
38909         this.unselect();
38910         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
38911             this.ui.onDisableChange(this, true);
38912         }
38913         this.fireEvent("disabledchange", this, true);
38914     },
38915
38916     /**
38917      * Enables this node
38918      */
38919     enable : function(){
38920         this.disabled = false;
38921         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
38922             this.ui.onDisableChange(this, false);
38923         }
38924         this.fireEvent("disabledchange", this, false);
38925     },
38926
38927     // private
38928     renderChildren : function(suppressEvent){
38929         if(suppressEvent !== false){
38930             this.fireEvent("beforechildrenrendered", this);
38931         }
38932         var cs = this.childNodes;
38933         for(var i = 0, len = cs.length; i < len; i++){
38934             cs[i].render(true);
38935         }
38936         this.childrenRendered = true;
38937     },
38938
38939     // private
38940     sort : function(fn, scope){
38941         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
38942         if(this.childrenRendered){
38943             var cs = this.childNodes;
38944             for(var i = 0, len = cs.length; i < len; i++){
38945                 cs[i].render(true);
38946             }
38947         }
38948     },
38949
38950     // private
38951     render : function(bulkRender){
38952         this.ui.render(bulkRender);
38953         if(!this.rendered){
38954             this.rendered = true;
38955             if(this.expanded){
38956                 this.expanded = false;
38957                 this.expand(false, false);
38958             }
38959         }
38960     },
38961
38962     // private
38963     renderIndent : function(deep, refresh){
38964         if(refresh){
38965             this.ui.childIndent = null;
38966         }
38967         this.ui.renderIndent();
38968         if(deep === true && this.childrenRendered){
38969             var cs = this.childNodes;
38970             for(var i = 0, len = cs.length; i < len; i++){
38971                 cs[i].renderIndent(true, refresh);
38972             }
38973         }
38974     }
38975 });/*
38976  * Based on:
38977  * Ext JS Library 1.1.1
38978  * Copyright(c) 2006-2007, Ext JS, LLC.
38979  *
38980  * Originally Released Under LGPL - original licence link has changed is not relivant.
38981  *
38982  * Fork - LGPL
38983  * <script type="text/javascript">
38984  */
38985  
38986 /**
38987  * @class Roo.tree.AsyncTreeNode
38988  * @extends Roo.tree.TreeNode
38989  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
38990  * @constructor
38991  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
38992  */
38993  Roo.tree.AsyncTreeNode = function(config){
38994     this.loaded = false;
38995     this.loading = false;
38996     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
38997     /**
38998     * @event beforeload
38999     * Fires before this node is loaded, return false to cancel
39000     * @param {Node} this This node
39001     */
39002     this.addEvents({'beforeload':true, 'load': true});
39003     /**
39004     * @event load
39005     * Fires when this node is loaded
39006     * @param {Node} this This node
39007     */
39008     /**
39009      * The loader used by this node (defaults to using the tree's defined loader)
39010      * @type TreeLoader
39011      * @property loader
39012      */
39013 };
39014 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
39015     expand : function(deep, anim, callback){
39016         if(this.loading){ // if an async load is already running, waiting til it's done
39017             var timer;
39018             var f = function(){
39019                 if(!this.loading){ // done loading
39020                     clearInterval(timer);
39021                     this.expand(deep, anim, callback);
39022                 }
39023             }.createDelegate(this);
39024             timer = setInterval(f, 200);
39025             return;
39026         }
39027         if(!this.loaded){
39028             if(this.fireEvent("beforeload", this) === false){
39029                 return;
39030             }
39031             this.loading = true;
39032             this.ui.beforeLoad(this);
39033             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
39034             if(loader){
39035                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
39036                 return;
39037             }
39038         }
39039         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
39040     },
39041     
39042     /**
39043      * Returns true if this node is currently loading
39044      * @return {Boolean}
39045      */
39046     isLoading : function(){
39047         return this.loading;  
39048     },
39049     
39050     loadComplete : function(deep, anim, callback){
39051         this.loading = false;
39052         this.loaded = true;
39053         this.ui.afterLoad(this);
39054         this.fireEvent("load", this);
39055         this.expand(deep, anim, callback);
39056     },
39057     
39058     /**
39059      * Returns true if this node has been loaded
39060      * @return {Boolean}
39061      */
39062     isLoaded : function(){
39063         return this.loaded;
39064     },
39065     
39066     hasChildNodes : function(){
39067         if(!this.isLeaf() && !this.loaded){
39068             return true;
39069         }else{
39070             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
39071         }
39072     },
39073
39074     /**
39075      * Trigger a reload for this node
39076      * @param {Function} callback
39077      */
39078     reload : function(callback){
39079         this.collapse(false, false);
39080         while(this.firstChild){
39081             this.removeChild(this.firstChild);
39082         }
39083         this.childrenRendered = false;
39084         this.loaded = false;
39085         if(this.isHiddenRoot()){
39086             this.expanded = false;
39087         }
39088         this.expand(false, false, callback);
39089     }
39090 });/*
39091  * Based on:
39092  * Ext JS Library 1.1.1
39093  * Copyright(c) 2006-2007, Ext JS, LLC.
39094  *
39095  * Originally Released Under LGPL - original licence link has changed is not relivant.
39096  *
39097  * Fork - LGPL
39098  * <script type="text/javascript">
39099  */
39100  
39101 /**
39102  * @class Roo.tree.TreeNodeUI
39103  * @constructor
39104  * @param {Object} node The node to render
39105  * The TreeNode UI implementation is separate from the
39106  * tree implementation. Unless you are customizing the tree UI,
39107  * you should never have to use this directly.
39108  */
39109 Roo.tree.TreeNodeUI = function(node){
39110     this.node = node;
39111     this.rendered = false;
39112     this.animating = false;
39113     this.emptyIcon = Roo.BLANK_IMAGE_URL;
39114 };
39115
39116 Roo.tree.TreeNodeUI.prototype = {
39117     removeChild : function(node){
39118         if(this.rendered){
39119             this.ctNode.removeChild(node.ui.getEl());
39120         }
39121     },
39122
39123     beforeLoad : function(){
39124          this.addClass("x-tree-node-loading");
39125     },
39126
39127     afterLoad : function(){
39128          this.removeClass("x-tree-node-loading");
39129     },
39130
39131     onTextChange : function(node, text, oldText){
39132         if(this.rendered){
39133             this.textNode.innerHTML = text;
39134         }
39135     },
39136
39137     onDisableChange : function(node, state){
39138         this.disabled = state;
39139         if(state){
39140             this.addClass("x-tree-node-disabled");
39141         }else{
39142             this.removeClass("x-tree-node-disabled");
39143         }
39144     },
39145
39146     onSelectedChange : function(state){
39147         if(state){
39148             this.focus();
39149             this.addClass("x-tree-selected");
39150         }else{
39151             //this.blur();
39152             this.removeClass("x-tree-selected");
39153         }
39154     },
39155
39156     onMove : function(tree, node, oldParent, newParent, index, refNode){
39157         this.childIndent = null;
39158         if(this.rendered){
39159             var targetNode = newParent.ui.getContainer();
39160             if(!targetNode){//target not rendered
39161                 this.holder = document.createElement("div");
39162                 this.holder.appendChild(this.wrap);
39163                 return;
39164             }
39165             var insertBefore = refNode ? refNode.ui.getEl() : null;
39166             if(insertBefore){
39167                 targetNode.insertBefore(this.wrap, insertBefore);
39168             }else{
39169                 targetNode.appendChild(this.wrap);
39170             }
39171             this.node.renderIndent(true);
39172         }
39173     },
39174
39175     addClass : function(cls){
39176         if(this.elNode){
39177             Roo.fly(this.elNode).addClass(cls);
39178         }
39179     },
39180
39181     removeClass : function(cls){
39182         if(this.elNode){
39183             Roo.fly(this.elNode).removeClass(cls);
39184         }
39185     },
39186
39187     remove : function(){
39188         if(this.rendered){
39189             this.holder = document.createElement("div");
39190             this.holder.appendChild(this.wrap);
39191         }
39192     },
39193
39194     fireEvent : function(){
39195         return this.node.fireEvent.apply(this.node, arguments);
39196     },
39197
39198     initEvents : function(){
39199         this.node.on("move", this.onMove, this);
39200         var E = Roo.EventManager;
39201         var a = this.anchor;
39202
39203         var el = Roo.fly(a, '_treeui');
39204
39205         if(Roo.isOpera){ // opera render bug ignores the CSS
39206             el.setStyle("text-decoration", "none");
39207         }
39208
39209         el.on("click", this.onClick, this);
39210         el.on("dblclick", this.onDblClick, this);
39211
39212         if(this.checkbox){
39213             Roo.EventManager.on(this.checkbox,
39214                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
39215         }
39216
39217         el.on("contextmenu", this.onContextMenu, this);
39218
39219         var icon = Roo.fly(this.iconNode);
39220         icon.on("click", this.onClick, this);
39221         icon.on("dblclick", this.onDblClick, this);
39222         icon.on("contextmenu", this.onContextMenu, this);
39223         E.on(this.ecNode, "click", this.ecClick, this, true);
39224
39225         if(this.node.disabled){
39226             this.addClass("x-tree-node-disabled");
39227         }
39228         if(this.node.hidden){
39229             this.addClass("x-tree-node-disabled");
39230         }
39231         var ot = this.node.getOwnerTree();
39232         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
39233         if(dd && (!this.node.isRoot || ot.rootVisible)){
39234             Roo.dd.Registry.register(this.elNode, {
39235                 node: this.node,
39236                 handles: this.getDDHandles(),
39237                 isHandle: false
39238             });
39239         }
39240     },
39241
39242     getDDHandles : function(){
39243         return [this.iconNode, this.textNode];
39244     },
39245
39246     hide : function(){
39247         if(this.rendered){
39248             this.wrap.style.display = "none";
39249         }
39250     },
39251
39252     show : function(){
39253         if(this.rendered){
39254             this.wrap.style.display = "";
39255         }
39256     },
39257
39258     onContextMenu : function(e){
39259         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
39260             e.preventDefault();
39261             this.focus();
39262             this.fireEvent("contextmenu", this.node, e);
39263         }
39264     },
39265
39266     onClick : function(e){
39267         if(this.dropping){
39268             e.stopEvent();
39269             return;
39270         }
39271         if(this.fireEvent("beforeclick", this.node, e) !== false){
39272             if(!this.disabled && this.node.attributes.href){
39273                 this.fireEvent("click", this.node, e);
39274                 return;
39275             }
39276             e.preventDefault();
39277             if(this.disabled){
39278                 return;
39279             }
39280
39281             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
39282                 this.node.toggle();
39283             }
39284
39285             this.fireEvent("click", this.node, e);
39286         }else{
39287             e.stopEvent();
39288         }
39289     },
39290
39291     onDblClick : function(e){
39292         e.preventDefault();
39293         if(this.disabled){
39294             return;
39295         }
39296         if(this.checkbox){
39297             this.toggleCheck();
39298         }
39299         if(!this.animating && this.node.hasChildNodes()){
39300             this.node.toggle();
39301         }
39302         this.fireEvent("dblclick", this.node, e);
39303     },
39304
39305     onCheckChange : function(){
39306         var checked = this.checkbox.checked;
39307         this.node.attributes.checked = checked;
39308         this.fireEvent('checkchange', this.node, checked);
39309     },
39310
39311     ecClick : function(e){
39312         if(!this.animating && this.node.hasChildNodes()){
39313             this.node.toggle();
39314         }
39315     },
39316
39317     startDrop : function(){
39318         this.dropping = true;
39319     },
39320
39321     // delayed drop so the click event doesn't get fired on a drop
39322     endDrop : function(){
39323        setTimeout(function(){
39324            this.dropping = false;
39325        }.createDelegate(this), 50);
39326     },
39327
39328     expand : function(){
39329         this.updateExpandIcon();
39330         this.ctNode.style.display = "";
39331     },
39332
39333     focus : function(){
39334         if(!this.node.preventHScroll){
39335             try{this.anchor.focus();
39336             }catch(e){}
39337         }else if(!Roo.isIE){
39338             try{
39339                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
39340                 var l = noscroll.scrollLeft;
39341                 this.anchor.focus();
39342                 noscroll.scrollLeft = l;
39343             }catch(e){}
39344         }
39345     },
39346
39347     toggleCheck : function(value){
39348         var cb = this.checkbox;
39349         if(cb){
39350             cb.checked = (value === undefined ? !cb.checked : value);
39351         }
39352     },
39353
39354     blur : function(){
39355         try{
39356             this.anchor.blur();
39357         }catch(e){}
39358     },
39359
39360     animExpand : function(callback){
39361         var ct = Roo.get(this.ctNode);
39362         ct.stopFx();
39363         if(!this.node.hasChildNodes()){
39364             this.updateExpandIcon();
39365             this.ctNode.style.display = "";
39366             Roo.callback(callback);
39367             return;
39368         }
39369         this.animating = true;
39370         this.updateExpandIcon();
39371
39372         ct.slideIn('t', {
39373            callback : function(){
39374                this.animating = false;
39375                Roo.callback(callback);
39376             },
39377             scope: this,
39378             duration: this.node.ownerTree.duration || .25
39379         });
39380     },
39381
39382     highlight : function(){
39383         var tree = this.node.getOwnerTree();
39384         Roo.fly(this.wrap).highlight(
39385             tree.hlColor || "C3DAF9",
39386             {endColor: tree.hlBaseColor}
39387         );
39388     },
39389
39390     collapse : function(){
39391         this.updateExpandIcon();
39392         this.ctNode.style.display = "none";
39393     },
39394
39395     animCollapse : function(callback){
39396         var ct = Roo.get(this.ctNode);
39397         ct.enableDisplayMode('block');
39398         ct.stopFx();
39399
39400         this.animating = true;
39401         this.updateExpandIcon();
39402
39403         ct.slideOut('t', {
39404             callback : function(){
39405                this.animating = false;
39406                Roo.callback(callback);
39407             },
39408             scope: this,
39409             duration: this.node.ownerTree.duration || .25
39410         });
39411     },
39412
39413     getContainer : function(){
39414         return this.ctNode;
39415     },
39416
39417     getEl : function(){
39418         return this.wrap;
39419     },
39420
39421     appendDDGhost : function(ghostNode){
39422         ghostNode.appendChild(this.elNode.cloneNode(true));
39423     },
39424
39425     getDDRepairXY : function(){
39426         return Roo.lib.Dom.getXY(this.iconNode);
39427     },
39428
39429     onRender : function(){
39430         this.render();
39431     },
39432
39433     render : function(bulkRender){
39434         var n = this.node, a = n.attributes;
39435         var targetNode = n.parentNode ?
39436               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
39437
39438         if(!this.rendered){
39439             this.rendered = true;
39440
39441             this.renderElements(n, a, targetNode, bulkRender);
39442
39443             if(a.qtip){
39444                if(this.textNode.setAttributeNS){
39445                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
39446                    if(a.qtipTitle){
39447                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
39448                    }
39449                }else{
39450                    this.textNode.setAttribute("ext:qtip", a.qtip);
39451                    if(a.qtipTitle){
39452                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
39453                    }
39454                }
39455             }else if(a.qtipCfg){
39456                 a.qtipCfg.target = Roo.id(this.textNode);
39457                 Roo.QuickTips.register(a.qtipCfg);
39458             }
39459             this.initEvents();
39460             if(!this.node.expanded){
39461                 this.updateExpandIcon();
39462             }
39463         }else{
39464             if(bulkRender === true) {
39465                 targetNode.appendChild(this.wrap);
39466             }
39467         }
39468     },
39469
39470     renderElements : function(n, a, targetNode, bulkRender)
39471     {
39472         // add some indent caching, this helps performance when rendering a large tree
39473         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
39474         var t = n.getOwnerTree();
39475         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
39476         if (typeof(n.attributes.html) != 'undefined') {
39477             txt = n.attributes.html;
39478         }
39479         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
39480         var cb = typeof a.checked == 'boolean';
39481         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
39482         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
39483             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
39484             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
39485             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
39486             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
39487             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
39488              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
39489                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
39490             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
39491             "</li>"];
39492
39493         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
39494             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
39495                                 n.nextSibling.ui.getEl(), buf.join(""));
39496         }else{
39497             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
39498         }
39499
39500         this.elNode = this.wrap.childNodes[0];
39501         this.ctNode = this.wrap.childNodes[1];
39502         var cs = this.elNode.childNodes;
39503         this.indentNode = cs[0];
39504         this.ecNode = cs[1];
39505         this.iconNode = cs[2];
39506         var index = 3;
39507         if(cb){
39508             this.checkbox = cs[3];
39509             index++;
39510         }
39511         this.anchor = cs[index];
39512         this.textNode = cs[index].firstChild;
39513     },
39514
39515     getAnchor : function(){
39516         return this.anchor;
39517     },
39518
39519     getTextEl : function(){
39520         return this.textNode;
39521     },
39522
39523     getIconEl : function(){
39524         return this.iconNode;
39525     },
39526
39527     isChecked : function(){
39528         return this.checkbox ? this.checkbox.checked : false;
39529     },
39530
39531     updateExpandIcon : function(){
39532         if(this.rendered){
39533             var n = this.node, c1, c2;
39534             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
39535             var hasChild = n.hasChildNodes();
39536             if(hasChild){
39537                 if(n.expanded){
39538                     cls += "-minus";
39539                     c1 = "x-tree-node-collapsed";
39540                     c2 = "x-tree-node-expanded";
39541                 }else{
39542                     cls += "-plus";
39543                     c1 = "x-tree-node-expanded";
39544                     c2 = "x-tree-node-collapsed";
39545                 }
39546                 if(this.wasLeaf){
39547                     this.removeClass("x-tree-node-leaf");
39548                     this.wasLeaf = false;
39549                 }
39550                 if(this.c1 != c1 || this.c2 != c2){
39551                     Roo.fly(this.elNode).replaceClass(c1, c2);
39552                     this.c1 = c1; this.c2 = c2;
39553                 }
39554             }else{
39555                 // this changes non-leafs into leafs if they have no children.
39556                 // it's not very rational behaviour..
39557                 
39558                 if(!this.wasLeaf && this.node.leaf){
39559                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
39560                     delete this.c1;
39561                     delete this.c2;
39562                     this.wasLeaf = true;
39563                 }
39564             }
39565             var ecc = "x-tree-ec-icon "+cls;
39566             if(this.ecc != ecc){
39567                 this.ecNode.className = ecc;
39568                 this.ecc = ecc;
39569             }
39570         }
39571     },
39572
39573     getChildIndent : function(){
39574         if(!this.childIndent){
39575             var buf = [];
39576             var p = this.node;
39577             while(p){
39578                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
39579                     if(!p.isLast()) {
39580                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
39581                     } else {
39582                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
39583                     }
39584                 }
39585                 p = p.parentNode;
39586             }
39587             this.childIndent = buf.join("");
39588         }
39589         return this.childIndent;
39590     },
39591
39592     renderIndent : function(){
39593         if(this.rendered){
39594             var indent = "";
39595             var p = this.node.parentNode;
39596             if(p){
39597                 indent = p.ui.getChildIndent();
39598             }
39599             if(this.indentMarkup != indent){ // don't rerender if not required
39600                 this.indentNode.innerHTML = indent;
39601                 this.indentMarkup = indent;
39602             }
39603             this.updateExpandIcon();
39604         }
39605     }
39606 };
39607
39608 Roo.tree.RootTreeNodeUI = function(){
39609     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
39610 };
39611 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
39612     render : function(){
39613         if(!this.rendered){
39614             var targetNode = this.node.ownerTree.innerCt.dom;
39615             this.node.expanded = true;
39616             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
39617             this.wrap = this.ctNode = targetNode.firstChild;
39618         }
39619     },
39620     collapse : function(){
39621     },
39622     expand : function(){
39623     }
39624 });/*
39625  * Based on:
39626  * Ext JS Library 1.1.1
39627  * Copyright(c) 2006-2007, Ext JS, LLC.
39628  *
39629  * Originally Released Under LGPL - original licence link has changed is not relivant.
39630  *
39631  * Fork - LGPL
39632  * <script type="text/javascript">
39633  */
39634 /**
39635  * @class Roo.tree.TreeLoader
39636  * @extends Roo.util.Observable
39637  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
39638  * nodes from a specified URL. The response must be a javascript Array definition
39639  * who's elements are node definition objects. eg:
39640  * <pre><code>
39641 {  success : true,
39642    data :      [
39643    
39644     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
39645     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
39646     ]
39647 }
39648
39649
39650 </code></pre>
39651  * <br><br>
39652  * The old style respose with just an array is still supported, but not recommended.
39653  * <br><br>
39654  *
39655  * A server request is sent, and child nodes are loaded only when a node is expanded.
39656  * The loading node's id is passed to the server under the parameter name "node" to
39657  * enable the server to produce the correct child nodes.
39658  * <br><br>
39659  * To pass extra parameters, an event handler may be attached to the "beforeload"
39660  * event, and the parameters specified in the TreeLoader's baseParams property:
39661  * <pre><code>
39662     myTreeLoader.on("beforeload", function(treeLoader, node) {
39663         this.baseParams.category = node.attributes.category;
39664     }, this);
39665     
39666 </code></pre>
39667  *
39668  * This would pass an HTTP parameter called "category" to the server containing
39669  * the value of the Node's "category" attribute.
39670  * @constructor
39671  * Creates a new Treeloader.
39672  * @param {Object} config A config object containing config properties.
39673  */
39674 Roo.tree.TreeLoader = function(config){
39675     this.baseParams = {};
39676     this.requestMethod = "POST";
39677     Roo.apply(this, config);
39678
39679     this.addEvents({
39680     
39681         /**
39682          * @event beforeload
39683          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
39684          * @param {Object} This TreeLoader object.
39685          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
39686          * @param {Object} callback The callback function specified in the {@link #load} call.
39687          */
39688         beforeload : true,
39689         /**
39690          * @event load
39691          * Fires when the node has been successfuly loaded.
39692          * @param {Object} This TreeLoader object.
39693          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
39694          * @param {Object} response The response object containing the data from the server.
39695          */
39696         load : true,
39697         /**
39698          * @event loadexception
39699          * Fires if the network request failed.
39700          * @param {Object} This TreeLoader object.
39701          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
39702          * @param {Object} response The response object containing the data from the server.
39703          */
39704         loadexception : true,
39705         /**
39706          * @event create
39707          * Fires before a node is created, enabling you to return custom Node types 
39708          * @param {Object} This TreeLoader object.
39709          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
39710          */
39711         create : true
39712     });
39713
39714     Roo.tree.TreeLoader.superclass.constructor.call(this);
39715 };
39716
39717 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
39718     /**
39719     * @cfg {String} dataUrl The URL from which to request a Json string which
39720     * specifies an array of node definition object representing the child nodes
39721     * to be loaded.
39722     */
39723     /**
39724     * @cfg {String} requestMethod either GET or POST
39725     * defaults to POST (due to BC)
39726     * to be loaded.
39727     */
39728     /**
39729     * @cfg {Object} baseParams (optional) An object containing properties which
39730     * specify HTTP parameters to be passed to each request for child nodes.
39731     */
39732     /**
39733     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
39734     * created by this loader. If the attributes sent by the server have an attribute in this object,
39735     * they take priority.
39736     */
39737     /**
39738     * @cfg {Object} uiProviders (optional) An object containing properties which
39739     * 
39740     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
39741     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
39742     * <i>uiProvider</i> attribute of a returned child node is a string rather
39743     * than a reference to a TreeNodeUI implementation, this that string value
39744     * is used as a property name in the uiProviders object. You can define the provider named
39745     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
39746     */
39747     uiProviders : {},
39748
39749     /**
39750     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
39751     * child nodes before loading.
39752     */
39753     clearOnLoad : true,
39754
39755     /**
39756     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
39757     * property on loading, rather than expecting an array. (eg. more compatible to a standard
39758     * Grid query { data : [ .....] }
39759     */
39760     
39761     root : false,
39762      /**
39763     * @cfg {String} queryParam (optional) 
39764     * Name of the query as it will be passed on the querystring (defaults to 'node')
39765     * eg. the request will be ?node=[id]
39766     */
39767     
39768     
39769     queryParam: false,
39770     
39771     /**
39772      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
39773      * This is called automatically when a node is expanded, but may be used to reload
39774      * a node (or append new children if the {@link #clearOnLoad} option is false.)
39775      * @param {Roo.tree.TreeNode} node
39776      * @param {Function} callback
39777      */
39778     load : function(node, callback){
39779         if(this.clearOnLoad){
39780             while(node.firstChild){
39781                 node.removeChild(node.firstChild);
39782             }
39783         }
39784         if(node.attributes.children){ // preloaded json children
39785             var cs = node.attributes.children;
39786             for(var i = 0, len = cs.length; i < len; i++){
39787                 node.appendChild(this.createNode(cs[i]));
39788             }
39789             if(typeof callback == "function"){
39790                 callback();
39791             }
39792         }else if(this.dataUrl){
39793             this.requestData(node, callback);
39794         }
39795     },
39796
39797     getParams: function(node){
39798         var buf = [], bp = this.baseParams;
39799         for(var key in bp){
39800             if(typeof bp[key] != "function"){
39801                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
39802             }
39803         }
39804         var n = this.queryParam === false ? 'node' : this.queryParam;
39805         buf.push(n + "=", encodeURIComponent(node.id));
39806         return buf.join("");
39807     },
39808
39809     requestData : function(node, callback){
39810         if(this.fireEvent("beforeload", this, node, callback) !== false){
39811             this.transId = Roo.Ajax.request({
39812                 method:this.requestMethod,
39813                 url: this.dataUrl||this.url,
39814                 success: this.handleResponse,
39815                 failure: this.handleFailure,
39816                 scope: this,
39817                 argument: {callback: callback, node: node},
39818                 params: this.getParams(node)
39819             });
39820         }else{
39821             // if the load is cancelled, make sure we notify
39822             // the node that we are done
39823             if(typeof callback == "function"){
39824                 callback();
39825             }
39826         }
39827     },
39828
39829     isLoading : function(){
39830         return this.transId ? true : false;
39831     },
39832
39833     abort : function(){
39834         if(this.isLoading()){
39835             Roo.Ajax.abort(this.transId);
39836         }
39837     },
39838
39839     // private
39840     createNode : function(attr)
39841     {
39842         // apply baseAttrs, nice idea Corey!
39843         if(this.baseAttrs){
39844             Roo.applyIf(attr, this.baseAttrs);
39845         }
39846         if(this.applyLoader !== false){
39847             attr.loader = this;
39848         }
39849         // uiProvider = depreciated..
39850         
39851         if(typeof(attr.uiProvider) == 'string'){
39852            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
39853                 /**  eval:var:attr */ eval(attr.uiProvider);
39854         }
39855         if(typeof(this.uiProviders['default']) != 'undefined') {
39856             attr.uiProvider = this.uiProviders['default'];
39857         }
39858         
39859         this.fireEvent('create', this, attr);
39860         
39861         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
39862         return(attr.leaf ?
39863                         new Roo.tree.TreeNode(attr) :
39864                         new Roo.tree.AsyncTreeNode(attr));
39865     },
39866
39867     processResponse : function(response, node, callback)
39868     {
39869         var json = response.responseText;
39870         try {
39871             
39872             var o = Roo.decode(json);
39873             
39874             if (this.root === false && typeof(o.success) != undefined) {
39875                 this.root = 'data'; // the default behaviour for list like data..
39876                 }
39877                 
39878             if (this.root !== false &&  !o.success) {
39879                 // it's a failure condition.
39880                 var a = response.argument;
39881                 this.fireEvent("loadexception", this, a.node, response);
39882                 Roo.log("Load failed - should have a handler really");
39883                 return;
39884             }
39885             
39886             
39887             
39888             if (this.root !== false) {
39889                  o = o[this.root];
39890             }
39891             
39892             for(var i = 0, len = o.length; i < len; i++){
39893                 var n = this.createNode(o[i]);
39894                 if(n){
39895                     node.appendChild(n);
39896                 }
39897             }
39898             if(typeof callback == "function"){
39899                 callback(this, node);
39900             }
39901         }catch(e){
39902             this.handleFailure(response);
39903         }
39904     },
39905
39906     handleResponse : function(response){
39907         this.transId = false;
39908         var a = response.argument;
39909         this.processResponse(response, a.node, a.callback);
39910         this.fireEvent("load", this, a.node, response);
39911     },
39912
39913     handleFailure : function(response)
39914     {
39915         // should handle failure better..
39916         this.transId = false;
39917         var a = response.argument;
39918         this.fireEvent("loadexception", this, a.node, response);
39919         if(typeof a.callback == "function"){
39920             a.callback(this, a.node);
39921         }
39922     }
39923 });/*
39924  * Based on:
39925  * Ext JS Library 1.1.1
39926  * Copyright(c) 2006-2007, Ext JS, LLC.
39927  *
39928  * Originally Released Under LGPL - original licence link has changed is not relivant.
39929  *
39930  * Fork - LGPL
39931  * <script type="text/javascript">
39932  */
39933
39934 /**
39935 * @class Roo.tree.TreeFilter
39936 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
39937 * @param {TreePanel} tree
39938 * @param {Object} config (optional)
39939  */
39940 Roo.tree.TreeFilter = function(tree, config){
39941     this.tree = tree;
39942     this.filtered = {};
39943     Roo.apply(this, config);
39944 };
39945
39946 Roo.tree.TreeFilter.prototype = {
39947     clearBlank:false,
39948     reverse:false,
39949     autoClear:false,
39950     remove:false,
39951
39952      /**
39953      * Filter the data by a specific attribute.
39954      * @param {String/RegExp} value Either string that the attribute value
39955      * should start with or a RegExp to test against the attribute
39956      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
39957      * @param {TreeNode} startNode (optional) The node to start the filter at.
39958      */
39959     filter : function(value, attr, startNode){
39960         attr = attr || "text";
39961         var f;
39962         if(typeof value == "string"){
39963             var vlen = value.length;
39964             // auto clear empty filter
39965             if(vlen == 0 && this.clearBlank){
39966                 this.clear();
39967                 return;
39968             }
39969             value = value.toLowerCase();
39970             f = function(n){
39971                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
39972             };
39973         }else if(value.exec){ // regex?
39974             f = function(n){
39975                 return value.test(n.attributes[attr]);
39976             };
39977         }else{
39978             throw 'Illegal filter type, must be string or regex';
39979         }
39980         this.filterBy(f, null, startNode);
39981         },
39982
39983     /**
39984      * Filter by a function. The passed function will be called with each
39985      * node in the tree (or from the startNode). If the function returns true, the node is kept
39986      * otherwise it is filtered. If a node is filtered, its children are also filtered.
39987      * @param {Function} fn The filter function
39988      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
39989      */
39990     filterBy : function(fn, scope, startNode){
39991         startNode = startNode || this.tree.root;
39992         if(this.autoClear){
39993             this.clear();
39994         }
39995         var af = this.filtered, rv = this.reverse;
39996         var f = function(n){
39997             if(n == startNode){
39998                 return true;
39999             }
40000             if(af[n.id]){
40001                 return false;
40002             }
40003             var m = fn.call(scope || n, n);
40004             if(!m || rv){
40005                 af[n.id] = n;
40006                 n.ui.hide();
40007                 return false;
40008             }
40009             return true;
40010         };
40011         startNode.cascade(f);
40012         if(this.remove){
40013            for(var id in af){
40014                if(typeof id != "function"){
40015                    var n = af[id];
40016                    if(n && n.parentNode){
40017                        n.parentNode.removeChild(n);
40018                    }
40019                }
40020            }
40021         }
40022     },
40023
40024     /**
40025      * Clears the current filter. Note: with the "remove" option
40026      * set a filter cannot be cleared.
40027      */
40028     clear : function(){
40029         var t = this.tree;
40030         var af = this.filtered;
40031         for(var id in af){
40032             if(typeof id != "function"){
40033                 var n = af[id];
40034                 if(n){
40035                     n.ui.show();
40036                 }
40037             }
40038         }
40039         this.filtered = {};
40040     }
40041 };
40042 /*
40043  * Based on:
40044  * Ext JS Library 1.1.1
40045  * Copyright(c) 2006-2007, Ext JS, LLC.
40046  *
40047  * Originally Released Under LGPL - original licence link has changed is not relivant.
40048  *
40049  * Fork - LGPL
40050  * <script type="text/javascript">
40051  */
40052  
40053
40054 /**
40055  * @class Roo.tree.TreeSorter
40056  * Provides sorting of nodes in a TreePanel
40057  * 
40058  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
40059  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
40060  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
40061  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
40062  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
40063  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
40064  * @constructor
40065  * @param {TreePanel} tree
40066  * @param {Object} config
40067  */
40068 Roo.tree.TreeSorter = function(tree, config){
40069     Roo.apply(this, config);
40070     tree.on("beforechildrenrendered", this.doSort, this);
40071     tree.on("append", this.updateSort, this);
40072     tree.on("insert", this.updateSort, this);
40073     
40074     var dsc = this.dir && this.dir.toLowerCase() == "desc";
40075     var p = this.property || "text";
40076     var sortType = this.sortType;
40077     var fs = this.folderSort;
40078     var cs = this.caseSensitive === true;
40079     var leafAttr = this.leafAttr || 'leaf';
40080
40081     this.sortFn = function(n1, n2){
40082         if(fs){
40083             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
40084                 return 1;
40085             }
40086             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
40087                 return -1;
40088             }
40089         }
40090         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
40091         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
40092         if(v1 < v2){
40093                         return dsc ? +1 : -1;
40094                 }else if(v1 > v2){
40095                         return dsc ? -1 : +1;
40096         }else{
40097                 return 0;
40098         }
40099     };
40100 };
40101
40102 Roo.tree.TreeSorter.prototype = {
40103     doSort : function(node){
40104         node.sort(this.sortFn);
40105     },
40106     
40107     compareNodes : function(n1, n2){
40108         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
40109     },
40110     
40111     updateSort : function(tree, node){
40112         if(node.childrenRendered){
40113             this.doSort.defer(1, this, [node]);
40114         }
40115     }
40116 };/*
40117  * Based on:
40118  * Ext JS Library 1.1.1
40119  * Copyright(c) 2006-2007, Ext JS, LLC.
40120  *
40121  * Originally Released Under LGPL - original licence link has changed is not relivant.
40122  *
40123  * Fork - LGPL
40124  * <script type="text/javascript">
40125  */
40126
40127 if(Roo.dd.DropZone){
40128     
40129 Roo.tree.TreeDropZone = function(tree, config){
40130     this.allowParentInsert = false;
40131     this.allowContainerDrop = false;
40132     this.appendOnly = false;
40133     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
40134     this.tree = tree;
40135     this.lastInsertClass = "x-tree-no-status";
40136     this.dragOverData = {};
40137 };
40138
40139 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
40140     ddGroup : "TreeDD",
40141     scroll:  true,
40142     
40143     expandDelay : 1000,
40144     
40145     expandNode : function(node){
40146         if(node.hasChildNodes() && !node.isExpanded()){
40147             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
40148         }
40149     },
40150     
40151     queueExpand : function(node){
40152         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
40153     },
40154     
40155     cancelExpand : function(){
40156         if(this.expandProcId){
40157             clearTimeout(this.expandProcId);
40158             this.expandProcId = false;
40159         }
40160     },
40161     
40162     isValidDropPoint : function(n, pt, dd, e, data){
40163         if(!n || !data){ return false; }
40164         var targetNode = n.node;
40165         var dropNode = data.node;
40166         // default drop rules
40167         if(!(targetNode && targetNode.isTarget && pt)){
40168             return false;
40169         }
40170         if(pt == "append" && targetNode.allowChildren === false){
40171             return false;
40172         }
40173         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
40174             return false;
40175         }
40176         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
40177             return false;
40178         }
40179         // reuse the object
40180         var overEvent = this.dragOverData;
40181         overEvent.tree = this.tree;
40182         overEvent.target = targetNode;
40183         overEvent.data = data;
40184         overEvent.point = pt;
40185         overEvent.source = dd;
40186         overEvent.rawEvent = e;
40187         overEvent.dropNode = dropNode;
40188         overEvent.cancel = false;  
40189         var result = this.tree.fireEvent("nodedragover", overEvent);
40190         return overEvent.cancel === false && result !== false;
40191     },
40192     
40193     getDropPoint : function(e, n, dd)
40194     {
40195         var tn = n.node;
40196         if(tn.isRoot){
40197             return tn.allowChildren !== false ? "append" : false; // always append for root
40198         }
40199         var dragEl = n.ddel;
40200         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
40201         var y = Roo.lib.Event.getPageY(e);
40202         //var noAppend = tn.allowChildren === false || tn.isLeaf();
40203         
40204         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
40205         var noAppend = tn.allowChildren === false;
40206         if(this.appendOnly || tn.parentNode.allowChildren === false){
40207             return noAppend ? false : "append";
40208         }
40209         var noBelow = false;
40210         if(!this.allowParentInsert){
40211             noBelow = tn.hasChildNodes() && tn.isExpanded();
40212         }
40213         var q = (b - t) / (noAppend ? 2 : 3);
40214         if(y >= t && y < (t + q)){
40215             return "above";
40216         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
40217             return "below";
40218         }else{
40219             return "append";
40220         }
40221     },
40222     
40223     onNodeEnter : function(n, dd, e, data)
40224     {
40225         this.cancelExpand();
40226     },
40227     
40228     onNodeOver : function(n, dd, e, data)
40229     {
40230        
40231         var pt = this.getDropPoint(e, n, dd);
40232         var node = n.node;
40233         
40234         // auto node expand check
40235         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
40236             this.queueExpand(node);
40237         }else if(pt != "append"){
40238             this.cancelExpand();
40239         }
40240         
40241         // set the insert point style on the target node
40242         var returnCls = this.dropNotAllowed;
40243         if(this.isValidDropPoint(n, pt, dd, e, data)){
40244            if(pt){
40245                var el = n.ddel;
40246                var cls;
40247                if(pt == "above"){
40248                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
40249                    cls = "x-tree-drag-insert-above";
40250                }else if(pt == "below"){
40251                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
40252                    cls = "x-tree-drag-insert-below";
40253                }else{
40254                    returnCls = "x-tree-drop-ok-append";
40255                    cls = "x-tree-drag-append";
40256                }
40257                if(this.lastInsertClass != cls){
40258                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
40259                    this.lastInsertClass = cls;
40260                }
40261            }
40262        }
40263        return returnCls;
40264     },
40265     
40266     onNodeOut : function(n, dd, e, data){
40267         
40268         this.cancelExpand();
40269         this.removeDropIndicators(n);
40270     },
40271     
40272     onNodeDrop : function(n, dd, e, data){
40273         var point = this.getDropPoint(e, n, dd);
40274         var targetNode = n.node;
40275         targetNode.ui.startDrop();
40276         if(!this.isValidDropPoint(n, point, dd, e, data)){
40277             targetNode.ui.endDrop();
40278             return false;
40279         }
40280         // first try to find the drop node
40281         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
40282         var dropEvent = {
40283             tree : this.tree,
40284             target: targetNode,
40285             data: data,
40286             point: point,
40287             source: dd,
40288             rawEvent: e,
40289             dropNode: dropNode,
40290             cancel: !dropNode   
40291         };
40292         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
40293         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
40294             targetNode.ui.endDrop();
40295             return false;
40296         }
40297         // allow target changing
40298         targetNode = dropEvent.target;
40299         if(point == "append" && !targetNode.isExpanded()){
40300             targetNode.expand(false, null, function(){
40301                 this.completeDrop(dropEvent);
40302             }.createDelegate(this));
40303         }else{
40304             this.completeDrop(dropEvent);
40305         }
40306         return true;
40307     },
40308     
40309     completeDrop : function(de){
40310         var ns = de.dropNode, p = de.point, t = de.target;
40311         if(!(ns instanceof Array)){
40312             ns = [ns];
40313         }
40314         var n;
40315         for(var i = 0, len = ns.length; i < len; i++){
40316             n = ns[i];
40317             if(p == "above"){
40318                 t.parentNode.insertBefore(n, t);
40319             }else if(p == "below"){
40320                 t.parentNode.insertBefore(n, t.nextSibling);
40321             }else{
40322                 t.appendChild(n);
40323             }
40324         }
40325         n.ui.focus();
40326         if(this.tree.hlDrop){
40327             n.ui.highlight();
40328         }
40329         t.ui.endDrop();
40330         this.tree.fireEvent("nodedrop", de);
40331     },
40332     
40333     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
40334         if(this.tree.hlDrop){
40335             dropNode.ui.focus();
40336             dropNode.ui.highlight();
40337         }
40338         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
40339     },
40340     
40341     getTree : function(){
40342         return this.tree;
40343     },
40344     
40345     removeDropIndicators : function(n){
40346         if(n && n.ddel){
40347             var el = n.ddel;
40348             Roo.fly(el).removeClass([
40349                     "x-tree-drag-insert-above",
40350                     "x-tree-drag-insert-below",
40351                     "x-tree-drag-append"]);
40352             this.lastInsertClass = "_noclass";
40353         }
40354     },
40355     
40356     beforeDragDrop : function(target, e, id){
40357         this.cancelExpand();
40358         return true;
40359     },
40360     
40361     afterRepair : function(data){
40362         if(data && Roo.enableFx){
40363             data.node.ui.highlight();
40364         }
40365         this.hideProxy();
40366     } 
40367     
40368 });
40369
40370 }
40371 /*
40372  * Based on:
40373  * Ext JS Library 1.1.1
40374  * Copyright(c) 2006-2007, Ext JS, LLC.
40375  *
40376  * Originally Released Under LGPL - original licence link has changed is not relivant.
40377  *
40378  * Fork - LGPL
40379  * <script type="text/javascript">
40380  */
40381  
40382
40383 if(Roo.dd.DragZone){
40384 Roo.tree.TreeDragZone = function(tree, config){
40385     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
40386     this.tree = tree;
40387 };
40388
40389 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
40390     ddGroup : "TreeDD",
40391    
40392     onBeforeDrag : function(data, e){
40393         var n = data.node;
40394         return n && n.draggable && !n.disabled;
40395     },
40396      
40397     
40398     onInitDrag : function(e){
40399         var data = this.dragData;
40400         this.tree.getSelectionModel().select(data.node);
40401         this.proxy.update("");
40402         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
40403         this.tree.fireEvent("startdrag", this.tree, data.node, e);
40404     },
40405     
40406     getRepairXY : function(e, data){
40407         return data.node.ui.getDDRepairXY();
40408     },
40409     
40410     onEndDrag : function(data, e){
40411         this.tree.fireEvent("enddrag", this.tree, data.node, e);
40412         
40413         
40414     },
40415     
40416     onValidDrop : function(dd, e, id){
40417         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
40418         this.hideProxy();
40419     },
40420     
40421     beforeInvalidDrop : function(e, id){
40422         // this scrolls the original position back into view
40423         var sm = this.tree.getSelectionModel();
40424         sm.clearSelections();
40425         sm.select(this.dragData.node);
40426     }
40427 });
40428 }/*
40429  * Based on:
40430  * Ext JS Library 1.1.1
40431  * Copyright(c) 2006-2007, Ext JS, LLC.
40432  *
40433  * Originally Released Under LGPL - original licence link has changed is not relivant.
40434  *
40435  * Fork - LGPL
40436  * <script type="text/javascript">
40437  */
40438 /**
40439  * @class Roo.tree.TreeEditor
40440  * @extends Roo.Editor
40441  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
40442  * as the editor field.
40443  * @constructor
40444  * @param {Object} config (used to be the tree panel.)
40445  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
40446  * 
40447  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
40448  * @cfg {Roo.form.TextField} field [required] The field configuration
40449  *
40450  * 
40451  */
40452 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
40453     var tree = config;
40454     var field;
40455     if (oldconfig) { // old style..
40456         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
40457     } else {
40458         // new style..
40459         tree = config.tree;
40460         config.field = config.field  || {};
40461         config.field.xtype = 'TextField';
40462         field = Roo.factory(config.field, Roo.form);
40463     }
40464     config = config || {};
40465     
40466     
40467     this.addEvents({
40468         /**
40469          * @event beforenodeedit
40470          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
40471          * false from the handler of this event.
40472          * @param {Editor} this
40473          * @param {Roo.tree.Node} node 
40474          */
40475         "beforenodeedit" : true
40476     });
40477     
40478     //Roo.log(config);
40479     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
40480
40481     this.tree = tree;
40482
40483     tree.on('beforeclick', this.beforeNodeClick, this);
40484     tree.getTreeEl().on('mousedown', this.hide, this);
40485     this.on('complete', this.updateNode, this);
40486     this.on('beforestartedit', this.fitToTree, this);
40487     this.on('startedit', this.bindScroll, this, {delay:10});
40488     this.on('specialkey', this.onSpecialKey, this);
40489 };
40490
40491 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
40492     /**
40493      * @cfg {String} alignment
40494      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
40495      */
40496     alignment: "l-l",
40497     // inherit
40498     autoSize: false,
40499     /**
40500      * @cfg {Boolean} hideEl
40501      * True to hide the bound element while the editor is displayed (defaults to false)
40502      */
40503     hideEl : false,
40504     /**
40505      * @cfg {String} cls
40506      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
40507      */
40508     cls: "x-small-editor x-tree-editor",
40509     /**
40510      * @cfg {Boolean} shim
40511      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
40512      */
40513     shim:false,
40514     // inherit
40515     shadow:"frame",
40516     /**
40517      * @cfg {Number} maxWidth
40518      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
40519      * the containing tree element's size, it will be automatically limited for you to the container width, taking
40520      * scroll and client offsets into account prior to each edit.
40521      */
40522     maxWidth: 250,
40523
40524     editDelay : 350,
40525
40526     // private
40527     fitToTree : function(ed, el){
40528         var td = this.tree.getTreeEl().dom, nd = el.dom;
40529         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
40530             td.scrollLeft = nd.offsetLeft;
40531         }
40532         var w = Math.min(
40533                 this.maxWidth,
40534                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
40535         this.setSize(w, '');
40536         
40537         return this.fireEvent('beforenodeedit', this, this.editNode);
40538         
40539     },
40540
40541     // private
40542     triggerEdit : function(node){
40543         this.completeEdit();
40544         this.editNode = node;
40545         this.startEdit(node.ui.textNode, node.text);
40546     },
40547
40548     // private
40549     bindScroll : function(){
40550         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
40551     },
40552
40553     // private
40554     beforeNodeClick : function(node, e){
40555         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
40556         this.lastClick = new Date();
40557         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
40558             e.stopEvent();
40559             this.triggerEdit(node);
40560             return false;
40561         }
40562         return true;
40563     },
40564
40565     // private
40566     updateNode : function(ed, value){
40567         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
40568         this.editNode.setText(value);
40569     },
40570
40571     // private
40572     onHide : function(){
40573         Roo.tree.TreeEditor.superclass.onHide.call(this);
40574         if(this.editNode){
40575             this.editNode.ui.focus();
40576         }
40577     },
40578
40579     // private
40580     onSpecialKey : function(field, e){
40581         var k = e.getKey();
40582         if(k == e.ESC){
40583             e.stopEvent();
40584             this.cancelEdit();
40585         }else if(k == e.ENTER && !e.hasModifier()){
40586             e.stopEvent();
40587             this.completeEdit();
40588         }
40589     }
40590 });//<Script type="text/javascript">
40591 /*
40592  * Based on:
40593  * Ext JS Library 1.1.1
40594  * Copyright(c) 2006-2007, Ext JS, LLC.
40595  *
40596  * Originally Released Under LGPL - original licence link has changed is not relivant.
40597  *
40598  * Fork - LGPL
40599  * <script type="text/javascript">
40600  */
40601  
40602 /**
40603  * Not documented??? - probably should be...
40604  */
40605
40606 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
40607     //focus: Roo.emptyFn, // prevent odd scrolling behavior
40608     
40609     renderElements : function(n, a, targetNode, bulkRender){
40610         //consel.log("renderElements?");
40611         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
40612
40613         var t = n.getOwnerTree();
40614         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
40615         
40616         var cols = t.columns;
40617         var bw = t.borderWidth;
40618         var c = cols[0];
40619         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
40620          var cb = typeof a.checked == "boolean";
40621         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
40622         var colcls = 'x-t-' + tid + '-c0';
40623         var buf = [
40624             '<li class="x-tree-node">',
40625             
40626                 
40627                 '<div class="x-tree-node-el ', a.cls,'">',
40628                     // extran...
40629                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
40630                 
40631                 
40632                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
40633                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
40634                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
40635                            (a.icon ? ' x-tree-node-inline-icon' : ''),
40636                            (a.iconCls ? ' '+a.iconCls : ''),
40637                            '" unselectable="on" />',
40638                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
40639                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
40640                              
40641                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
40642                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
40643                             '<span unselectable="on" qtip="' + tx + '">',
40644                              tx,
40645                              '</span></a>' ,
40646                     '</div>',
40647                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
40648                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
40649                  ];
40650         for(var i = 1, len = cols.length; i < len; i++){
40651             c = cols[i];
40652             colcls = 'x-t-' + tid + '-c' +i;
40653             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
40654             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
40655                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
40656                       "</div>");
40657          }
40658          
40659          buf.push(
40660             '</a>',
40661             '<div class="x-clear"></div></div>',
40662             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
40663             "</li>");
40664         
40665         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
40666             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
40667                                 n.nextSibling.ui.getEl(), buf.join(""));
40668         }else{
40669             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
40670         }
40671         var el = this.wrap.firstChild;
40672         this.elRow = el;
40673         this.elNode = el.firstChild;
40674         this.ranchor = el.childNodes[1];
40675         this.ctNode = this.wrap.childNodes[1];
40676         var cs = el.firstChild.childNodes;
40677         this.indentNode = cs[0];
40678         this.ecNode = cs[1];
40679         this.iconNode = cs[2];
40680         var index = 3;
40681         if(cb){
40682             this.checkbox = cs[3];
40683             index++;
40684         }
40685         this.anchor = cs[index];
40686         
40687         this.textNode = cs[index].firstChild;
40688         
40689         //el.on("click", this.onClick, this);
40690         //el.on("dblclick", this.onDblClick, this);
40691         
40692         
40693        // console.log(this);
40694     },
40695     initEvents : function(){
40696         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
40697         
40698             
40699         var a = this.ranchor;
40700
40701         var el = Roo.get(a);
40702
40703         if(Roo.isOpera){ // opera render bug ignores the CSS
40704             el.setStyle("text-decoration", "none");
40705         }
40706
40707         el.on("click", this.onClick, this);
40708         el.on("dblclick", this.onDblClick, this);
40709         el.on("contextmenu", this.onContextMenu, this);
40710         
40711     },
40712     
40713     /*onSelectedChange : function(state){
40714         if(state){
40715             this.focus();
40716             this.addClass("x-tree-selected");
40717         }else{
40718             //this.blur();
40719             this.removeClass("x-tree-selected");
40720         }
40721     },*/
40722     addClass : function(cls){
40723         if(this.elRow){
40724             Roo.fly(this.elRow).addClass(cls);
40725         }
40726         
40727     },
40728     
40729     
40730     removeClass : function(cls){
40731         if(this.elRow){
40732             Roo.fly(this.elRow).removeClass(cls);
40733         }
40734     }
40735
40736     
40737     
40738 });//<Script type="text/javascript">
40739
40740 /*
40741  * Based on:
40742  * Ext JS Library 1.1.1
40743  * Copyright(c) 2006-2007, Ext JS, LLC.
40744  *
40745  * Originally Released Under LGPL - original licence link has changed is not relivant.
40746  *
40747  * Fork - LGPL
40748  * <script type="text/javascript">
40749  */
40750  
40751
40752 /**
40753  * @class Roo.tree.ColumnTree
40754  * @extends Roo.tree.TreePanel
40755  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
40756  * @cfg {int} borderWidth  compined right/left border allowance
40757  * @constructor
40758  * @param {String/HTMLElement/Element} el The container element
40759  * @param {Object} config
40760  */
40761 Roo.tree.ColumnTree =  function(el, config)
40762 {
40763    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
40764    this.addEvents({
40765         /**
40766         * @event resize
40767         * Fire this event on a container when it resizes
40768         * @param {int} w Width
40769         * @param {int} h Height
40770         */
40771        "resize" : true
40772     });
40773     this.on('resize', this.onResize, this);
40774 };
40775
40776 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
40777     //lines:false,
40778     
40779     
40780     borderWidth: Roo.isBorderBox ? 0 : 2, 
40781     headEls : false,
40782     
40783     render : function(){
40784         // add the header.....
40785        
40786         Roo.tree.ColumnTree.superclass.render.apply(this);
40787         
40788         this.el.addClass('x-column-tree');
40789         
40790         this.headers = this.el.createChild(
40791             {cls:'x-tree-headers'},this.innerCt.dom);
40792    
40793         var cols = this.columns, c;
40794         var totalWidth = 0;
40795         this.headEls = [];
40796         var  len = cols.length;
40797         for(var i = 0; i < len; i++){
40798              c = cols[i];
40799              totalWidth += c.width;
40800             this.headEls.push(this.headers.createChild({
40801                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
40802                  cn: {
40803                      cls:'x-tree-hd-text',
40804                      html: c.header
40805                  },
40806                  style:'width:'+(c.width-this.borderWidth)+'px;'
40807              }));
40808         }
40809         this.headers.createChild({cls:'x-clear'});
40810         // prevent floats from wrapping when clipped
40811         this.headers.setWidth(totalWidth);
40812         //this.innerCt.setWidth(totalWidth);
40813         this.innerCt.setStyle({ overflow: 'auto' });
40814         this.onResize(this.width, this.height);
40815              
40816         
40817     },
40818     onResize : function(w,h)
40819     {
40820         this.height = h;
40821         this.width = w;
40822         // resize cols..
40823         this.innerCt.setWidth(this.width);
40824         this.innerCt.setHeight(this.height-20);
40825         
40826         // headers...
40827         var cols = this.columns, c;
40828         var totalWidth = 0;
40829         var expEl = false;
40830         var len = cols.length;
40831         for(var i = 0; i < len; i++){
40832             c = cols[i];
40833             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
40834                 // it's the expander..
40835                 expEl  = this.headEls[i];
40836                 continue;
40837             }
40838             totalWidth += c.width;
40839             
40840         }
40841         if (expEl) {
40842             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
40843         }
40844         this.headers.setWidth(w-20);
40845
40846         
40847         
40848         
40849     }
40850 });
40851 /*
40852  * Based on:
40853  * Ext JS Library 1.1.1
40854  * Copyright(c) 2006-2007, Ext JS, LLC.
40855  *
40856  * Originally Released Under LGPL - original licence link has changed is not relivant.
40857  *
40858  * Fork - LGPL
40859  * <script type="text/javascript">
40860  */
40861  
40862 /**
40863  * @class Roo.menu.Menu
40864  * @extends Roo.util.Observable
40865  * @children Roo.menu.Item Roo.menu.Separator Roo.menu.TextItem
40866  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
40867  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
40868  * @constructor
40869  * Creates a new Menu
40870  * @param {Object} config Configuration options
40871  */
40872 Roo.menu.Menu = function(config){
40873     
40874     Roo.menu.Menu.superclass.constructor.call(this, config);
40875     
40876     this.id = this.id || Roo.id();
40877     this.addEvents({
40878         /**
40879          * @event beforeshow
40880          * Fires before this menu is displayed
40881          * @param {Roo.menu.Menu} this
40882          */
40883         beforeshow : true,
40884         /**
40885          * @event beforehide
40886          * Fires before this menu is hidden
40887          * @param {Roo.menu.Menu} this
40888          */
40889         beforehide : true,
40890         /**
40891          * @event show
40892          * Fires after this menu is displayed
40893          * @param {Roo.menu.Menu} this
40894          */
40895         show : true,
40896         /**
40897          * @event hide
40898          * Fires after this menu is hidden
40899          * @param {Roo.menu.Menu} this
40900          */
40901         hide : true,
40902         /**
40903          * @event click
40904          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
40905          * @param {Roo.menu.Menu} this
40906          * @param {Roo.menu.Item} menuItem The menu item that was clicked
40907          * @param {Roo.EventObject} e
40908          */
40909         click : true,
40910         /**
40911          * @event mouseover
40912          * Fires when the mouse is hovering over this menu
40913          * @param {Roo.menu.Menu} this
40914          * @param {Roo.EventObject} e
40915          * @param {Roo.menu.Item} menuItem The menu item that was clicked
40916          */
40917         mouseover : true,
40918         /**
40919          * @event mouseout
40920          * Fires when the mouse exits this menu
40921          * @param {Roo.menu.Menu} this
40922          * @param {Roo.EventObject} e
40923          * @param {Roo.menu.Item} menuItem The menu item that was clicked
40924          */
40925         mouseout : true,
40926         /**
40927          * @event itemclick
40928          * Fires when a menu item contained in this menu is clicked
40929          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
40930          * @param {Roo.EventObject} e
40931          */
40932         itemclick: true
40933     });
40934     if (this.registerMenu) {
40935         Roo.menu.MenuMgr.register(this);
40936     }
40937     
40938     var mis = this.items;
40939     this.items = new Roo.util.MixedCollection();
40940     if(mis){
40941         this.add.apply(this, mis);
40942     }
40943 };
40944
40945 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
40946     /**
40947      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
40948      */
40949     minWidth : 120,
40950     /**
40951      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
40952      * for bottom-right shadow (defaults to "sides")
40953      */
40954     shadow : "sides",
40955     /**
40956      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
40957      * this menu (defaults to "tl-tr?")
40958      */
40959     subMenuAlign : "tl-tr?",
40960     /**
40961      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
40962      * relative to its element of origin (defaults to "tl-bl?")
40963      */
40964     defaultAlign : "tl-bl?",
40965     /**
40966      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
40967      */
40968     allowOtherMenus : false,
40969     /**
40970      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
40971      */
40972     registerMenu : true,
40973
40974     hidden:true,
40975
40976     // private
40977     render : function(){
40978         if(this.el){
40979             return;
40980         }
40981         var el = this.el = new Roo.Layer({
40982             cls: "x-menu",
40983             shadow:this.shadow,
40984             constrain: false,
40985             parentEl: this.parentEl || document.body,
40986             zindex:15000
40987         });
40988
40989         this.keyNav = new Roo.menu.MenuNav(this);
40990
40991         if(this.plain){
40992             el.addClass("x-menu-plain");
40993         }
40994         if(this.cls){
40995             el.addClass(this.cls);
40996         }
40997         // generic focus element
40998         this.focusEl = el.createChild({
40999             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
41000         });
41001         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
41002         //disabling touch- as it's causing issues ..
41003         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
41004         ul.on('click'   , this.onClick, this);
41005         
41006         
41007         ul.on("mouseover", this.onMouseOver, this);
41008         ul.on("mouseout", this.onMouseOut, this);
41009         this.items.each(function(item){
41010             if (item.hidden) {
41011                 return;
41012             }
41013             
41014             var li = document.createElement("li");
41015             li.className = "x-menu-list-item";
41016             ul.dom.appendChild(li);
41017             item.render(li, this);
41018         }, this);
41019         this.ul = ul;
41020         this.autoWidth();
41021     },
41022
41023     // private
41024     autoWidth : function(){
41025         var el = this.el, ul = this.ul;
41026         if(!el){
41027             return;
41028         }
41029         var w = this.width;
41030         if(w){
41031             el.setWidth(w);
41032         }else if(Roo.isIE){
41033             el.setWidth(this.minWidth);
41034             var t = el.dom.offsetWidth; // force recalc
41035             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
41036         }
41037     },
41038
41039     // private
41040     delayAutoWidth : function(){
41041         if(this.rendered){
41042             if(!this.awTask){
41043                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
41044             }
41045             this.awTask.delay(20);
41046         }
41047     },
41048
41049     // private
41050     findTargetItem : function(e){
41051         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
41052         if(t && t.menuItemId){
41053             return this.items.get(t.menuItemId);
41054         }
41055     },
41056
41057     // private
41058     onClick : function(e){
41059         Roo.log("menu.onClick");
41060         var t = this.findTargetItem(e);
41061         if(!t){
41062             return;
41063         }
41064         Roo.log(e);
41065         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
41066             if(t == this.activeItem && t.shouldDeactivate(e)){
41067                 this.activeItem.deactivate();
41068                 delete this.activeItem;
41069                 return;
41070             }
41071             if(t.canActivate){
41072                 this.setActiveItem(t, true);
41073             }
41074             return;
41075             
41076             
41077         }
41078         
41079         t.onClick(e);
41080         this.fireEvent("click", this, t, e);
41081     },
41082
41083     // private
41084     setActiveItem : function(item, autoExpand){
41085         if(item != this.activeItem){
41086             if(this.activeItem){
41087                 this.activeItem.deactivate();
41088             }
41089             this.activeItem = item;
41090             item.activate(autoExpand);
41091         }else if(autoExpand){
41092             item.expandMenu();
41093         }
41094     },
41095
41096     // private
41097     tryActivate : function(start, step){
41098         var items = this.items;
41099         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
41100             var item = items.get(i);
41101             if(!item.disabled && item.canActivate){
41102                 this.setActiveItem(item, false);
41103                 return item;
41104             }
41105         }
41106         return false;
41107     },
41108
41109     // private
41110     onMouseOver : function(e){
41111         var t;
41112         if(t = this.findTargetItem(e)){
41113             if(t.canActivate && !t.disabled){
41114                 this.setActiveItem(t, true);
41115             }
41116         }
41117         this.fireEvent("mouseover", this, e, t);
41118     },
41119
41120     // private
41121     onMouseOut : function(e){
41122         var t;
41123         if(t = this.findTargetItem(e)){
41124             if(t == this.activeItem && t.shouldDeactivate(e)){
41125                 this.activeItem.deactivate();
41126                 delete this.activeItem;
41127             }
41128         }
41129         this.fireEvent("mouseout", this, e, t);
41130     },
41131
41132     /**
41133      * Read-only.  Returns true if the menu is currently displayed, else false.
41134      * @type Boolean
41135      */
41136     isVisible : function(){
41137         return this.el && !this.hidden;
41138     },
41139
41140     /**
41141      * Displays this menu relative to another element
41142      * @param {String/HTMLElement/Roo.Element} element The element to align to
41143      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
41144      * the element (defaults to this.defaultAlign)
41145      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
41146      */
41147     show : function(el, pos, parentMenu){
41148         this.parentMenu = parentMenu;
41149         if(!this.el){
41150             this.render();
41151         }
41152         this.fireEvent("beforeshow", this);
41153         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
41154     },
41155
41156     /**
41157      * Displays this menu at a specific xy position
41158      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
41159      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
41160      */
41161     showAt : function(xy, parentMenu, /* private: */_e){
41162         this.parentMenu = parentMenu;
41163         if(!this.el){
41164             this.render();
41165         }
41166         if(_e !== false){
41167             this.fireEvent("beforeshow", this);
41168             xy = this.el.adjustForConstraints(xy);
41169         }
41170         this.el.setXY(xy);
41171         this.el.show();
41172         this.hidden = false;
41173         this.focus();
41174         this.fireEvent("show", this);
41175     },
41176
41177     focus : function(){
41178         if(!this.hidden){
41179             this.doFocus.defer(50, this);
41180         }
41181     },
41182
41183     doFocus : function(){
41184         if(!this.hidden){
41185             this.focusEl.focus();
41186         }
41187     },
41188
41189     /**
41190      * Hides this menu and optionally all parent menus
41191      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
41192      */
41193     hide : function(deep){
41194         if(this.el && this.isVisible()){
41195             this.fireEvent("beforehide", this);
41196             if(this.activeItem){
41197                 this.activeItem.deactivate();
41198                 this.activeItem = null;
41199             }
41200             this.el.hide();
41201             this.hidden = true;
41202             this.fireEvent("hide", this);
41203         }
41204         if(deep === true && this.parentMenu){
41205             this.parentMenu.hide(true);
41206         }
41207     },
41208
41209     /**
41210      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
41211      * Any of the following are valid:
41212      * <ul>
41213      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
41214      * <li>An HTMLElement object which will be converted to a menu item</li>
41215      * <li>A menu item config object that will be created as a new menu item</li>
41216      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
41217      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
41218      * </ul>
41219      * Usage:
41220      * <pre><code>
41221 // Create the menu
41222 var menu = new Roo.menu.Menu();
41223
41224 // Create a menu item to add by reference
41225 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
41226
41227 // Add a bunch of items at once using different methods.
41228 // Only the last item added will be returned.
41229 var item = menu.add(
41230     menuItem,                // add existing item by ref
41231     'Dynamic Item',          // new TextItem
41232     '-',                     // new separator
41233     { text: 'Config Item' }  // new item by config
41234 );
41235 </code></pre>
41236      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
41237      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
41238      */
41239     add : function(){
41240         var a = arguments, l = a.length, item;
41241         for(var i = 0; i < l; i++){
41242             var el = a[i];
41243             if ((typeof(el) == "object") && el.xtype && el.xns) {
41244                 el = Roo.factory(el, Roo.menu);
41245             }
41246             
41247             if(el.render){ // some kind of Item
41248                 item = this.addItem(el);
41249             }else if(typeof el == "string"){ // string
41250                 if(el == "separator" || el == "-"){
41251                     item = this.addSeparator();
41252                 }else{
41253                     item = this.addText(el);
41254                 }
41255             }else if(el.tagName || el.el){ // element
41256                 item = this.addElement(el);
41257             }else if(typeof el == "object"){ // must be menu item config?
41258                 item = this.addMenuItem(el);
41259             }
41260         }
41261         return item;
41262     },
41263
41264     /**
41265      * Returns this menu's underlying {@link Roo.Element} object
41266      * @return {Roo.Element} The element
41267      */
41268     getEl : function(){
41269         if(!this.el){
41270             this.render();
41271         }
41272         return this.el;
41273     },
41274
41275     /**
41276      * Adds a separator bar to the menu
41277      * @return {Roo.menu.Item} The menu item that was added
41278      */
41279     addSeparator : function(){
41280         return this.addItem(new Roo.menu.Separator());
41281     },
41282
41283     /**
41284      * Adds an {@link Roo.Element} object to the menu
41285      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
41286      * @return {Roo.menu.Item} The menu item that was added
41287      */
41288     addElement : function(el){
41289         return this.addItem(new Roo.menu.BaseItem(el));
41290     },
41291
41292     /**
41293      * Adds an existing object based on {@link Roo.menu.Item} to the menu
41294      * @param {Roo.menu.Item} item The menu item to add
41295      * @return {Roo.menu.Item} The menu item that was added
41296      */
41297     addItem : function(item){
41298         this.items.add(item);
41299         if(this.ul){
41300             var li = document.createElement("li");
41301             li.className = "x-menu-list-item";
41302             this.ul.dom.appendChild(li);
41303             item.render(li, this);
41304             this.delayAutoWidth();
41305         }
41306         return item;
41307     },
41308
41309     /**
41310      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
41311      * @param {Object} config A MenuItem config object
41312      * @return {Roo.menu.Item} The menu item that was added
41313      */
41314     addMenuItem : function(config){
41315         if(!(config instanceof Roo.menu.Item)){
41316             if(typeof config.checked == "boolean"){ // must be check menu item config?
41317                 config = new Roo.menu.CheckItem(config);
41318             }else{
41319                 config = new Roo.menu.Item(config);
41320             }
41321         }
41322         return this.addItem(config);
41323     },
41324
41325     /**
41326      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
41327      * @param {String} text The text to display in the menu item
41328      * @return {Roo.menu.Item} The menu item that was added
41329      */
41330     addText : function(text){
41331         return this.addItem(new Roo.menu.TextItem({ text : text }));
41332     },
41333
41334     /**
41335      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
41336      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
41337      * @param {Roo.menu.Item} item The menu item to add
41338      * @return {Roo.menu.Item} The menu item that was added
41339      */
41340     insert : function(index, item){
41341         this.items.insert(index, item);
41342         if(this.ul){
41343             var li = document.createElement("li");
41344             li.className = "x-menu-list-item";
41345             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
41346             item.render(li, this);
41347             this.delayAutoWidth();
41348         }
41349         return item;
41350     },
41351
41352     /**
41353      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
41354      * @param {Roo.menu.Item} item The menu item to remove
41355      */
41356     remove : function(item){
41357         this.items.removeKey(item.id);
41358         item.destroy();
41359     },
41360
41361     /**
41362      * Removes and destroys all items in the menu
41363      */
41364     removeAll : function(){
41365         var f;
41366         while(f = this.items.first()){
41367             this.remove(f);
41368         }
41369     }
41370 });
41371
41372 // MenuNav is a private utility class used internally by the Menu
41373 Roo.menu.MenuNav = function(menu){
41374     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
41375     this.scope = this.menu = menu;
41376 };
41377
41378 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
41379     doRelay : function(e, h){
41380         var k = e.getKey();
41381         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
41382             this.menu.tryActivate(0, 1);
41383             return false;
41384         }
41385         return h.call(this.scope || this, e, this.menu);
41386     },
41387
41388     up : function(e, m){
41389         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
41390             m.tryActivate(m.items.length-1, -1);
41391         }
41392     },
41393
41394     down : function(e, m){
41395         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
41396             m.tryActivate(0, 1);
41397         }
41398     },
41399
41400     right : function(e, m){
41401         if(m.activeItem){
41402             m.activeItem.expandMenu(true);
41403         }
41404     },
41405
41406     left : function(e, m){
41407         m.hide();
41408         if(m.parentMenu && m.parentMenu.activeItem){
41409             m.parentMenu.activeItem.activate();
41410         }
41411     },
41412
41413     enter : function(e, m){
41414         if(m.activeItem){
41415             e.stopPropagation();
41416             m.activeItem.onClick(e);
41417             m.fireEvent("click", this, m.activeItem);
41418             return true;
41419         }
41420     }
41421 });/*
41422  * Based on:
41423  * Ext JS Library 1.1.1
41424  * Copyright(c) 2006-2007, Ext JS, LLC.
41425  *
41426  * Originally Released Under LGPL - original licence link has changed is not relivant.
41427  *
41428  * Fork - LGPL
41429  * <script type="text/javascript">
41430  */
41431  
41432 /**
41433  * @class Roo.menu.MenuMgr
41434  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
41435  * @static
41436  */
41437 Roo.menu.MenuMgr = function(){
41438    var menus, active, groups = {}, attached = false, lastShow = new Date();
41439
41440    // private - called when first menu is created
41441    function init(){
41442        menus = {};
41443        active = new Roo.util.MixedCollection();
41444        Roo.get(document).addKeyListener(27, function(){
41445            if(active.length > 0){
41446                hideAll();
41447            }
41448        });
41449    }
41450
41451    // private
41452    function hideAll(){
41453        if(active && active.length > 0){
41454            var c = active.clone();
41455            c.each(function(m){
41456                m.hide();
41457            });
41458        }
41459    }
41460
41461    // private
41462    function onHide(m){
41463        active.remove(m);
41464        if(active.length < 1){
41465            Roo.get(document).un("mousedown", onMouseDown);
41466            attached = false;
41467        }
41468    }
41469
41470    // private
41471    function onShow(m){
41472        var last = active.last();
41473        lastShow = new Date();
41474        active.add(m);
41475        if(!attached){
41476            Roo.get(document).on("mousedown", onMouseDown);
41477            attached = true;
41478        }
41479        if(m.parentMenu){
41480           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
41481           m.parentMenu.activeChild = m;
41482        }else if(last && last.isVisible()){
41483           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
41484        }
41485    }
41486
41487    // private
41488    function onBeforeHide(m){
41489        if(m.activeChild){
41490            m.activeChild.hide();
41491        }
41492        if(m.autoHideTimer){
41493            clearTimeout(m.autoHideTimer);
41494            delete m.autoHideTimer;
41495        }
41496    }
41497
41498    // private
41499    function onBeforeShow(m){
41500        var pm = m.parentMenu;
41501        if(!pm && !m.allowOtherMenus){
41502            hideAll();
41503        }else if(pm && pm.activeChild && active != m){
41504            pm.activeChild.hide();
41505        }
41506    }
41507
41508    // private
41509    function onMouseDown(e){
41510        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
41511            hideAll();
41512        }
41513    }
41514
41515    // private
41516    function onBeforeCheck(mi, state){
41517        if(state){
41518            var g = groups[mi.group];
41519            for(var i = 0, l = g.length; i < l; i++){
41520                if(g[i] != mi){
41521                    g[i].setChecked(false);
41522                }
41523            }
41524        }
41525    }
41526
41527    return {
41528
41529        /**
41530         * Hides all menus that are currently visible
41531         */
41532        hideAll : function(){
41533             hideAll();  
41534        },
41535
41536        // private
41537        register : function(menu){
41538            if(!menus){
41539                init();
41540            }
41541            menus[menu.id] = menu;
41542            menu.on("beforehide", onBeforeHide);
41543            menu.on("hide", onHide);
41544            menu.on("beforeshow", onBeforeShow);
41545            menu.on("show", onShow);
41546            var g = menu.group;
41547            if(g && menu.events["checkchange"]){
41548                if(!groups[g]){
41549                    groups[g] = [];
41550                }
41551                groups[g].push(menu);
41552                menu.on("checkchange", onCheck);
41553            }
41554        },
41555
41556         /**
41557          * Returns a {@link Roo.menu.Menu} object
41558          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
41559          * be used to generate and return a new Menu instance.
41560          */
41561        get : function(menu){
41562            if(typeof menu == "string"){ // menu id
41563                return menus[menu];
41564            }else if(menu.events){  // menu instance
41565                return menu;
41566            }else if(typeof menu.length == 'number'){ // array of menu items?
41567                return new Roo.menu.Menu({items:menu});
41568            }else{ // otherwise, must be a config
41569                return new Roo.menu.Menu(menu);
41570            }
41571        },
41572
41573        // private
41574        unregister : function(menu){
41575            delete menus[menu.id];
41576            menu.un("beforehide", onBeforeHide);
41577            menu.un("hide", onHide);
41578            menu.un("beforeshow", onBeforeShow);
41579            menu.un("show", onShow);
41580            var g = menu.group;
41581            if(g && menu.events["checkchange"]){
41582                groups[g].remove(menu);
41583                menu.un("checkchange", onCheck);
41584            }
41585        },
41586
41587        // private
41588        registerCheckable : function(menuItem){
41589            var g = menuItem.group;
41590            if(g){
41591                if(!groups[g]){
41592                    groups[g] = [];
41593                }
41594                groups[g].push(menuItem);
41595                menuItem.on("beforecheckchange", onBeforeCheck);
41596            }
41597        },
41598
41599        // private
41600        unregisterCheckable : function(menuItem){
41601            var g = menuItem.group;
41602            if(g){
41603                groups[g].remove(menuItem);
41604                menuItem.un("beforecheckchange", onBeforeCheck);
41605            }
41606        }
41607    };
41608 }();/*
41609  * Based on:
41610  * Ext JS Library 1.1.1
41611  * Copyright(c) 2006-2007, Ext JS, LLC.
41612  *
41613  * Originally Released Under LGPL - original licence link has changed is not relivant.
41614  *
41615  * Fork - LGPL
41616  * <script type="text/javascript">
41617  */
41618  
41619
41620 /**
41621  * @class Roo.menu.BaseItem
41622  * @extends Roo.Component
41623  * @abstract
41624  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
41625  * management and base configuration options shared by all menu components.
41626  * @constructor
41627  * Creates a new BaseItem
41628  * @param {Object} config Configuration options
41629  */
41630 Roo.menu.BaseItem = function(config){
41631     Roo.menu.BaseItem.superclass.constructor.call(this, config);
41632
41633     this.addEvents({
41634         /**
41635          * @event click
41636          * Fires when this item is clicked
41637          * @param {Roo.menu.BaseItem} this
41638          * @param {Roo.EventObject} e
41639          */
41640         click: true,
41641         /**
41642          * @event activate
41643          * Fires when this item is activated
41644          * @param {Roo.menu.BaseItem} this
41645          */
41646         activate : true,
41647         /**
41648          * @event deactivate
41649          * Fires when this item is deactivated
41650          * @param {Roo.menu.BaseItem} this
41651          */
41652         deactivate : true
41653     });
41654
41655     if(this.handler){
41656         this.on("click", this.handler, this.scope, true);
41657     }
41658 };
41659
41660 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
41661     /**
41662      * @cfg {Function} handler
41663      * A function that will handle the click event of this menu item (defaults to undefined)
41664      */
41665     /**
41666      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
41667      */
41668     canActivate : false,
41669     
41670      /**
41671      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
41672      */
41673     hidden: false,
41674     
41675     /**
41676      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
41677      */
41678     activeClass : "x-menu-item-active",
41679     /**
41680      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
41681      */
41682     hideOnClick : true,
41683     /**
41684      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
41685      */
41686     hideDelay : 100,
41687
41688     // private
41689     ctype: "Roo.menu.BaseItem",
41690
41691     // private
41692     actionMode : "container",
41693
41694     // private
41695     render : function(container, parentMenu){
41696         this.parentMenu = parentMenu;
41697         Roo.menu.BaseItem.superclass.render.call(this, container);
41698         this.container.menuItemId = this.id;
41699     },
41700
41701     // private
41702     onRender : function(container, position){
41703         this.el = Roo.get(this.el);
41704         container.dom.appendChild(this.el.dom);
41705     },
41706
41707     // private
41708     onClick : function(e){
41709         if(!this.disabled && this.fireEvent("click", this, e) !== false
41710                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
41711             this.handleClick(e);
41712         }else{
41713             e.stopEvent();
41714         }
41715     },
41716
41717     // private
41718     activate : function(){
41719         if(this.disabled){
41720             return false;
41721         }
41722         var li = this.container;
41723         li.addClass(this.activeClass);
41724         this.region = li.getRegion().adjust(2, 2, -2, -2);
41725         this.fireEvent("activate", this);
41726         return true;
41727     },
41728
41729     // private
41730     deactivate : function(){
41731         this.container.removeClass(this.activeClass);
41732         this.fireEvent("deactivate", this);
41733     },
41734
41735     // private
41736     shouldDeactivate : function(e){
41737         return !this.region || !this.region.contains(e.getPoint());
41738     },
41739
41740     // private
41741     handleClick : function(e){
41742         if(this.hideOnClick){
41743             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
41744         }
41745     },
41746
41747     // private
41748     expandMenu : function(autoActivate){
41749         // do nothing
41750     },
41751
41752     // private
41753     hideMenu : function(){
41754         // do nothing
41755     }
41756 });/*
41757  * Based on:
41758  * Ext JS Library 1.1.1
41759  * Copyright(c) 2006-2007, Ext JS, LLC.
41760  *
41761  * Originally Released Under LGPL - original licence link has changed is not relivant.
41762  *
41763  * Fork - LGPL
41764  * <script type="text/javascript">
41765  */
41766  
41767 /**
41768  * @class Roo.menu.Adapter
41769  * @extends Roo.menu.BaseItem
41770  * @abstract
41771  * 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.
41772  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
41773  * @constructor
41774  * Creates a new Adapter
41775  * @param {Object} config Configuration options
41776  */
41777 Roo.menu.Adapter = function(component, config){
41778     Roo.menu.Adapter.superclass.constructor.call(this, config);
41779     this.component = component;
41780 };
41781 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
41782     // private
41783     canActivate : true,
41784
41785     // private
41786     onRender : function(container, position){
41787         this.component.render(container);
41788         this.el = this.component.getEl();
41789     },
41790
41791     // private
41792     activate : function(){
41793         if(this.disabled){
41794             return false;
41795         }
41796         this.component.focus();
41797         this.fireEvent("activate", this);
41798         return true;
41799     },
41800
41801     // private
41802     deactivate : function(){
41803         this.fireEvent("deactivate", this);
41804     },
41805
41806     // private
41807     disable : function(){
41808         this.component.disable();
41809         Roo.menu.Adapter.superclass.disable.call(this);
41810     },
41811
41812     // private
41813     enable : function(){
41814         this.component.enable();
41815         Roo.menu.Adapter.superclass.enable.call(this);
41816     }
41817 });/*
41818  * Based on:
41819  * Ext JS Library 1.1.1
41820  * Copyright(c) 2006-2007, Ext JS, LLC.
41821  *
41822  * Originally Released Under LGPL - original licence link has changed is not relivant.
41823  *
41824  * Fork - LGPL
41825  * <script type="text/javascript">
41826  */
41827
41828 /**
41829  * @class Roo.menu.TextItem
41830  * @extends Roo.menu.BaseItem
41831  * Adds a static text string to a menu, usually used as either a heading or group separator.
41832  * Note: old style constructor with text is still supported.
41833  * 
41834  * @constructor
41835  * Creates a new TextItem
41836  * @param {Object} cfg Configuration
41837  */
41838 Roo.menu.TextItem = function(cfg){
41839     if (typeof(cfg) == 'string') {
41840         this.text = cfg;
41841     } else {
41842         Roo.apply(this,cfg);
41843     }
41844     
41845     Roo.menu.TextItem.superclass.constructor.call(this);
41846 };
41847
41848 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
41849     /**
41850      * @cfg {String} text Text to show on item.
41851      */
41852     text : '',
41853     
41854     /**
41855      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
41856      */
41857     hideOnClick : false,
41858     /**
41859      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
41860      */
41861     itemCls : "x-menu-text",
41862
41863     // private
41864     onRender : function(){
41865         var s = document.createElement("span");
41866         s.className = this.itemCls;
41867         s.innerHTML = this.text;
41868         this.el = s;
41869         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
41870     }
41871 });/*
41872  * Based on:
41873  * Ext JS Library 1.1.1
41874  * Copyright(c) 2006-2007, Ext JS, LLC.
41875  *
41876  * Originally Released Under LGPL - original licence link has changed is not relivant.
41877  *
41878  * Fork - LGPL
41879  * <script type="text/javascript">
41880  */
41881
41882 /**
41883  * @class Roo.menu.Separator
41884  * @extends Roo.menu.BaseItem
41885  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
41886  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
41887  * @constructor
41888  * @param {Object} config Configuration options
41889  */
41890 Roo.menu.Separator = function(config){
41891     Roo.menu.Separator.superclass.constructor.call(this, config);
41892 };
41893
41894 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
41895     /**
41896      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
41897      */
41898     itemCls : "x-menu-sep",
41899     /**
41900      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
41901      */
41902     hideOnClick : false,
41903
41904     // private
41905     onRender : function(li){
41906         var s = document.createElement("span");
41907         s.className = this.itemCls;
41908         s.innerHTML = "&#160;";
41909         this.el = s;
41910         li.addClass("x-menu-sep-li");
41911         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
41912     }
41913 });/*
41914  * Based on:
41915  * Ext JS Library 1.1.1
41916  * Copyright(c) 2006-2007, Ext JS, LLC.
41917  *
41918  * Originally Released Under LGPL - original licence link has changed is not relivant.
41919  *
41920  * Fork - LGPL
41921  * <script type="text/javascript">
41922  */
41923 /**
41924  * @class Roo.menu.Item
41925  * @extends Roo.menu.BaseItem
41926  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
41927  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
41928  * activation and click handling.
41929  * @constructor
41930  * Creates a new Item
41931  * @param {Object} config Configuration options
41932  */
41933 Roo.menu.Item = function(config){
41934     Roo.menu.Item.superclass.constructor.call(this, config);
41935     if(this.menu){
41936         this.menu = Roo.menu.MenuMgr.get(this.menu);
41937     }
41938 };
41939 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
41940     /**
41941      * @cfg {Roo.menu.Menu} menu
41942      * A Sub menu
41943      */
41944     /**
41945      * @cfg {String} text
41946      * The text to show on the menu item.
41947      */
41948     text: '',
41949      /**
41950      * @cfg {String} html to render in menu
41951      * The text to show on the menu item (HTML version).
41952      */
41953     html: '',
41954     /**
41955      * @cfg {String} icon
41956      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
41957      */
41958     icon: undefined,
41959     /**
41960      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
41961      */
41962     itemCls : "x-menu-item",
41963     /**
41964      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
41965      */
41966     canActivate : true,
41967     /**
41968      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
41969      */
41970     showDelay: 200,
41971     // doc'd in BaseItem
41972     hideDelay: 200,
41973
41974     // private
41975     ctype: "Roo.menu.Item",
41976     
41977     // private
41978     onRender : function(container, position){
41979         var el = document.createElement("a");
41980         el.hideFocus = true;
41981         el.unselectable = "on";
41982         el.href = this.href || "#";
41983         if(this.hrefTarget){
41984             el.target = this.hrefTarget;
41985         }
41986         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
41987         
41988         var html = this.html.length ? this.html  : String.format('{0}',this.text);
41989         
41990         el.innerHTML = String.format(
41991                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
41992                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
41993         this.el = el;
41994         Roo.menu.Item.superclass.onRender.call(this, container, position);
41995     },
41996
41997     /**
41998      * Sets the text to display in this menu item
41999      * @param {String} text The text to display
42000      * @param {Boolean} isHTML true to indicate text is pure html.
42001      */
42002     setText : function(text, isHTML){
42003         if (isHTML) {
42004             this.html = text;
42005         } else {
42006             this.text = text;
42007             this.html = '';
42008         }
42009         if(this.rendered){
42010             var html = this.html.length ? this.html  : String.format('{0}',this.text);
42011      
42012             this.el.update(String.format(
42013                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
42014                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
42015             this.parentMenu.autoWidth();
42016         }
42017     },
42018
42019     // private
42020     handleClick : function(e){
42021         if(!this.href){ // if no link defined, stop the event automatically
42022             e.stopEvent();
42023         }
42024         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
42025     },
42026
42027     // private
42028     activate : function(autoExpand){
42029         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
42030             this.focus();
42031             if(autoExpand){
42032                 this.expandMenu();
42033             }
42034         }
42035         return true;
42036     },
42037
42038     // private
42039     shouldDeactivate : function(e){
42040         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
42041             if(this.menu && this.menu.isVisible()){
42042                 return !this.menu.getEl().getRegion().contains(e.getPoint());
42043             }
42044             return true;
42045         }
42046         return false;
42047     },
42048
42049     // private
42050     deactivate : function(){
42051         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
42052         this.hideMenu();
42053     },
42054
42055     // private
42056     expandMenu : function(autoActivate){
42057         if(!this.disabled && this.menu){
42058             clearTimeout(this.hideTimer);
42059             delete this.hideTimer;
42060             if(!this.menu.isVisible() && !this.showTimer){
42061                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
42062             }else if (this.menu.isVisible() && autoActivate){
42063                 this.menu.tryActivate(0, 1);
42064             }
42065         }
42066     },
42067
42068     // private
42069     deferExpand : function(autoActivate){
42070         delete this.showTimer;
42071         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
42072         if(autoActivate){
42073             this.menu.tryActivate(0, 1);
42074         }
42075     },
42076
42077     // private
42078     hideMenu : function(){
42079         clearTimeout(this.showTimer);
42080         delete this.showTimer;
42081         if(!this.hideTimer && this.menu && this.menu.isVisible()){
42082             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
42083         }
42084     },
42085
42086     // private
42087     deferHide : function(){
42088         delete this.hideTimer;
42089         this.menu.hide();
42090     }
42091 });/*
42092  * Based on:
42093  * Ext JS Library 1.1.1
42094  * Copyright(c) 2006-2007, Ext JS, LLC.
42095  *
42096  * Originally Released Under LGPL - original licence link has changed is not relivant.
42097  *
42098  * Fork - LGPL
42099  * <script type="text/javascript">
42100  */
42101  
42102 /**
42103  * @class Roo.menu.CheckItem
42104  * @extends Roo.menu.Item
42105  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
42106  * @constructor
42107  * Creates a new CheckItem
42108  * @param {Object} config Configuration options
42109  */
42110 Roo.menu.CheckItem = function(config){
42111     Roo.menu.CheckItem.superclass.constructor.call(this, config);
42112     this.addEvents({
42113         /**
42114          * @event beforecheckchange
42115          * Fires before the checked value is set, providing an opportunity to cancel if needed
42116          * @param {Roo.menu.CheckItem} this
42117          * @param {Boolean} checked The new checked value that will be set
42118          */
42119         "beforecheckchange" : true,
42120         /**
42121          * @event checkchange
42122          * Fires after the checked value has been set
42123          * @param {Roo.menu.CheckItem} this
42124          * @param {Boolean} checked The checked value that was set
42125          */
42126         "checkchange" : true
42127     });
42128     if(this.checkHandler){
42129         this.on('checkchange', this.checkHandler, this.scope);
42130     }
42131 };
42132 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
42133     /**
42134      * @cfg {String} group
42135      * All check items with the same group name will automatically be grouped into a single-select
42136      * radio button group (defaults to '')
42137      */
42138     /**
42139      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
42140      */
42141     itemCls : "x-menu-item x-menu-check-item",
42142     /**
42143      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
42144      */
42145     groupClass : "x-menu-group-item",
42146
42147     /**
42148      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
42149      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
42150      * initialized with checked = true will be rendered as checked.
42151      */
42152     checked: false,
42153
42154     // private
42155     ctype: "Roo.menu.CheckItem",
42156
42157     // private
42158     onRender : function(c){
42159         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
42160         if(this.group){
42161             this.el.addClass(this.groupClass);
42162         }
42163         Roo.menu.MenuMgr.registerCheckable(this);
42164         if(this.checked){
42165             this.checked = false;
42166             this.setChecked(true, true);
42167         }
42168     },
42169
42170     // private
42171     destroy : function(){
42172         if(this.rendered){
42173             Roo.menu.MenuMgr.unregisterCheckable(this);
42174         }
42175         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
42176     },
42177
42178     /**
42179      * Set the checked state of this item
42180      * @param {Boolean} checked The new checked value
42181      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
42182      */
42183     setChecked : function(state, suppressEvent){
42184         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
42185             if(this.container){
42186                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
42187             }
42188             this.checked = state;
42189             if(suppressEvent !== true){
42190                 this.fireEvent("checkchange", this, state);
42191             }
42192         }
42193     },
42194
42195     // private
42196     handleClick : function(e){
42197        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
42198            this.setChecked(!this.checked);
42199        }
42200        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
42201     }
42202 });/*
42203  * Based on:
42204  * Ext JS Library 1.1.1
42205  * Copyright(c) 2006-2007, Ext JS, LLC.
42206  *
42207  * Originally Released Under LGPL - original licence link has changed is not relivant.
42208  *
42209  * Fork - LGPL
42210  * <script type="text/javascript">
42211  */
42212  
42213 /**
42214  * @class Roo.menu.DateItem
42215  * @extends Roo.menu.Adapter
42216  * A menu item that wraps the {@link Roo.DatPicker} component.
42217  * @constructor
42218  * Creates a new DateItem
42219  * @param {Object} config Configuration options
42220  */
42221 Roo.menu.DateItem = function(config){
42222     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
42223     /** The Roo.DatePicker object @type Roo.DatePicker */
42224     this.picker = this.component;
42225     this.addEvents({select: true});
42226     
42227     this.picker.on("render", function(picker){
42228         picker.getEl().swallowEvent("click");
42229         picker.container.addClass("x-menu-date-item");
42230     });
42231
42232     this.picker.on("select", this.onSelect, this);
42233 };
42234
42235 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
42236     // private
42237     onSelect : function(picker, date){
42238         this.fireEvent("select", this, date, picker);
42239         Roo.menu.DateItem.superclass.handleClick.call(this);
42240     }
42241 });/*
42242  * Based on:
42243  * Ext JS Library 1.1.1
42244  * Copyright(c) 2006-2007, Ext JS, LLC.
42245  *
42246  * Originally Released Under LGPL - original licence link has changed is not relivant.
42247  *
42248  * Fork - LGPL
42249  * <script type="text/javascript">
42250  */
42251  
42252 /**
42253  * @class Roo.menu.ColorItem
42254  * @extends Roo.menu.Adapter
42255  * A menu item that wraps the {@link Roo.ColorPalette} component.
42256  * @constructor
42257  * Creates a new ColorItem
42258  * @param {Object} config Configuration options
42259  */
42260 Roo.menu.ColorItem = function(config){
42261     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
42262     /** The Roo.ColorPalette object @type Roo.ColorPalette */
42263     this.palette = this.component;
42264     this.relayEvents(this.palette, ["select"]);
42265     if(this.selectHandler){
42266         this.on('select', this.selectHandler, this.scope);
42267     }
42268 };
42269 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
42270  * Based on:
42271  * Ext JS Library 1.1.1
42272  * Copyright(c) 2006-2007, Ext JS, LLC.
42273  *
42274  * Originally Released Under LGPL - original licence link has changed is not relivant.
42275  *
42276  * Fork - LGPL
42277  * <script type="text/javascript">
42278  */
42279  
42280
42281 /**
42282  * @class Roo.menu.DateMenu
42283  * @extends Roo.menu.Menu
42284  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
42285  * @constructor
42286  * Creates a new DateMenu
42287  * @param {Object} config Configuration options
42288  */
42289 Roo.menu.DateMenu = function(config){
42290     Roo.menu.DateMenu.superclass.constructor.call(this, config);
42291     this.plain = true;
42292     var di = new Roo.menu.DateItem(config);
42293     this.add(di);
42294     /**
42295      * The {@link Roo.DatePicker} instance for this DateMenu
42296      * @type DatePicker
42297      */
42298     this.picker = di.picker;
42299     /**
42300      * @event select
42301      * @param {DatePicker} picker
42302      * @param {Date} date
42303      */
42304     this.relayEvents(di, ["select"]);
42305     this.on('beforeshow', function(){
42306         if(this.picker){
42307             this.picker.hideMonthPicker(false);
42308         }
42309     }, this);
42310 };
42311 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
42312     cls:'x-date-menu'
42313 });/*
42314  * Based on:
42315  * Ext JS Library 1.1.1
42316  * Copyright(c) 2006-2007, Ext JS, LLC.
42317  *
42318  * Originally Released Under LGPL - original licence link has changed is not relivant.
42319  *
42320  * Fork - LGPL
42321  * <script type="text/javascript">
42322  */
42323  
42324
42325 /**
42326  * @class Roo.menu.ColorMenu
42327  * @extends Roo.menu.Menu
42328  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
42329  * @constructor
42330  * Creates a new ColorMenu
42331  * @param {Object} config Configuration options
42332  */
42333 Roo.menu.ColorMenu = function(config){
42334     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
42335     this.plain = true;
42336     var ci = new Roo.menu.ColorItem(config);
42337     this.add(ci);
42338     /**
42339      * The {@link Roo.ColorPalette} instance for this ColorMenu
42340      * @type ColorPalette
42341      */
42342     this.palette = ci.palette;
42343     /**
42344      * @event select
42345      * @param {ColorPalette} palette
42346      * @param {String} color
42347      */
42348     this.relayEvents(ci, ["select"]);
42349 };
42350 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
42351  * Based on:
42352  * Ext JS Library 1.1.1
42353  * Copyright(c) 2006-2007, Ext JS, LLC.
42354  *
42355  * Originally Released Under LGPL - original licence link has changed is not relivant.
42356  *
42357  * Fork - LGPL
42358  * <script type="text/javascript">
42359  */
42360  
42361 /**
42362  * @class Roo.form.TextItem
42363  * @extends Roo.BoxComponent
42364  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
42365  * @constructor
42366  * Creates a new TextItem
42367  * @param {Object} config Configuration options
42368  */
42369 Roo.form.TextItem = function(config){
42370     Roo.form.TextItem.superclass.constructor.call(this, config);
42371 };
42372
42373 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
42374     
42375     /**
42376      * @cfg {String} tag the tag for this item (default div)
42377      */
42378     tag : 'div',
42379     /**
42380      * @cfg {String} html the content for this item
42381      */
42382     html : '',
42383     
42384     getAutoCreate : function()
42385     {
42386         var cfg = {
42387             id: this.id,
42388             tag: this.tag,
42389             html: this.html,
42390             cls: 'x-form-item'
42391         };
42392         
42393         return cfg;
42394         
42395     },
42396     
42397     onRender : function(ct, position)
42398     {
42399         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
42400         
42401         if(!this.el){
42402             var cfg = this.getAutoCreate();
42403             if(!cfg.name){
42404                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
42405             }
42406             if (!cfg.name.length) {
42407                 delete cfg.name;
42408             }
42409             this.el = ct.createChild(cfg, position);
42410         }
42411     },
42412     /*
42413      * setHTML
42414      * @param {String} html update the Contents of the element.
42415      */
42416     setHTML : function(html)
42417     {
42418         this.fieldEl.dom.innerHTML = html;
42419     }
42420     
42421 });/*
42422  * Based on:
42423  * Ext JS Library 1.1.1
42424  * Copyright(c) 2006-2007, Ext JS, LLC.
42425  *
42426  * Originally Released Under LGPL - original licence link has changed is not relivant.
42427  *
42428  * Fork - LGPL
42429  * <script type="text/javascript">
42430  */
42431  
42432 /**
42433  * @class Roo.form.Field
42434  * @extends Roo.BoxComponent
42435  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
42436  * @constructor
42437  * Creates a new Field
42438  * @param {Object} config Configuration options
42439  */
42440 Roo.form.Field = function(config){
42441     Roo.form.Field.superclass.constructor.call(this, config);
42442 };
42443
42444 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
42445     /**
42446      * @cfg {String} fieldLabel Label to use when rendering a form.
42447      */
42448         /**
42449      * @cfg {String} labelSeparator the ':' after a field label (default :)  = set it to empty string to hide the field label.
42450      */
42451        /**
42452      * @cfg {String} qtip Mouse over tip
42453      */
42454      
42455     /**
42456      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
42457      */
42458     invalidClass : "x-form-invalid",
42459     /**
42460      * @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")
42461      */
42462     invalidText : "The value in this field is invalid",
42463     /**
42464      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
42465      */
42466     focusClass : "x-form-focus",
42467     /**
42468      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
42469       automatic validation (defaults to "keyup").
42470      */
42471     validationEvent : "keyup",
42472     /**
42473      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
42474      */
42475     validateOnBlur : true,
42476     /**
42477      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
42478      */
42479     validationDelay : 250,
42480     /**
42481      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42482      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
42483      */
42484     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
42485     /**
42486      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
42487      */
42488     fieldClass : "x-form-field",
42489     /**
42490      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
42491      *<pre>
42492 Value         Description
42493 -----------   ----------------------------------------------------------------------
42494 qtip          Display a quick tip when the user hovers over the field
42495 title         Display a default browser title attribute popup
42496 under         Add a block div beneath the field containing the error text
42497 side          Add an error icon to the right of the field with a popup on hover
42498 [element id]  Add the error text directly to the innerHTML of the specified element
42499 </pre>
42500      */
42501     msgTarget : 'qtip',
42502     /**
42503      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
42504      */
42505     msgFx : 'normal',
42506
42507     /**
42508      * @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.
42509      */
42510     readOnly : false,
42511
42512     /**
42513      * @cfg {Boolean} disabled True to disable the field (defaults to false).
42514      */
42515     disabled : false,
42516
42517     /**
42518      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
42519      */
42520     inputType : undefined,
42521     
42522     /**
42523      * @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).
42524          */
42525         tabIndex : undefined,
42526         
42527     // private
42528     isFormField : true,
42529
42530     // private
42531     hasFocus : false,
42532     /**
42533      * @property {Roo.Element} fieldEl
42534      * Element Containing the rendered Field (with label etc.)
42535      */
42536     /**
42537      * @cfg {Mixed} value A value to initialize this field with.
42538      */
42539     value : undefined,
42540
42541     /**
42542      * @cfg {String} name The field's HTML name attribute.
42543      */
42544     /**
42545      * @cfg {String} cls A CSS class to apply to the field's underlying element.
42546      */
42547     // private
42548     loadedValue : false,
42549      
42550      
42551         // private ??
42552         initComponent : function(){
42553         Roo.form.Field.superclass.initComponent.call(this);
42554         this.addEvents({
42555             /**
42556              * @event focus
42557              * Fires when this field receives input focus.
42558              * @param {Roo.form.Field} this
42559              */
42560             focus : true,
42561             /**
42562              * @event blur
42563              * Fires when this field loses input focus.
42564              * @param {Roo.form.Field} this
42565              */
42566             blur : true,
42567             /**
42568              * @event specialkey
42569              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
42570              * {@link Roo.EventObject#getKey} to determine which key was pressed.
42571              * @param {Roo.form.Field} this
42572              * @param {Roo.EventObject} e The event object
42573              */
42574             specialkey : true,
42575             /**
42576              * @event change
42577              * Fires just before the field blurs if the field value has changed.
42578              * @param {Roo.form.Field} this
42579              * @param {Mixed} newValue The new value
42580              * @param {Mixed} oldValue The original value
42581              */
42582             change : true,
42583             /**
42584              * @event invalid
42585              * Fires after the field has been marked as invalid.
42586              * @param {Roo.form.Field} this
42587              * @param {String} msg The validation message
42588              */
42589             invalid : true,
42590             /**
42591              * @event valid
42592              * Fires after the field has been validated with no errors.
42593              * @param {Roo.form.Field} this
42594              */
42595             valid : true,
42596              /**
42597              * @event keyup
42598              * Fires after the key up
42599              * @param {Roo.form.Field} this
42600              * @param {Roo.EventObject}  e The event Object
42601              */
42602             keyup : true
42603         });
42604     },
42605
42606     /**
42607      * Returns the name attribute of the field if available
42608      * @return {String} name The field name
42609      */
42610     getName: function(){
42611          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
42612     },
42613
42614     // private
42615     onRender : function(ct, position){
42616         Roo.form.Field.superclass.onRender.call(this, ct, position);
42617         if(!this.el){
42618             var cfg = this.getAutoCreate();
42619             if(!cfg.name){
42620                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
42621             }
42622             if (!cfg.name.length) {
42623                 delete cfg.name;
42624             }
42625             if(this.inputType){
42626                 cfg.type = this.inputType;
42627             }
42628             this.el = ct.createChild(cfg, position);
42629         }
42630         var type = this.el.dom.type;
42631         if(type){
42632             if(type == 'password'){
42633                 type = 'text';
42634             }
42635             this.el.addClass('x-form-'+type);
42636         }
42637         if(this.readOnly){
42638             this.el.dom.readOnly = true;
42639         }
42640         if(this.tabIndex !== undefined){
42641             this.el.dom.setAttribute('tabIndex', this.tabIndex);
42642         }
42643
42644         this.el.addClass([this.fieldClass, this.cls]);
42645         this.initValue();
42646     },
42647
42648     /**
42649      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
42650      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
42651      * @return {Roo.form.Field} this
42652      */
42653     applyTo : function(target){
42654         this.allowDomMove = false;
42655         this.el = Roo.get(target);
42656         this.render(this.el.dom.parentNode);
42657         return this;
42658     },
42659
42660     // private
42661     initValue : function(){
42662         if(this.value !== undefined){
42663             this.setValue(this.value);
42664         }else if(this.el.dom.value.length > 0){
42665             this.setValue(this.el.dom.value);
42666         }
42667     },
42668
42669     /**
42670      * Returns true if this field has been changed since it was originally loaded and is not disabled.
42671      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
42672      */
42673     isDirty : function() {
42674         if(this.disabled) {
42675             return false;
42676         }
42677         return String(this.getValue()) !== String(this.originalValue);
42678     },
42679
42680     /**
42681      * stores the current value in loadedValue
42682      */
42683     resetHasChanged : function()
42684     {
42685         this.loadedValue = String(this.getValue());
42686     },
42687     /**
42688      * checks the current value against the 'loaded' value.
42689      * Note - will return false if 'resetHasChanged' has not been called first.
42690      */
42691     hasChanged : function()
42692     {
42693         if(this.disabled || this.readOnly) {
42694             return false;
42695         }
42696         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
42697     },
42698     
42699     
42700     
42701     // private
42702     afterRender : function(){
42703         Roo.form.Field.superclass.afterRender.call(this);
42704         this.initEvents();
42705     },
42706
42707     // private
42708     fireKey : function(e){
42709         //Roo.log('field ' + e.getKey());
42710         if(e.isNavKeyPress()){
42711             this.fireEvent("specialkey", this, e);
42712         }
42713     },
42714
42715     /**
42716      * Resets the current field value to the originally loaded value and clears any validation messages
42717      */
42718     reset : function(){
42719         this.setValue(this.resetValue);
42720         this.originalValue = this.getValue();
42721         this.clearInvalid();
42722     },
42723
42724     // private
42725     initEvents : function(){
42726         // safari killled keypress - so keydown is now used..
42727         this.el.on("keydown" , this.fireKey,  this);
42728         this.el.on("focus", this.onFocus,  this);
42729         this.el.on("blur", this.onBlur,  this);
42730         this.el.relayEvent('keyup', this);
42731
42732         // reference to original value for reset
42733         this.originalValue = this.getValue();
42734         this.resetValue =  this.getValue();
42735     },
42736
42737     // private
42738     onFocus : function(){
42739         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
42740             this.el.addClass(this.focusClass);
42741         }
42742         if(!this.hasFocus){
42743             this.hasFocus = true;
42744             this.startValue = this.getValue();
42745             this.fireEvent("focus", this);
42746         }
42747     },
42748
42749     beforeBlur : Roo.emptyFn,
42750
42751     // private
42752     onBlur : function(){
42753         this.beforeBlur();
42754         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
42755             this.el.removeClass(this.focusClass);
42756         }
42757         this.hasFocus = false;
42758         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
42759             this.validate();
42760         }
42761         var v = this.getValue();
42762         if(String(v) !== String(this.startValue)){
42763             this.fireEvent('change', this, v, this.startValue);
42764         }
42765         this.fireEvent("blur", this);
42766     },
42767
42768     /**
42769      * Returns whether or not the field value is currently valid
42770      * @param {Boolean} preventMark True to disable marking the field invalid
42771      * @return {Boolean} True if the value is valid, else false
42772      */
42773     isValid : function(preventMark){
42774         if(this.disabled){
42775             return true;
42776         }
42777         var restore = this.preventMark;
42778         this.preventMark = preventMark === true;
42779         var v = this.validateValue(this.processValue(this.getRawValue()));
42780         this.preventMark = restore;
42781         return v;
42782     },
42783
42784     /**
42785      * Validates the field value
42786      * @return {Boolean} True if the value is valid, else false
42787      */
42788     validate : function(){
42789         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
42790             this.clearInvalid();
42791             return true;
42792         }
42793         return false;
42794     },
42795
42796     processValue : function(value){
42797         return value;
42798     },
42799
42800     // private
42801     // Subclasses should provide the validation implementation by overriding this
42802     validateValue : function(value){
42803         return true;
42804     },
42805
42806     /**
42807      * Mark this field as invalid
42808      * @param {String} msg The validation message
42809      */
42810     markInvalid : function(msg){
42811         if(!this.rendered || this.preventMark){ // not rendered
42812             return;
42813         }
42814         
42815         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
42816         
42817         obj.el.addClass(this.invalidClass);
42818         msg = msg || this.invalidText;
42819         switch(this.msgTarget){
42820             case 'qtip':
42821                 obj.el.dom.qtip = msg;
42822                 obj.el.dom.qclass = 'x-form-invalid-tip';
42823                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
42824                     Roo.QuickTips.enable();
42825                 }
42826                 break;
42827             case 'title':
42828                 this.el.dom.title = msg;
42829                 break;
42830             case 'under':
42831                 if(!this.errorEl){
42832                     var elp = this.el.findParent('.x-form-element', 5, true);
42833                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
42834                     this.errorEl.setWidth(elp.getWidth(true)-20);
42835                 }
42836                 this.errorEl.update(msg);
42837                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
42838                 break;
42839             case 'side':
42840                 if(!this.errorIcon){
42841                     var elp = this.el.findParent('.x-form-element', 5, true);
42842                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
42843                 }
42844                 this.alignErrorIcon();
42845                 this.errorIcon.dom.qtip = msg;
42846                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
42847                 this.errorIcon.show();
42848                 this.on('resize', this.alignErrorIcon, this);
42849                 break;
42850             default:
42851                 var t = Roo.getDom(this.msgTarget);
42852                 t.innerHTML = msg;
42853                 t.style.display = this.msgDisplay;
42854                 break;
42855         }
42856         this.fireEvent('invalid', this, msg);
42857     },
42858
42859     // private
42860     alignErrorIcon : function(){
42861         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
42862     },
42863
42864     /**
42865      * Clear any invalid styles/messages for this field
42866      */
42867     clearInvalid : function(){
42868         if(!this.rendered || this.preventMark){ // not rendered
42869             return;
42870         }
42871         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
42872         
42873         obj.el.removeClass(this.invalidClass);
42874         switch(this.msgTarget){
42875             case 'qtip':
42876                 obj.el.dom.qtip = '';
42877                 break;
42878             case 'title':
42879                 this.el.dom.title = '';
42880                 break;
42881             case 'under':
42882                 if(this.errorEl){
42883                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
42884                 }
42885                 break;
42886             case 'side':
42887                 if(this.errorIcon){
42888                     this.errorIcon.dom.qtip = '';
42889                     this.errorIcon.hide();
42890                     this.un('resize', this.alignErrorIcon, this);
42891                 }
42892                 break;
42893             default:
42894                 var t = Roo.getDom(this.msgTarget);
42895                 t.innerHTML = '';
42896                 t.style.display = 'none';
42897                 break;
42898         }
42899         this.fireEvent('valid', this);
42900     },
42901
42902     /**
42903      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
42904      * @return {Mixed} value The field value
42905      */
42906     getRawValue : function(){
42907         var v = this.el.getValue();
42908         
42909         return v;
42910     },
42911
42912     /**
42913      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
42914      * @return {Mixed} value The field value
42915      */
42916     getValue : function(){
42917         var v = this.el.getValue();
42918          
42919         return v;
42920     },
42921
42922     /**
42923      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
42924      * @param {Mixed} value The value to set
42925      */
42926     setRawValue : function(v){
42927         return this.el.dom.value = (v === null || v === undefined ? '' : v);
42928     },
42929
42930     /**
42931      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
42932      * @param {Mixed} value The value to set
42933      */
42934     setValue : function(v){
42935         this.value = v;
42936         if(this.rendered){
42937             this.el.dom.value = (v === null || v === undefined ? '' : v);
42938              this.validate();
42939         }
42940     },
42941
42942     adjustSize : function(w, h){
42943         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
42944         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
42945         return s;
42946     },
42947
42948     adjustWidth : function(tag, w){
42949         tag = tag.toLowerCase();
42950         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
42951             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
42952                 if(tag == 'input'){
42953                     return w + 2;
42954                 }
42955                 if(tag == 'textarea'){
42956                     return w-2;
42957                 }
42958             }else if(Roo.isOpera){
42959                 if(tag == 'input'){
42960                     return w + 2;
42961                 }
42962                 if(tag == 'textarea'){
42963                     return w-2;
42964                 }
42965             }
42966         }
42967         return w;
42968     }
42969 });
42970
42971
42972 // anything other than normal should be considered experimental
42973 Roo.form.Field.msgFx = {
42974     normal : {
42975         show: function(msgEl, f){
42976             msgEl.setDisplayed('block');
42977         },
42978
42979         hide : function(msgEl, f){
42980             msgEl.setDisplayed(false).update('');
42981         }
42982     },
42983
42984     slide : {
42985         show: function(msgEl, f){
42986             msgEl.slideIn('t', {stopFx:true});
42987         },
42988
42989         hide : function(msgEl, f){
42990             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
42991         }
42992     },
42993
42994     slideRight : {
42995         show: function(msgEl, f){
42996             msgEl.fixDisplay();
42997             msgEl.alignTo(f.el, 'tl-tr');
42998             msgEl.slideIn('l', {stopFx:true});
42999         },
43000
43001         hide : function(msgEl, f){
43002             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
43003         }
43004     }
43005 };/*
43006  * Based on:
43007  * Ext JS Library 1.1.1
43008  * Copyright(c) 2006-2007, Ext JS, LLC.
43009  *
43010  * Originally Released Under LGPL - original licence link has changed is not relivant.
43011  *
43012  * Fork - LGPL
43013  * <script type="text/javascript">
43014  */
43015  
43016
43017 /**
43018  * @class Roo.form.TextField
43019  * @extends Roo.form.Field
43020  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
43021  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
43022  * @constructor
43023  * Creates a new TextField
43024  * @param {Object} config Configuration options
43025  */
43026 Roo.form.TextField = function(config){
43027     Roo.form.TextField.superclass.constructor.call(this, config);
43028     this.addEvents({
43029         /**
43030          * @event autosize
43031          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
43032          * according to the default logic, but this event provides a hook for the developer to apply additional
43033          * logic at runtime to resize the field if needed.
43034              * @param {Roo.form.Field} this This text field
43035              * @param {Number} width The new field width
43036              */
43037         autosize : true
43038     });
43039 };
43040
43041 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
43042     /**
43043      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
43044      */
43045     grow : false,
43046     /**
43047      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
43048      */
43049     growMin : 30,
43050     /**
43051      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
43052      */
43053     growMax : 800,
43054     /**
43055      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
43056      */
43057     vtype : null,
43058     /**
43059      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
43060      */
43061     maskRe : null,
43062     /**
43063      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
43064      */
43065     disableKeyFilter : false,
43066     /**
43067      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
43068      */
43069     allowBlank : true,
43070     /**
43071      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
43072      */
43073     minLength : 0,
43074     /**
43075      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
43076      */
43077     maxLength : Number.MAX_VALUE,
43078     /**
43079      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
43080      */
43081     minLengthText : "The minimum length for this field is {0}",
43082     /**
43083      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
43084      */
43085     maxLengthText : "The maximum length for this field is {0}",
43086     /**
43087      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
43088      */
43089     selectOnFocus : false,
43090     /**
43091      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
43092      */    
43093     allowLeadingSpace : false,
43094     /**
43095      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
43096      */
43097     blankText : "This field is required",
43098     /**
43099      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
43100      * If available, this function will be called only after the basic validators all return true, and will be passed the
43101      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
43102      */
43103     validator : null,
43104     /**
43105      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
43106      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
43107      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
43108      */
43109     regex : null,
43110     /**
43111      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
43112      */
43113     regexText : "",
43114     /**
43115      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
43116      */
43117     emptyText : null,
43118    
43119
43120     // private
43121     initEvents : function()
43122     {
43123         if (this.emptyText) {
43124             this.el.attr('placeholder', this.emptyText);
43125         }
43126         
43127         Roo.form.TextField.superclass.initEvents.call(this);
43128         if(this.validationEvent == 'keyup'){
43129             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43130             this.el.on('keyup', this.filterValidation, this);
43131         }
43132         else if(this.validationEvent !== false){
43133             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43134         }
43135         
43136         if(this.selectOnFocus){
43137             this.on("focus", this.preFocus, this);
43138         }
43139                 if (!this.allowLeadingSpace) {
43140                         this.on('blur', this.cleanLeadingSpace, this);
43141                 }
43142         
43143         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43144             this.el.on("keypress", this.filterKeys, this);
43145         }
43146         if(this.grow){
43147             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
43148             this.el.on("click", this.autoSize,  this);
43149         }
43150         if(this.el.is('input[type=password]') && Roo.isSafari){
43151             this.el.on('keydown', this.SafariOnKeyDown, this);
43152         }
43153     },
43154
43155     processValue : function(value){
43156         if(this.stripCharsRe){
43157             var newValue = value.replace(this.stripCharsRe, '');
43158             if(newValue !== value){
43159                 this.setRawValue(newValue);
43160                 return newValue;
43161             }
43162         }
43163         return value;
43164     },
43165
43166     filterValidation : function(e){
43167         if(!e.isNavKeyPress()){
43168             this.validationTask.delay(this.validationDelay);
43169         }
43170     },
43171
43172     // private
43173     onKeyUp : function(e){
43174         if(!e.isNavKeyPress()){
43175             this.autoSize();
43176         }
43177     },
43178     // private - clean the leading white space
43179     cleanLeadingSpace : function(e)
43180     {
43181         if ( this.inputType == 'file') {
43182             return;
43183         }
43184         
43185         this.setValue((this.getValue() + '').replace(/^\s+/,''));
43186     },
43187     /**
43188      * Resets the current field value to the originally-loaded value and clears any validation messages.
43189      *  
43190      */
43191     reset : function(){
43192         Roo.form.TextField.superclass.reset.call(this);
43193        
43194     }, 
43195     // private
43196     preFocus : function(){
43197         
43198         if(this.selectOnFocus){
43199             this.el.dom.select();
43200         }
43201     },
43202
43203     
43204     // private
43205     filterKeys : function(e){
43206         var k = e.getKey();
43207         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
43208             return;
43209         }
43210         var c = e.getCharCode(), cc = String.fromCharCode(c);
43211         if(Roo.isIE && (e.isSpecialKey() || !cc)){
43212             return;
43213         }
43214         if(!this.maskRe.test(cc)){
43215             e.stopEvent();
43216         }
43217     },
43218
43219     setValue : function(v){
43220         
43221         Roo.form.TextField.superclass.setValue.apply(this, arguments);
43222         
43223         this.autoSize();
43224     },
43225
43226     /**
43227      * Validates a value according to the field's validation rules and marks the field as invalid
43228      * if the validation fails
43229      * @param {Mixed} value The value to validate
43230      * @return {Boolean} True if the value is valid, else false
43231      */
43232     validateValue : function(value){
43233         if(value.length < 1)  { // if it's blank
43234              if(this.allowBlank){
43235                 this.clearInvalid();
43236                 return true;
43237              }else{
43238                 this.markInvalid(this.blankText);
43239                 return false;
43240              }
43241         }
43242         if(value.length < this.minLength){
43243             this.markInvalid(String.format(this.minLengthText, this.minLength));
43244             return false;
43245         }
43246         if(value.length > this.maxLength){
43247             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
43248             return false;
43249         }
43250         if(this.vtype){
43251             var vt = Roo.form.VTypes;
43252                         if (value.trim() != value) { // trim before checking email (and other stuf??)
43253                                 value = value.trim();
43254                                 this.el.dom.value  = value;
43255                         }
43256                         
43257             if(!vt[this.vtype](value, this)){
43258                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
43259                 return false;
43260             }
43261         }
43262         if(typeof this.validator == "function"){
43263             var msg = this.validator(value);
43264             if(msg !== true){
43265                 this.markInvalid(msg);
43266                 return false;
43267             }
43268         }
43269         if(this.regex && !this.regex.test(value)){
43270             this.markInvalid(this.regexText);
43271             return false;
43272         }
43273         return true;
43274     },
43275
43276     /**
43277      * Selects text in this field
43278      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
43279      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
43280      */
43281     selectText : function(start, end){
43282         var v = this.getRawValue();
43283         if(v.length > 0){
43284             start = start === undefined ? 0 : start;
43285             end = end === undefined ? v.length : end;
43286             var d = this.el.dom;
43287             if(d.setSelectionRange){
43288                 d.setSelectionRange(start, end);
43289             }else if(d.createTextRange){
43290                 var range = d.createTextRange();
43291                 range.moveStart("character", start);
43292                 range.moveEnd("character", v.length-end);
43293                 range.select();
43294             }
43295         }
43296     },
43297
43298     /**
43299      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
43300      * This only takes effect if grow = true, and fires the autosize event.
43301      */
43302     autoSize : function(){
43303         if(!this.grow || !this.rendered){
43304             return;
43305         }
43306         if(!this.metrics){
43307             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
43308         }
43309         var el = this.el;
43310         var v = el.dom.value;
43311         var d = document.createElement('div');
43312         d.appendChild(document.createTextNode(v));
43313         v = d.innerHTML;
43314         d = null;
43315         v += "&#160;";
43316         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
43317         this.el.setWidth(w);
43318         this.fireEvent("autosize", this, w);
43319     },
43320     
43321     // private
43322     SafariOnKeyDown : function(event)
43323     {
43324         // this is a workaround for a password hang bug on chrome/ webkit.
43325         
43326         var isSelectAll = false;
43327         
43328         if(this.el.dom.selectionEnd > 0){
43329             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
43330         }
43331         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
43332             event.preventDefault();
43333             this.setValue('');
43334             return;
43335         }
43336         
43337         // skip handling paste
43338         if(isSelectAll && event.getCharCode() > 31 && !(event.ctrlKey && event.getCharCode() == 86)){ // backspace and delete key
43339             
43340             event.preventDefault();
43341             // this is very hacky as keydown always get's upper case.
43342             
43343             var cc = String.fromCharCode(event.getCharCode());
43344             
43345             
43346             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
43347             
43348         }
43349         
43350         
43351     }
43352 });Roo.form.Password = function(config){
43353     Roo.form.Password.superclass.constructor.call(this, config);
43354
43355     this.inputType = 'password';
43356 };
43357
43358 Roo.extend(Roo.form.Password, Roo.form.TextField,  {
43359     onRender : function(ct, position)
43360     {
43361         Roo.form.Password.superclass.onRender.call(this, ct, position);
43362
43363         this.parentEl().addClass('form-password');
43364
43365         this.wrap = this.el.wrap({
43366             cls : 'password-wrap'
43367         });
43368
43369         this.toggle = this.wrap.createChild({
43370             tag : 'Button',
43371             cls : 'password-toggle'
43372         });
43373
43374
43375         this.toggleEl().addClass('password-hidden');
43376
43377         this.toggleEl().on('click', this.onToggleClick, this);;
43378     },
43379     
43380     parentEl : function()
43381     {
43382         return this.el.findParent('.x-form-element', 5, true);
43383     },
43384
43385     toggleEl: function()
43386     {
43387         return this.parentEl().select('button.password-toggle',true).first();
43388     },
43389
43390     onToggleClick : function(e) 
43391     {
43392         var input = this.el;
43393         var toggle = this.toggleEl();
43394
43395         toggle.removeClass(['password-visible', 'password-hidden']);
43396
43397         if(input.attr('type') == 'password') {
43398             input.attr('type', 'text');
43399             toggle.addClass('password-visible');
43400         }
43401         else {
43402             input.attr('type', 'password');
43403             toggle.addClass('password-hidden');
43404         }
43405     }
43406 });/*
43407  * Based on:
43408  * Ext JS Library 1.1.1
43409  * Copyright(c) 2006-2007, Ext JS, LLC.
43410  *
43411  * Originally Released Under LGPL - original licence link has changed is not relivant.
43412  *
43413  * Fork - LGPL
43414  * <script type="text/javascript">
43415  */
43416  
43417 /**
43418  * @class Roo.form.Hidden
43419  * @extends Roo.form.TextField
43420  * Simple Hidden element used on forms 
43421  * 
43422  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
43423  * 
43424  * @constructor
43425  * Creates a new Hidden form element.
43426  * @param {Object} config Configuration options
43427  */
43428
43429
43430
43431 // easy hidden field...
43432 Roo.form.Hidden = function(config){
43433     Roo.form.Hidden.superclass.constructor.call(this, config);
43434 };
43435   
43436 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
43437     fieldLabel:      '',
43438     inputType:      'hidden',
43439     width:          50,
43440     allowBlank:     true,
43441     labelSeparator: '',
43442     hidden:         true,
43443     itemCls :       'x-form-item-display-none'
43444
43445
43446 });
43447
43448
43449 /*
43450  * Based on:
43451  * Ext JS Library 1.1.1
43452  * Copyright(c) 2006-2007, Ext JS, LLC.
43453  *
43454  * Originally Released Under LGPL - original licence link has changed is not relivant.
43455  *
43456  * Fork - LGPL
43457  * <script type="text/javascript">
43458  */
43459  
43460 /**
43461  * @class Roo.form.TriggerField
43462  * @extends Roo.form.TextField
43463  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
43464  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
43465  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
43466  * for which you can provide a custom implementation.  For example:
43467  * <pre><code>
43468 var trigger = new Roo.form.TriggerField();
43469 trigger.onTriggerClick = myTriggerFn;
43470 trigger.applyTo('my-field');
43471 </code></pre>
43472  *
43473  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
43474  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
43475  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
43476  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
43477  * @constructor
43478  * Create a new TriggerField.
43479  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
43480  * to the base TextField)
43481  */
43482 Roo.form.TriggerField = function(config){
43483     this.mimicing = false;
43484     Roo.form.TriggerField.superclass.constructor.call(this, config);
43485 };
43486
43487 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
43488     /**
43489      * @cfg {String} triggerClass A CSS class to apply to the trigger
43490      */
43491     /**
43492      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43493      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
43494      */
43495     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
43496     /**
43497      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
43498      */
43499     hideTrigger:false,
43500
43501     /** @cfg {Boolean} grow @hide */
43502     /** @cfg {Number} growMin @hide */
43503     /** @cfg {Number} growMax @hide */
43504
43505     /**
43506      * @hide 
43507      * @method
43508      */
43509     autoSize: Roo.emptyFn,
43510     // private
43511     monitorTab : true,
43512     // private
43513     deferHeight : true,
43514
43515     
43516     actionMode : 'wrap',
43517     // private
43518     onResize : function(w, h){
43519         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
43520         if(typeof w == 'number'){
43521             var x = w - this.trigger.getWidth();
43522             this.el.setWidth(this.adjustWidth('input', x));
43523             this.trigger.setStyle('left', x+'px');
43524         }
43525     },
43526
43527     // private
43528     adjustSize : Roo.BoxComponent.prototype.adjustSize,
43529
43530     // private
43531     getResizeEl : function(){
43532         return this.wrap;
43533     },
43534
43535     // private
43536     getPositionEl : function(){
43537         return this.wrap;
43538     },
43539
43540     // private
43541     alignErrorIcon : function(){
43542         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
43543     },
43544
43545     // private
43546     onRender : function(ct, position){
43547         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
43548         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
43549         this.trigger = this.wrap.createChild(this.triggerConfig ||
43550                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
43551         if(this.hideTrigger){
43552             this.trigger.setDisplayed(false);
43553         }
43554         this.initTrigger();
43555         if(!this.width){
43556             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
43557         }
43558     },
43559
43560     // private
43561     initTrigger : function(){
43562         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43563         this.trigger.addClassOnOver('x-form-trigger-over');
43564         this.trigger.addClassOnClick('x-form-trigger-click');
43565     },
43566
43567     // private
43568     onDestroy : function(){
43569         if(this.trigger){
43570             this.trigger.removeAllListeners();
43571             this.trigger.remove();
43572         }
43573         if(this.wrap){
43574             this.wrap.remove();
43575         }
43576         Roo.form.TriggerField.superclass.onDestroy.call(this);
43577     },
43578
43579     // private
43580     onFocus : function(){
43581         Roo.form.TriggerField.superclass.onFocus.call(this);
43582         if(!this.mimicing){
43583             this.wrap.addClass('x-trigger-wrap-focus');
43584             this.mimicing = true;
43585             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
43586             if(this.monitorTab){
43587                 this.el.on("keydown", this.checkTab, this);
43588             }
43589         }
43590     },
43591
43592     // private
43593     checkTab : function(e){
43594         if(e.getKey() == e.TAB){
43595             this.triggerBlur();
43596         }
43597     },
43598
43599     // private
43600     onBlur : function(){
43601         // do nothing
43602     },
43603
43604     // private
43605     mimicBlur : function(e, t){
43606         if(!this.wrap.contains(t) && this.validateBlur()){
43607             this.triggerBlur();
43608         }
43609     },
43610
43611     // private
43612     triggerBlur : function(){
43613         this.mimicing = false;
43614         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
43615         if(this.monitorTab){
43616             this.el.un("keydown", this.checkTab, this);
43617         }
43618         this.wrap.removeClass('x-trigger-wrap-focus');
43619         Roo.form.TriggerField.superclass.onBlur.call(this);
43620     },
43621
43622     // private
43623     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
43624     validateBlur : function(e, t){
43625         return true;
43626     },
43627
43628     // private
43629     onDisable : function(){
43630         Roo.form.TriggerField.superclass.onDisable.call(this);
43631         if(this.wrap){
43632             this.wrap.addClass('x-item-disabled');
43633         }
43634     },
43635
43636     // private
43637     onEnable : function(){
43638         Roo.form.TriggerField.superclass.onEnable.call(this);
43639         if(this.wrap){
43640             this.wrap.removeClass('x-item-disabled');
43641         }
43642     },
43643
43644     // private
43645     onShow : function(){
43646         var ae = this.getActionEl();
43647         
43648         if(ae){
43649             ae.dom.style.display = '';
43650             ae.dom.style.visibility = 'visible';
43651         }
43652     },
43653
43654     // private
43655     
43656     onHide : function(){
43657         var ae = this.getActionEl();
43658         ae.dom.style.display = 'none';
43659     },
43660
43661     /**
43662      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
43663      * by an implementing function.
43664      * @method
43665      * @param {EventObject} e
43666      */
43667     onTriggerClick : Roo.emptyFn
43668 });
43669
43670 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
43671 // to be extended by an implementing class.  For an example of implementing this class, see the custom
43672 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
43673 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
43674     initComponent : function(){
43675         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
43676
43677         this.triggerConfig = {
43678             tag:'span', cls:'x-form-twin-triggers', cn:[
43679             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
43680             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
43681         ]};
43682     },
43683
43684     getTrigger : function(index){
43685         return this.triggers[index];
43686     },
43687
43688     initTrigger : function(){
43689         var ts = this.trigger.select('.x-form-trigger', true);
43690         this.wrap.setStyle('overflow', 'hidden');
43691         var triggerField = this;
43692         ts.each(function(t, all, index){
43693             t.hide = function(){
43694                 var w = triggerField.wrap.getWidth();
43695                 this.dom.style.display = 'none';
43696                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
43697             };
43698             t.show = function(){
43699                 var w = triggerField.wrap.getWidth();
43700                 this.dom.style.display = '';
43701                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
43702             };
43703             var triggerIndex = 'Trigger'+(index+1);
43704
43705             if(this['hide'+triggerIndex]){
43706                 t.dom.style.display = 'none';
43707             }
43708             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
43709             t.addClassOnOver('x-form-trigger-over');
43710             t.addClassOnClick('x-form-trigger-click');
43711         }, this);
43712         this.triggers = ts.elements;
43713     },
43714
43715     onTrigger1Click : Roo.emptyFn,
43716     onTrigger2Click : Roo.emptyFn
43717 });/*
43718  * Based on:
43719  * Ext JS Library 1.1.1
43720  * Copyright(c) 2006-2007, Ext JS, LLC.
43721  *
43722  * Originally Released Under LGPL - original licence link has changed is not relivant.
43723  *
43724  * Fork - LGPL
43725  * <script type="text/javascript">
43726  */
43727  
43728 /**
43729  * @class Roo.form.TextArea
43730  * @extends Roo.form.TextField
43731  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
43732  * support for auto-sizing.
43733  * @constructor
43734  * Creates a new TextArea
43735  * @param {Object} config Configuration options
43736  */
43737 Roo.form.TextArea = function(config){
43738     Roo.form.TextArea.superclass.constructor.call(this, config);
43739     // these are provided exchanges for backwards compat
43740     // minHeight/maxHeight were replaced by growMin/growMax to be
43741     // compatible with TextField growing config values
43742     if(this.minHeight !== undefined){
43743         this.growMin = this.minHeight;
43744     }
43745     if(this.maxHeight !== undefined){
43746         this.growMax = this.maxHeight;
43747     }
43748 };
43749
43750 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
43751     /**
43752      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
43753      */
43754     growMin : 60,
43755     /**
43756      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
43757      */
43758     growMax: 1000,
43759     /**
43760      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
43761      * in the field (equivalent to setting overflow: hidden, defaults to false)
43762      */
43763     preventScrollbars: false,
43764     /**
43765      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43766      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
43767      */
43768
43769     // private
43770     onRender : function(ct, position){
43771         if(!this.el){
43772             this.defaultAutoCreate = {
43773                 tag: "textarea",
43774                 style:"width:300px;height:60px;",
43775                 autocomplete: "new-password"
43776             };
43777         }
43778         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
43779         if(this.grow){
43780             this.textSizeEl = Roo.DomHelper.append(document.body, {
43781                 tag: "pre", cls: "x-form-grow-sizer"
43782             });
43783             if(this.preventScrollbars){
43784                 this.el.setStyle("overflow", "hidden");
43785             }
43786             this.el.setHeight(this.growMin);
43787         }
43788     },
43789
43790     onDestroy : function(){
43791         if(this.textSizeEl){
43792             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
43793         }
43794         Roo.form.TextArea.superclass.onDestroy.call(this);
43795     },
43796
43797     // private
43798     onKeyUp : function(e){
43799         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
43800             this.autoSize();
43801         }
43802     },
43803
43804     /**
43805      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
43806      * This only takes effect if grow = true, and fires the autosize event if the height changes.
43807      */
43808     autoSize : function(){
43809         if(!this.grow || !this.textSizeEl){
43810             return;
43811         }
43812         var el = this.el;
43813         var v = el.dom.value;
43814         var ts = this.textSizeEl;
43815
43816         ts.innerHTML = '';
43817         ts.appendChild(document.createTextNode(v));
43818         v = ts.innerHTML;
43819
43820         Roo.fly(ts).setWidth(this.el.getWidth());
43821         if(v.length < 1){
43822             v = "&#160;&#160;";
43823         }else{
43824             if(Roo.isIE){
43825                 v = v.replace(/\n/g, '<p>&#160;</p>');
43826             }
43827             v += "&#160;\n&#160;";
43828         }
43829         ts.innerHTML = v;
43830         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
43831         if(h != this.lastHeight){
43832             this.lastHeight = h;
43833             this.el.setHeight(h);
43834             this.fireEvent("autosize", this, h);
43835         }
43836     }
43837 });/*
43838  * Based on:
43839  * Ext JS Library 1.1.1
43840  * Copyright(c) 2006-2007, Ext JS, LLC.
43841  *
43842  * Originally Released Under LGPL - original licence link has changed is not relivant.
43843  *
43844  * Fork - LGPL
43845  * <script type="text/javascript">
43846  */
43847  
43848
43849 /**
43850  * @class Roo.form.NumberField
43851  * @extends Roo.form.TextField
43852  * Numeric text field that provides automatic keystroke filtering and numeric validation.
43853  * @constructor
43854  * Creates a new NumberField
43855  * @param {Object} config Configuration options
43856  */
43857 Roo.form.NumberField = function(config){
43858     Roo.form.NumberField.superclass.constructor.call(this, config);
43859 };
43860
43861 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
43862     /**
43863      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
43864      */
43865     fieldClass: "x-form-field x-form-num-field",
43866     /**
43867      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43868      */
43869     allowDecimals : true,
43870     /**
43871      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43872      */
43873     decimalSeparator : ".",
43874     /**
43875      * @cfg {String} thousandSeparator Character(s) to allow as the thousand separator (defaults to '') - set to ',' for example
43876      */
43877     thousandSeparator : "",
43878     /**
43879      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43880      */
43881     decimalPrecision : 2,
43882     /**
43883      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43884      */
43885     allowNegative : true,
43886     /**
43887      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43888      */
43889     minValue : Number.NEGATIVE_INFINITY,
43890     /**
43891      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43892      */
43893     maxValue : Number.MAX_VALUE,
43894     /**
43895      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43896      */
43897     minText : "The minimum value for this field is {0}",
43898     /**
43899      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43900      */
43901     maxText : "The maximum value for this field is {0}",
43902     /**
43903      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43904      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43905      */
43906     nanText : "{0} is not a valid number",
43907     
43908     hiddenField : false,
43909      
43910     onRender : function(ct, position)
43911     {
43912         Roo.form.TextField.superclass.onRender.call(this, ct, position);
43913     
43914             //this.el.dom.removeAttribute('name'); 
43915         Roo.log("Changing name?");
43916         if (this.thousandSeparator != '') {
43917             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
43918             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
43919                         'before', true);
43920             this.hiddenField.value = this.value ? this.parseValue(this.value) : '';
43921             this.el.on('blur', this.onBlur, this);
43922         }
43923         
43924             // prevent input submission
43925         
43926             
43927             
43928     },
43929      onBlur : function(){
43930         this.beforeBlur();
43931         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43932             this.el.removeClass(this.focusClass);
43933         }
43934         this.hasFocus = false;
43935         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43936             this.validate();
43937         }
43938         var v = this.getValue();
43939         if(String(v) !== String(this.startValue)){
43940             this.setValue( this.parseValue(v));
43941             this.fireEvent('change', this, v, this.startValue);
43942         }
43943         this.fireEvent("blur", this);
43944     },
43945     
43946     // override name, so that it works with hidden field.
43947     getName: function(){
43948         if (this.thousandSeparator != '') {
43949             return this.name;
43950         }
43951         return Roo.form.TextField.superclass.getName.call(this);
43952     },
43953     // private
43954     initEvents : function(){
43955           
43956         var allowed = "0123456789";
43957         if(this.allowDecimals){
43958             allowed += this.decimalSeparator;
43959         }
43960         allowed += this.thousandSeparator;
43961         if(this.allowNegative){
43962             allowed += "-";
43963         }
43964         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43965         var keyPress = function(e){
43966             var k = e.getKey();
43967             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43968                 return;
43969             }
43970             var c = e.getCharCode();
43971             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43972                 e.stopEvent();
43973             }
43974         };
43975         this.el.on("keypress", keyPress, this);
43976     },
43977
43978     // private
43979     validateValue : function(value){
43980         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
43981             return false;
43982         }
43983         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
43984              return true;
43985         }
43986         var num = this.parseValue(value);
43987         if(isNaN(num)){
43988             this.markInvalid(String.format(this.nanText, value));
43989             return false;
43990         }
43991         if(num < this.minValue){
43992             this.markInvalid(String.format(this.minText, this.minValue));
43993             return false;
43994         }
43995         if(num > this.maxValue){
43996             this.markInvalid(String.format(this.maxText, this.maxValue));
43997             return false;
43998         }
43999         return true;
44000     },
44001
44002     getValue : function(){
44003         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
44004     },
44005
44006     // private
44007     parseValue : function(value){
44008         value = parseFloat(String(value).replace(this.decimalSeparator, ".").split(this.thousandSeparator).join(''));
44009         return isNaN(value) ? '' : value;
44010     },
44011
44012     // private
44013     fixPrecision : function(value){
44014         var nan = isNaN(value);
44015         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44016             return nan ? '' : value;
44017         }
44018         return parseFloat(value).toFixed(this.decimalPrecision);
44019     },
44020
44021     setValue : function(v){
44022         v = this.fixPrecision(v);
44023         if(this.thousandSeparator != ''){
44024             v = Roo.util.Format.number(v, this.decimalPrecision, this.thousandSeparator);
44025         } 
44026         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
44027         if (this.hiddenField !== false) {
44028             this.hiddenField.value = v ? this.parseValue(v) : '';
44029         }
44030         
44031
44032     },
44033
44034     // private
44035     decimalPrecisionFcn : function(v){
44036         return Math.floor(v);
44037     },
44038
44039     beforeBlur : function(){
44040         var v = this.parseValue(this.getRawValue());
44041         if(v){
44042             this.setValue(v);
44043         }
44044     }
44045 });/*
44046  * Based on:
44047  * Ext JS Library 1.1.1
44048  * Copyright(c) 2006-2007, Ext JS, LLC.
44049  *
44050  * Originally Released Under LGPL - original licence link has changed is not relivant.
44051  *
44052  * Fork - LGPL
44053  * <script type="text/javascript">
44054  */
44055  
44056 /**
44057  * @class Roo.form.DateField
44058  * @extends Roo.form.TriggerField
44059  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
44060 * @constructor
44061 * Create a new DateField
44062 * @param {Object} config
44063  */
44064 Roo.form.DateField = function(config)
44065 {
44066     Roo.form.DateField.superclass.constructor.call(this, config);
44067     
44068       this.addEvents({
44069          
44070         /**
44071          * @event select
44072          * Fires when a date is selected
44073              * @param {Roo.form.DateField} combo This combo box
44074              * @param {Date} date The date selected
44075              */
44076         'select' : true
44077          
44078     });
44079     
44080     
44081     if(typeof this.minValue == "string") {
44082         this.minValue = this.parseDate(this.minValue);
44083     }
44084     if(typeof this.maxValue == "string") {
44085         this.maxValue = this.parseDate(this.maxValue);
44086     }
44087     this.ddMatch = null;
44088     if(this.disabledDates){
44089         var dd = this.disabledDates;
44090         var re = "(?:";
44091         for(var i = 0; i < dd.length; i++){
44092             re += dd[i];
44093             if(i != dd.length-1) {
44094                 re += "|";
44095             }
44096         }
44097         this.ddMatch = new RegExp(re + ")");
44098     }
44099 };
44100
44101 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
44102     /**
44103      * @cfg {String} format
44104      * The default date format string which can be overriden for localization support.  The format must be
44105      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
44106      */
44107     format : "m/d/y",
44108     /**
44109      * @cfg {String} altFormats
44110      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
44111      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
44112      */
44113     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
44114     /**
44115      * @cfg {Array} disabledDays
44116      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
44117      */
44118     disabledDays : null,
44119     /**
44120      * @cfg {String} disabledDaysText
44121      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
44122      */
44123     disabledDaysText : "Disabled",
44124     /**
44125      * @cfg {Array} disabledDates
44126      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
44127      * expression so they are very powerful. Some examples:
44128      * <ul>
44129      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
44130      * <li>["03/08", "09/16"] would disable those days for every year</li>
44131      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
44132      * <li>["03/../2006"] would disable every day in March 2006</li>
44133      * <li>["^03"] would disable every day in every March</li>
44134      * </ul>
44135      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
44136      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
44137      */
44138     disabledDates : null,
44139     /**
44140      * @cfg {String} disabledDatesText
44141      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
44142      */
44143     disabledDatesText : "Disabled",
44144         
44145         
44146         /**
44147      * @cfg {Date/String} zeroValue
44148      * if the date is less that this number, then the field is rendered as empty
44149      * default is 1800
44150      */
44151         zeroValue : '1800-01-01',
44152         
44153         
44154     /**
44155      * @cfg {Date/String} minValue
44156      * The minimum allowed date. Can be either a Javascript date object or a string date in a
44157      * valid format (defaults to null).
44158      */
44159     minValue : null,
44160     /**
44161      * @cfg {Date/String} maxValue
44162      * The maximum allowed date. Can be either a Javascript date object or a string date in a
44163      * valid format (defaults to null).
44164      */
44165     maxValue : null,
44166     /**
44167      * @cfg {String} minText
44168      * The error text to display when the date in the cell is before minValue (defaults to
44169      * 'The date in this field must be after {minValue}').
44170      */
44171     minText : "The date in this field must be equal to or after {0}",
44172     /**
44173      * @cfg {String} maxText
44174      * The error text to display when the date in the cell is after maxValue (defaults to
44175      * 'The date in this field must be before {maxValue}').
44176      */
44177     maxText : "The date in this field must be equal to or before {0}",
44178     /**
44179      * @cfg {String} invalidText
44180      * The error text to display when the date in the field is invalid (defaults to
44181      * '{value} is not a valid date - it must be in the format {format}').
44182      */
44183     invalidText : "{0} is not a valid date - it must be in the format {1}",
44184     /**
44185      * @cfg {String} triggerClass
44186      * An additional CSS class used to style the trigger button.  The trigger will always get the
44187      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
44188      * which displays a calendar icon).
44189      */
44190     triggerClass : 'x-form-date-trigger',
44191     
44192
44193     /**
44194      * @cfg {Boolean} useIso
44195      * if enabled, then the date field will use a hidden field to store the 
44196      * real value as iso formated date. default (false)
44197      */ 
44198     useIso : false,
44199     /**
44200      * @cfg {String/Object} autoCreate
44201      * A DomHelper element spec, or true for a default element spec (defaults to
44202      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
44203      */ 
44204     // private
44205     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
44206     
44207     // private
44208     hiddenField: false,
44209     
44210     onRender : function(ct, position)
44211     {
44212         Roo.form.DateField.superclass.onRender.call(this, ct, position);
44213         if (this.useIso) {
44214             //this.el.dom.removeAttribute('name'); 
44215             Roo.log("Changing name?");
44216             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
44217             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
44218                     'before', true);
44219             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
44220             // prevent input submission
44221             this.hiddenName = this.name;
44222         }
44223             
44224             
44225     },
44226     
44227     // private
44228     validateValue : function(value)
44229     {
44230         value = this.formatDate(value);
44231         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
44232             Roo.log('super failed');
44233             return false;
44234         }
44235         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
44236              return true;
44237         }
44238         var svalue = value;
44239         value = this.parseDate(value);
44240         if(!value){
44241             Roo.log('parse date failed' + svalue);
44242             this.markInvalid(String.format(this.invalidText, svalue, this.format));
44243             return false;
44244         }
44245         var time = value.getTime();
44246         if(this.minValue && time < this.minValue.getTime()){
44247             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
44248             return false;
44249         }
44250         if(this.maxValue && time > this.maxValue.getTime()){
44251             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
44252             return false;
44253         }
44254         if(this.disabledDays){
44255             var day = value.getDay();
44256             for(var i = 0; i < this.disabledDays.length; i++) {
44257                 if(day === this.disabledDays[i]){
44258                     this.markInvalid(this.disabledDaysText);
44259                     return false;
44260                 }
44261             }
44262         }
44263         var fvalue = this.formatDate(value);
44264         if(this.ddMatch && this.ddMatch.test(fvalue)){
44265             this.markInvalid(String.format(this.disabledDatesText, fvalue));
44266             return false;
44267         }
44268         return true;
44269     },
44270
44271     // private
44272     // Provides logic to override the default TriggerField.validateBlur which just returns true
44273     validateBlur : function(){
44274         return !this.menu || !this.menu.isVisible();
44275     },
44276     
44277     getName: function()
44278     {
44279         // returns hidden if it's set..
44280         if (!this.rendered) {return ''};
44281         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
44282         
44283     },
44284
44285     /**
44286      * Returns the current date value of the date field.
44287      * @return {Date} The date value
44288      */
44289     getValue : function(){
44290         
44291         return  this.hiddenField ?
44292                 this.hiddenField.value :
44293                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
44294     },
44295
44296     /**
44297      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
44298      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
44299      * (the default format used is "m/d/y").
44300      * <br />Usage:
44301      * <pre><code>
44302 //All of these calls set the same date value (May 4, 2006)
44303
44304 //Pass a date object:
44305 var dt = new Date('5/4/06');
44306 dateField.setValue(dt);
44307
44308 //Pass a date string (default format):
44309 dateField.setValue('5/4/06');
44310
44311 //Pass a date string (custom format):
44312 dateField.format = 'Y-m-d';
44313 dateField.setValue('2006-5-4');
44314 </code></pre>
44315      * @param {String/Date} date The date or valid date string
44316      */
44317     setValue : function(date){
44318         if (this.hiddenField) {
44319             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
44320         }
44321         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
44322         // make sure the value field is always stored as a date..
44323         this.value = this.parseDate(date);
44324         
44325         
44326     },
44327
44328     // private
44329     parseDate : function(value){
44330                 
44331                 if (value instanceof Date) {
44332                         if (value < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
44333                                 return  '';
44334                         }
44335                         return value;
44336                 }
44337                 
44338                 
44339         if(!value || value instanceof Date){
44340             return value;
44341         }
44342         var v = Date.parseDate(value, this.format);
44343          if (!v && this.useIso) {
44344             v = Date.parseDate(value, 'Y-m-d');
44345         }
44346         if(!v && this.altFormats){
44347             if(!this.altFormatsArray){
44348                 this.altFormatsArray = this.altFormats.split("|");
44349             }
44350             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
44351                 v = Date.parseDate(value, this.altFormatsArray[i]);
44352             }
44353         }
44354                 if (v < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
44355                         v = '';
44356                 }
44357         return v;
44358     },
44359
44360     // private
44361     formatDate : function(date, fmt){
44362         return (!date || !(date instanceof Date)) ?
44363                date : date.dateFormat(fmt || this.format);
44364     },
44365
44366     // private
44367     menuListeners : {
44368         select: function(m, d){
44369             
44370             this.setValue(d);
44371             this.fireEvent('select', this, d);
44372         },
44373         show : function(){ // retain focus styling
44374             this.onFocus();
44375         },
44376         hide : function(){
44377             this.focus.defer(10, this);
44378             var ml = this.menuListeners;
44379             this.menu.un("select", ml.select,  this);
44380             this.menu.un("show", ml.show,  this);
44381             this.menu.un("hide", ml.hide,  this);
44382         }
44383     },
44384
44385     // private
44386     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
44387     onTriggerClick : function(){
44388         if(this.disabled || this.readOnly){
44389             return;
44390         }
44391         if(this.menu == null){
44392             this.menu = new Roo.menu.DateMenu();
44393         }
44394         Roo.apply(this.menu.picker,  {
44395             showClear: this.allowBlank,
44396             minDate : this.minValue,
44397             maxDate : this.maxValue,
44398             disabledDatesRE : this.ddMatch,
44399             disabledDatesText : this.disabledDatesText,
44400             disabledDays : this.disabledDays,
44401             disabledDaysText : this.disabledDaysText,
44402             format : this.useIso ? 'Y-m-d' : this.format,
44403             minText : String.format(this.minText, this.formatDate(this.minValue)),
44404             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
44405         });
44406         this.menu.on(Roo.apply({}, this.menuListeners, {
44407             scope:this
44408         }));
44409         this.menu.picker.setValue(this.getValue() || new Date());
44410         this.menu.show(this.el, "tl-bl?");
44411     },
44412
44413     beforeBlur : function(){
44414         var v = this.parseDate(this.getRawValue());
44415         if(v){
44416             this.setValue(v);
44417         }
44418     },
44419
44420     /*@
44421      * overide
44422      * 
44423      */
44424     isDirty : function() {
44425         if(this.disabled) {
44426             return false;
44427         }
44428         
44429         if(typeof(this.startValue) === 'undefined'){
44430             return false;
44431         }
44432         
44433         return String(this.getValue()) !== String(this.startValue);
44434         
44435     },
44436     // @overide
44437     cleanLeadingSpace : function(e)
44438     {
44439        return;
44440     }
44441     
44442 });/*
44443  * Based on:
44444  * Ext JS Library 1.1.1
44445  * Copyright(c) 2006-2007, Ext JS, LLC.
44446  *
44447  * Originally Released Under LGPL - original licence link has changed is not relivant.
44448  *
44449  * Fork - LGPL
44450  * <script type="text/javascript">
44451  */
44452  
44453 /**
44454  * @class Roo.form.MonthField
44455  * @extends Roo.form.TriggerField
44456  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
44457 * @constructor
44458 * Create a new MonthField
44459 * @param {Object} config
44460  */
44461 Roo.form.MonthField = function(config){
44462     
44463     Roo.form.MonthField.superclass.constructor.call(this, config);
44464     
44465       this.addEvents({
44466          
44467         /**
44468          * @event select
44469          * Fires when a date is selected
44470              * @param {Roo.form.MonthFieeld} combo This combo box
44471              * @param {Date} date The date selected
44472              */
44473         'select' : true
44474          
44475     });
44476     
44477     
44478     if(typeof this.minValue == "string") {
44479         this.minValue = this.parseDate(this.minValue);
44480     }
44481     if(typeof this.maxValue == "string") {
44482         this.maxValue = this.parseDate(this.maxValue);
44483     }
44484     this.ddMatch = null;
44485     if(this.disabledDates){
44486         var dd = this.disabledDates;
44487         var re = "(?:";
44488         for(var i = 0; i < dd.length; i++){
44489             re += dd[i];
44490             if(i != dd.length-1) {
44491                 re += "|";
44492             }
44493         }
44494         this.ddMatch = new RegExp(re + ")");
44495     }
44496 };
44497
44498 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
44499     /**
44500      * @cfg {String} format
44501      * The default date format string which can be overriden for localization support.  The format must be
44502      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
44503      */
44504     format : "M Y",
44505     /**
44506      * @cfg {String} altFormats
44507      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
44508      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
44509      */
44510     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
44511     /**
44512      * @cfg {Array} disabledDays
44513      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
44514      */
44515     disabledDays : [0,1,2,3,4,5,6],
44516     /**
44517      * @cfg {String} disabledDaysText
44518      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
44519      */
44520     disabledDaysText : "Disabled",
44521     /**
44522      * @cfg {Array} disabledDates
44523      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
44524      * expression so they are very powerful. Some examples:
44525      * <ul>
44526      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
44527      * <li>["03/08", "09/16"] would disable those days for every year</li>
44528      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
44529      * <li>["03/../2006"] would disable every day in March 2006</li>
44530      * <li>["^03"] would disable every day in every March</li>
44531      * </ul>
44532      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
44533      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
44534      */
44535     disabledDates : null,
44536     /**
44537      * @cfg {String} disabledDatesText
44538      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
44539      */
44540     disabledDatesText : "Disabled",
44541     /**
44542      * @cfg {Date/String} minValue
44543      * The minimum allowed date. Can be either a Javascript date object or a string date in a
44544      * valid format (defaults to null).
44545      */
44546     minValue : null,
44547     /**
44548      * @cfg {Date/String} maxValue
44549      * The maximum allowed date. Can be either a Javascript date object or a string date in a
44550      * valid format (defaults to null).
44551      */
44552     maxValue : null,
44553     /**
44554      * @cfg {String} minText
44555      * The error text to display when the date in the cell is before minValue (defaults to
44556      * 'The date in this field must be after {minValue}').
44557      */
44558     minText : "The date in this field must be equal to or after {0}",
44559     /**
44560      * @cfg {String} maxTextf
44561      * The error text to display when the date in the cell is after maxValue (defaults to
44562      * 'The date in this field must be before {maxValue}').
44563      */
44564     maxText : "The date in this field must be equal to or before {0}",
44565     /**
44566      * @cfg {String} invalidText
44567      * The error text to display when the date in the field is invalid (defaults to
44568      * '{value} is not a valid date - it must be in the format {format}').
44569      */
44570     invalidText : "{0} is not a valid date - it must be in the format {1}",
44571     /**
44572      * @cfg {String} triggerClass
44573      * An additional CSS class used to style the trigger button.  The trigger will always get the
44574      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
44575      * which displays a calendar icon).
44576      */
44577     triggerClass : 'x-form-date-trigger',
44578     
44579
44580     /**
44581      * @cfg {Boolean} useIso
44582      * if enabled, then the date field will use a hidden field to store the 
44583      * real value as iso formated date. default (true)
44584      */ 
44585     useIso : true,
44586     /**
44587      * @cfg {String/Object} autoCreate
44588      * A DomHelper element spec, or true for a default element spec (defaults to
44589      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
44590      */ 
44591     // private
44592     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
44593     
44594     // private
44595     hiddenField: false,
44596     
44597     hideMonthPicker : false,
44598     
44599     onRender : function(ct, position)
44600     {
44601         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
44602         if (this.useIso) {
44603             this.el.dom.removeAttribute('name'); 
44604             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
44605                     'before', true);
44606             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
44607             // prevent input submission
44608             this.hiddenName = this.name;
44609         }
44610             
44611             
44612     },
44613     
44614     // private
44615     validateValue : function(value)
44616     {
44617         value = this.formatDate(value);
44618         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
44619             return false;
44620         }
44621         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
44622              return true;
44623         }
44624         var svalue = value;
44625         value = this.parseDate(value);
44626         if(!value){
44627             this.markInvalid(String.format(this.invalidText, svalue, this.format));
44628             return false;
44629         }
44630         var time = value.getTime();
44631         if(this.minValue && time < this.minValue.getTime()){
44632             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
44633             return false;
44634         }
44635         if(this.maxValue && time > this.maxValue.getTime()){
44636             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
44637             return false;
44638         }
44639         /*if(this.disabledDays){
44640             var day = value.getDay();
44641             for(var i = 0; i < this.disabledDays.length; i++) {
44642                 if(day === this.disabledDays[i]){
44643                     this.markInvalid(this.disabledDaysText);
44644                     return false;
44645                 }
44646             }
44647         }
44648         */
44649         var fvalue = this.formatDate(value);
44650         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
44651             this.markInvalid(String.format(this.disabledDatesText, fvalue));
44652             return false;
44653         }
44654         */
44655         return true;
44656     },
44657
44658     // private
44659     // Provides logic to override the default TriggerField.validateBlur which just returns true
44660     validateBlur : function(){
44661         return !this.menu || !this.menu.isVisible();
44662     },
44663
44664     /**
44665      * Returns the current date value of the date field.
44666      * @return {Date} The date value
44667      */
44668     getValue : function(){
44669         
44670         
44671         
44672         return  this.hiddenField ?
44673                 this.hiddenField.value :
44674                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
44675     },
44676
44677     /**
44678      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
44679      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
44680      * (the default format used is "m/d/y").
44681      * <br />Usage:
44682      * <pre><code>
44683 //All of these calls set the same date value (May 4, 2006)
44684
44685 //Pass a date object:
44686 var dt = new Date('5/4/06');
44687 monthField.setValue(dt);
44688
44689 //Pass a date string (default format):
44690 monthField.setValue('5/4/06');
44691
44692 //Pass a date string (custom format):
44693 monthField.format = 'Y-m-d';
44694 monthField.setValue('2006-5-4');
44695 </code></pre>
44696      * @param {String/Date} date The date or valid date string
44697      */
44698     setValue : function(date){
44699         Roo.log('month setValue' + date);
44700         // can only be first of month..
44701         
44702         var val = this.parseDate(date);
44703         
44704         if (this.hiddenField) {
44705             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
44706         }
44707         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
44708         this.value = this.parseDate(date);
44709     },
44710
44711     // private
44712     parseDate : function(value){
44713         if(!value || value instanceof Date){
44714             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
44715             return value;
44716         }
44717         var v = Date.parseDate(value, this.format);
44718         if (!v && this.useIso) {
44719             v = Date.parseDate(value, 'Y-m-d');
44720         }
44721         if (v) {
44722             // 
44723             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
44724         }
44725         
44726         
44727         if(!v && this.altFormats){
44728             if(!this.altFormatsArray){
44729                 this.altFormatsArray = this.altFormats.split("|");
44730             }
44731             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
44732                 v = Date.parseDate(value, this.altFormatsArray[i]);
44733             }
44734         }
44735         return v;
44736     },
44737
44738     // private
44739     formatDate : function(date, fmt){
44740         return (!date || !(date instanceof Date)) ?
44741                date : date.dateFormat(fmt || this.format);
44742     },
44743
44744     // private
44745     menuListeners : {
44746         select: function(m, d){
44747             this.setValue(d);
44748             this.fireEvent('select', this, d);
44749         },
44750         show : function(){ // retain focus styling
44751             this.onFocus();
44752         },
44753         hide : function(){
44754             this.focus.defer(10, this);
44755             var ml = this.menuListeners;
44756             this.menu.un("select", ml.select,  this);
44757             this.menu.un("show", ml.show,  this);
44758             this.menu.un("hide", ml.hide,  this);
44759         }
44760     },
44761     // private
44762     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
44763     onTriggerClick : function(){
44764         if(this.disabled){
44765             return;
44766         }
44767         if(this.menu == null){
44768             this.menu = new Roo.menu.DateMenu();
44769            
44770         }
44771         
44772         Roo.apply(this.menu.picker,  {
44773             
44774             showClear: this.allowBlank,
44775             minDate : this.minValue,
44776             maxDate : this.maxValue,
44777             disabledDatesRE : this.ddMatch,
44778             disabledDatesText : this.disabledDatesText,
44779             
44780             format : this.useIso ? 'Y-m-d' : this.format,
44781             minText : String.format(this.minText, this.formatDate(this.minValue)),
44782             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
44783             
44784         });
44785          this.menu.on(Roo.apply({}, this.menuListeners, {
44786             scope:this
44787         }));
44788        
44789         
44790         var m = this.menu;
44791         var p = m.picker;
44792         
44793         // hide month picker get's called when we called by 'before hide';
44794         
44795         var ignorehide = true;
44796         p.hideMonthPicker  = function(disableAnim){
44797             if (ignorehide) {
44798                 return;
44799             }
44800              if(this.monthPicker){
44801                 Roo.log("hideMonthPicker called");
44802                 if(disableAnim === true){
44803                     this.monthPicker.hide();
44804                 }else{
44805                     this.monthPicker.slideOut('t', {duration:.2});
44806                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
44807                     p.fireEvent("select", this, this.value);
44808                     m.hide();
44809                 }
44810             }
44811         }
44812         
44813         Roo.log('picker set value');
44814         Roo.log(this.getValue());
44815         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
44816         m.show(this.el, 'tl-bl?');
44817         ignorehide  = false;
44818         // this will trigger hideMonthPicker..
44819         
44820         
44821         // hidden the day picker
44822         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
44823         
44824         
44825         
44826       
44827         
44828         p.showMonthPicker.defer(100, p);
44829     
44830         
44831        
44832     },
44833
44834     beforeBlur : function(){
44835         var v = this.parseDate(this.getRawValue());
44836         if(v){
44837             this.setValue(v);
44838         }
44839     }
44840
44841     /** @cfg {Boolean} grow @hide */
44842     /** @cfg {Number} growMin @hide */
44843     /** @cfg {Number} growMax @hide */
44844     /**
44845      * @hide
44846      * @method autoSize
44847      */
44848 });/*
44849  * Based on:
44850  * Ext JS Library 1.1.1
44851  * Copyright(c) 2006-2007, Ext JS, LLC.
44852  *
44853  * Originally Released Under LGPL - original licence link has changed is not relivant.
44854  *
44855  * Fork - LGPL
44856  * <script type="text/javascript">
44857  */
44858  
44859
44860 /**
44861  * @class Roo.form.ComboBox
44862  * @extends Roo.form.TriggerField
44863  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
44864  * @constructor
44865  * Create a new ComboBox.
44866  * @param {Object} config Configuration options
44867  */
44868 Roo.form.ComboBox = function(config){
44869     Roo.form.ComboBox.superclass.constructor.call(this, config);
44870     this.addEvents({
44871         /**
44872          * @event expand
44873          * Fires when the dropdown list is expanded
44874              * @param {Roo.form.ComboBox} combo This combo box
44875              */
44876         'expand' : true,
44877         /**
44878          * @event collapse
44879          * Fires when the dropdown list is collapsed
44880              * @param {Roo.form.ComboBox} combo This combo box
44881              */
44882         'collapse' : true,
44883         /**
44884          * @event beforeselect
44885          * Fires before a list item is selected. Return false to cancel the selection.
44886              * @param {Roo.form.ComboBox} combo This combo box
44887              * @param {Roo.data.Record} record The data record returned from the underlying store
44888              * @param {Number} index The index of the selected item in the dropdown list
44889              */
44890         'beforeselect' : true,
44891         /**
44892          * @event select
44893          * Fires when a list item is selected
44894              * @param {Roo.form.ComboBox} combo This combo box
44895              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
44896              * @param {Number} index The index of the selected item in the dropdown list
44897              */
44898         'select' : true,
44899         /**
44900          * @event beforequery
44901          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
44902          * The event object passed has these properties:
44903              * @param {Roo.form.ComboBox} combo This combo box
44904              * @param {String} query The query
44905              * @param {Boolean} forceAll true to force "all" query
44906              * @param {Boolean} cancel true to cancel the query
44907              * @param {Object} e The query event object
44908              */
44909         'beforequery': true,
44910          /**
44911          * @event add
44912          * Fires when the 'add' icon is pressed (add a listener to enable add button)
44913              * @param {Roo.form.ComboBox} combo This combo box
44914              */
44915         'add' : true,
44916         /**
44917          * @event edit
44918          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
44919              * @param {Roo.form.ComboBox} combo This combo box
44920              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
44921              */
44922         'edit' : true
44923         
44924         
44925     });
44926     if(this.transform){
44927         this.allowDomMove = false;
44928         var s = Roo.getDom(this.transform);
44929         if(!this.hiddenName){
44930             this.hiddenName = s.name;
44931         }
44932         if(!this.store){
44933             this.mode = 'local';
44934             var d = [], opts = s.options;
44935             for(var i = 0, len = opts.length;i < len; i++){
44936                 var o = opts[i];
44937                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
44938                 if(o.selected) {
44939                     this.value = value;
44940                 }
44941                 d.push([value, o.text]);
44942             }
44943             this.store = new Roo.data.SimpleStore({
44944                 'id': 0,
44945                 fields: ['value', 'text'],
44946                 data : d
44947             });
44948             this.valueField = 'value';
44949             this.displayField = 'text';
44950         }
44951         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
44952         if(!this.lazyRender){
44953             this.target = true;
44954             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
44955             s.parentNode.removeChild(s); // remove it
44956             this.render(this.el.parentNode);
44957         }else{
44958             s.parentNode.removeChild(s); // remove it
44959         }
44960
44961     }
44962     if (this.store) {
44963         this.store = Roo.factory(this.store, Roo.data);
44964     }
44965     
44966     this.selectedIndex = -1;
44967     if(this.mode == 'local'){
44968         if(config.queryDelay === undefined){
44969             this.queryDelay = 10;
44970         }
44971         if(config.minChars === undefined){
44972             this.minChars = 0;
44973         }
44974     }
44975 };
44976
44977 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
44978     /**
44979      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
44980      */
44981     /**
44982      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
44983      * rendering into an Roo.Editor, defaults to false)
44984      */
44985     /**
44986      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
44987      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
44988      */
44989     /**
44990      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
44991      */
44992     /**
44993      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
44994      * the dropdown list (defaults to undefined, with no header element)
44995      */
44996
44997      /**
44998      * @cfg {String/Roo.Template} tpl The template to use to render the output
44999      */
45000      
45001     // private
45002     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
45003     /**
45004      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
45005      */
45006     listWidth: undefined,
45007     /**
45008      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
45009      * mode = 'remote' or 'text' if mode = 'local')
45010      */
45011     displayField: undefined,
45012     /**
45013      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
45014      * mode = 'remote' or 'value' if mode = 'local'). 
45015      * Note: use of a valueField requires the user make a selection
45016      * in order for a value to be mapped.
45017      */
45018     valueField: undefined,
45019     
45020     
45021     /**
45022      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
45023      * field's data value (defaults to the underlying DOM element's name)
45024      */
45025     hiddenName: undefined,
45026     /**
45027      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
45028      */
45029     listClass: '',
45030     /**
45031      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
45032      */
45033     selectedClass: 'x-combo-selected',
45034     /**
45035      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
45036      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
45037      * which displays a downward arrow icon).
45038      */
45039     triggerClass : 'x-form-arrow-trigger',
45040     /**
45041      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
45042      */
45043     shadow:'sides',
45044     /**
45045      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
45046      * anchor positions (defaults to 'tl-bl')
45047      */
45048     listAlign: 'tl-bl?',
45049     /**
45050      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
45051      */
45052     maxHeight: 300,
45053     /**
45054      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
45055      * query specified by the allQuery config option (defaults to 'query')
45056      */
45057     triggerAction: 'query',
45058     /**
45059      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
45060      * (defaults to 4, does not apply if editable = false)
45061      */
45062     minChars : 4,
45063     /**
45064      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
45065      * delay (typeAheadDelay) if it matches a known value (defaults to false)
45066      */
45067     typeAhead: false,
45068     /**
45069      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
45070      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
45071      */
45072     queryDelay: 500,
45073     /**
45074      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
45075      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
45076      */
45077     pageSize: 0,
45078     /**
45079      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
45080      * when editable = true (defaults to false)
45081      */
45082     selectOnFocus:false,
45083     /**
45084      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
45085      */
45086     queryParam: 'query',
45087     /**
45088      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
45089      * when mode = 'remote' (defaults to 'Loading...')
45090      */
45091     loadingText: 'Loading...',
45092     /**
45093      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
45094      */
45095     resizable: false,
45096     /**
45097      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
45098      */
45099     handleHeight : 8,
45100     /**
45101      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
45102      * traditional select (defaults to true)
45103      */
45104     editable: true,
45105     /**
45106      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
45107      */
45108     allQuery: '',
45109     /**
45110      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
45111      */
45112     mode: 'remote',
45113     /**
45114      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
45115      * listWidth has a higher value)
45116      */
45117     minListWidth : 70,
45118     /**
45119      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
45120      * allow the user to set arbitrary text into the field (defaults to false)
45121      */
45122     forceSelection:false,
45123     /**
45124      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
45125      * if typeAhead = true (defaults to 250)
45126      */
45127     typeAheadDelay : 250,
45128     /**
45129      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
45130      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
45131      */
45132     valueNotFoundText : undefined,
45133     /**
45134      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
45135      */
45136     blockFocus : false,
45137     
45138     /**
45139      * @cfg {Boolean} disableClear Disable showing of clear button.
45140      */
45141     disableClear : false,
45142     /**
45143      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
45144      */
45145     alwaysQuery : false,
45146     
45147     //private
45148     addicon : false,
45149     editicon: false,
45150     
45151     // element that contains real text value.. (when hidden is used..)
45152      
45153     // private
45154     onRender : function(ct, position)
45155     {
45156         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
45157         
45158                 if(this.hiddenName){
45159             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
45160                     'before', true);
45161             this.hiddenField.value =
45162                 this.hiddenValue !== undefined ? this.hiddenValue :
45163                 this.value !== undefined ? this.value : '';
45164
45165             // prevent input submission
45166             this.el.dom.removeAttribute('name');
45167              
45168              
45169         }
45170         
45171         if(Roo.isGecko){
45172             this.el.dom.setAttribute('autocomplete', 'off');
45173         }
45174
45175         var cls = 'x-combo-list';
45176
45177         this.list = new Roo.Layer({
45178             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
45179         });
45180
45181         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
45182         this.list.setWidth(lw);
45183         this.list.swallowEvent('mousewheel');
45184         this.assetHeight = 0;
45185
45186         if(this.title){
45187             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
45188             this.assetHeight += this.header.getHeight();
45189         }
45190
45191         this.innerList = this.list.createChild({cls:cls+'-inner'});
45192         this.innerList.on('mouseover', this.onViewOver, this);
45193         this.innerList.on('mousemove', this.onViewMove, this);
45194         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
45195         
45196         if(this.allowBlank && !this.pageSize && !this.disableClear){
45197             this.footer = this.list.createChild({cls:cls+'-ft'});
45198             this.pageTb = new Roo.Toolbar(this.footer);
45199            
45200         }
45201         if(this.pageSize){
45202             this.footer = this.list.createChild({cls:cls+'-ft'});
45203             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
45204                     {pageSize: this.pageSize});
45205             
45206         }
45207         
45208         if (this.pageTb && this.allowBlank && !this.disableClear) {
45209             var _this = this;
45210             this.pageTb.add(new Roo.Toolbar.Fill(), {
45211                 cls: 'x-btn-icon x-btn-clear',
45212                 text: '&#160;',
45213                 handler: function()
45214                 {
45215                     _this.collapse();
45216                     _this.clearValue();
45217                     _this.onSelect(false, -1);
45218                 }
45219             });
45220         }
45221         if (this.footer) {
45222             this.assetHeight += this.footer.getHeight();
45223         }
45224         
45225
45226         if(!this.tpl){
45227             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
45228         }
45229
45230         this.view = new Roo.View(this.innerList, this.tpl, {
45231             singleSelect:true,
45232             store: this.store,
45233             selectedClass: this.selectedClass
45234         });
45235
45236         this.view.on('click', this.onViewClick, this);
45237
45238         this.store.on('beforeload', this.onBeforeLoad, this);
45239         this.store.on('load', this.onLoad, this);
45240         this.store.on('loadexception', this.onLoadException, this);
45241
45242         if(this.resizable){
45243             this.resizer = new Roo.Resizable(this.list,  {
45244                pinned:true, handles:'se'
45245             });
45246             this.resizer.on('resize', function(r, w, h){
45247                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
45248                 this.listWidth = w;
45249                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
45250                 this.restrictHeight();
45251             }, this);
45252             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
45253         }
45254         if(!this.editable){
45255             this.editable = true;
45256             this.setEditable(false);
45257         }  
45258         
45259         
45260         if (typeof(this.events.add.listeners) != 'undefined') {
45261             
45262             this.addicon = this.wrap.createChild(
45263                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
45264        
45265             this.addicon.on('click', function(e) {
45266                 this.fireEvent('add', this);
45267             }, this);
45268         }
45269         if (typeof(this.events.edit.listeners) != 'undefined') {
45270             
45271             this.editicon = this.wrap.createChild(
45272                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
45273             if (this.addicon) {
45274                 this.editicon.setStyle('margin-left', '40px');
45275             }
45276             this.editicon.on('click', function(e) {
45277                 
45278                 // we fire even  if inothing is selected..
45279                 this.fireEvent('edit', this, this.lastData );
45280                 
45281             }, this);
45282         }
45283         
45284         
45285         
45286     },
45287
45288     // private
45289     initEvents : function(){
45290         Roo.form.ComboBox.superclass.initEvents.call(this);
45291
45292         this.keyNav = new Roo.KeyNav(this.el, {
45293             "up" : function(e){
45294                 this.inKeyMode = true;
45295                 this.selectPrev();
45296             },
45297
45298             "down" : function(e){
45299                 if(!this.isExpanded()){
45300                     this.onTriggerClick();
45301                 }else{
45302                     this.inKeyMode = true;
45303                     this.selectNext();
45304                 }
45305             },
45306
45307             "enter" : function(e){
45308                 this.onViewClick();
45309                 //return true;
45310             },
45311
45312             "esc" : function(e){
45313                 this.collapse();
45314             },
45315
45316             "tab" : function(e){
45317                 this.onViewClick(false);
45318                 this.fireEvent("specialkey", this, e);
45319                 return true;
45320             },
45321
45322             scope : this,
45323
45324             doRelay : function(foo, bar, hname){
45325                 if(hname == 'down' || this.scope.isExpanded()){
45326                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
45327                 }
45328                 return true;
45329             },
45330
45331             forceKeyDown: true
45332         });
45333         this.queryDelay = Math.max(this.queryDelay || 10,
45334                 this.mode == 'local' ? 10 : 250);
45335         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
45336         if(this.typeAhead){
45337             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
45338         }
45339         if(this.editable !== false){
45340             this.el.on("keyup", this.onKeyUp, this);
45341         }
45342         if(this.forceSelection){
45343             this.on('blur', this.doForce, this);
45344         }
45345     },
45346
45347     onDestroy : function(){
45348         if(this.view){
45349             this.view.setStore(null);
45350             this.view.el.removeAllListeners();
45351             this.view.el.remove();
45352             this.view.purgeListeners();
45353         }
45354         if(this.list){
45355             this.list.destroy();
45356         }
45357         if(this.store){
45358             this.store.un('beforeload', this.onBeforeLoad, this);
45359             this.store.un('load', this.onLoad, this);
45360             this.store.un('loadexception', this.onLoadException, this);
45361         }
45362         Roo.form.ComboBox.superclass.onDestroy.call(this);
45363     },
45364
45365     // private
45366     fireKey : function(e){
45367         if(e.isNavKeyPress() && !this.list.isVisible()){
45368             this.fireEvent("specialkey", this, e);
45369         }
45370     },
45371
45372     // private
45373     onResize: function(w, h){
45374         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
45375         
45376         if(typeof w != 'number'){
45377             // we do not handle it!?!?
45378             return;
45379         }
45380         var tw = this.trigger.getWidth();
45381         tw += this.addicon ? this.addicon.getWidth() : 0;
45382         tw += this.editicon ? this.editicon.getWidth() : 0;
45383         var x = w - tw;
45384         this.el.setWidth( this.adjustWidth('input', x));
45385             
45386         this.trigger.setStyle('left', x+'px');
45387         
45388         if(this.list && this.listWidth === undefined){
45389             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
45390             this.list.setWidth(lw);
45391             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
45392         }
45393         
45394     
45395         
45396     },
45397
45398     /**
45399      * Allow or prevent the user from directly editing the field text.  If false is passed,
45400      * the user will only be able to select from the items defined in the dropdown list.  This method
45401      * is the runtime equivalent of setting the 'editable' config option at config time.
45402      * @param {Boolean} value True to allow the user to directly edit the field text
45403      */
45404     setEditable : function(value){
45405         if(value == this.editable){
45406             return;
45407         }
45408         this.editable = value;
45409         if(!value){
45410             this.el.dom.setAttribute('readOnly', true);
45411             this.el.on('mousedown', this.onTriggerClick,  this);
45412             this.el.addClass('x-combo-noedit');
45413         }else{
45414             this.el.dom.setAttribute('readOnly', false);
45415             this.el.un('mousedown', this.onTriggerClick,  this);
45416             this.el.removeClass('x-combo-noedit');
45417         }
45418     },
45419
45420     // private
45421     onBeforeLoad : function(){
45422         if(!this.hasFocus){
45423             return;
45424         }
45425         this.innerList.update(this.loadingText ?
45426                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
45427         this.restrictHeight();
45428         this.selectedIndex = -1;
45429     },
45430
45431     // private
45432     onLoad : function(){
45433         if(!this.hasFocus){
45434             return;
45435         }
45436         if(this.store.getCount() > 0){
45437             this.expand();
45438             this.restrictHeight();
45439             if(this.lastQuery == this.allQuery){
45440                 if(this.editable){
45441                     this.el.dom.select();
45442                 }
45443                 if(!this.selectByValue(this.value, true)){
45444                     this.select(0, true);
45445                 }
45446             }else{
45447                 this.selectNext();
45448                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
45449                     this.taTask.delay(this.typeAheadDelay);
45450                 }
45451             }
45452         }else{
45453             this.onEmptyResults();
45454         }
45455         //this.el.focus();
45456     },
45457     // private
45458     onLoadException : function()
45459     {
45460         this.collapse();
45461         Roo.log(this.store.reader.jsonData);
45462         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
45463             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
45464         }
45465         
45466         
45467     },
45468     // private
45469     onTypeAhead : function(){
45470         if(this.store.getCount() > 0){
45471             var r = this.store.getAt(0);
45472             var newValue = r.data[this.displayField];
45473             var len = newValue.length;
45474             var selStart = this.getRawValue().length;
45475             if(selStart != len){
45476                 this.setRawValue(newValue);
45477                 this.selectText(selStart, newValue.length);
45478             }
45479         }
45480     },
45481
45482     // private
45483     onSelect : function(record, index){
45484         if(this.fireEvent('beforeselect', this, record, index) !== false){
45485             this.setFromData(index > -1 ? record.data : false);
45486             this.collapse();
45487             this.fireEvent('select', this, record, index);
45488         }
45489     },
45490
45491     /**
45492      * Returns the currently selected field value or empty string if no value is set.
45493      * @return {String} value The selected value
45494      */
45495     getValue : function(){
45496         if(this.valueField){
45497             return typeof this.value != 'undefined' ? this.value : '';
45498         }
45499         return Roo.form.ComboBox.superclass.getValue.call(this);
45500     },
45501
45502     /**
45503      * Clears any text/value currently set in the field
45504      */
45505     clearValue : function(){
45506         if(this.hiddenField){
45507             this.hiddenField.value = '';
45508         }
45509         this.value = '';
45510         this.setRawValue('');
45511         this.lastSelectionText = '';
45512         
45513     },
45514
45515     /**
45516      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
45517      * will be displayed in the field.  If the value does not match the data value of an existing item,
45518      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
45519      * Otherwise the field will be blank (although the value will still be set).
45520      * @param {String} value The value to match
45521      */
45522     setValue : function(v){
45523         var text = v;
45524         if(this.valueField){
45525             var r = this.findRecord(this.valueField, v);
45526             if(r){
45527                 text = r.data[this.displayField];
45528             }else if(this.valueNotFoundText !== undefined){
45529                 text = this.valueNotFoundText;
45530             }
45531         }
45532         this.lastSelectionText = text;
45533         if(this.hiddenField){
45534             this.hiddenField.value = v;
45535         }
45536         Roo.form.ComboBox.superclass.setValue.call(this, text);
45537         this.value = v;
45538     },
45539     /**
45540      * @property {Object} the last set data for the element
45541      */
45542     
45543     lastData : false,
45544     /**
45545      * Sets the value of the field based on a object which is related to the record format for the store.
45546      * @param {Object} value the value to set as. or false on reset?
45547      */
45548     setFromData : function(o){
45549         var dv = ''; // display value
45550         var vv = ''; // value value..
45551         this.lastData = o;
45552         if (this.displayField) {
45553             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
45554         } else {
45555             // this is an error condition!!!
45556             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
45557         }
45558         
45559         if(this.valueField){
45560             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
45561         }
45562         if(this.hiddenField){
45563             this.hiddenField.value = vv;
45564             
45565             this.lastSelectionText = dv;
45566             Roo.form.ComboBox.superclass.setValue.call(this, dv);
45567             this.value = vv;
45568             return;
45569         }
45570         // no hidden field.. - we store the value in 'value', but still display
45571         // display field!!!!
45572         this.lastSelectionText = dv;
45573         Roo.form.ComboBox.superclass.setValue.call(this, dv);
45574         this.value = vv;
45575         
45576         
45577     },
45578     // private
45579     reset : function(){
45580         // overridden so that last data is reset..
45581         this.setValue(this.resetValue);
45582         this.originalValue = this.getValue();
45583         this.clearInvalid();
45584         this.lastData = false;
45585         if (this.view) {
45586             this.view.clearSelections();
45587         }
45588     },
45589     // private
45590     findRecord : function(prop, value){
45591         var record;
45592         if(this.store.getCount() > 0){
45593             this.store.each(function(r){
45594                 if(r.data[prop] == value){
45595                     record = r;
45596                     return false;
45597                 }
45598                 return true;
45599             });
45600         }
45601         return record;
45602     },
45603     
45604     getName: function()
45605     {
45606         // returns hidden if it's set..
45607         if (!this.rendered) {return ''};
45608         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
45609         
45610     },
45611     // private
45612     onViewMove : function(e, t){
45613         this.inKeyMode = false;
45614     },
45615
45616     // private
45617     onViewOver : function(e, t){
45618         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
45619             return;
45620         }
45621         var item = this.view.findItemFromChild(t);
45622         if(item){
45623             var index = this.view.indexOf(item);
45624             this.select(index, false);
45625         }
45626     },
45627
45628     // private
45629     onViewClick : function(doFocus)
45630     {
45631         var index = this.view.getSelectedIndexes()[0];
45632         var r = this.store.getAt(index);
45633         if(r){
45634             this.onSelect(r, index);
45635         }
45636         if(doFocus !== false && !this.blockFocus){
45637             this.el.focus();
45638         }
45639     },
45640
45641     // private
45642     restrictHeight : function(){
45643         this.innerList.dom.style.height = '';
45644         var inner = this.innerList.dom;
45645         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
45646         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
45647         this.list.beginUpdate();
45648         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
45649         this.list.alignTo(this.el, this.listAlign);
45650         this.list.endUpdate();
45651     },
45652
45653     // private
45654     onEmptyResults : function(){
45655         this.collapse();
45656     },
45657
45658     /**
45659      * Returns true if the dropdown list is expanded, else false.
45660      */
45661     isExpanded : function(){
45662         return this.list.isVisible();
45663     },
45664
45665     /**
45666      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
45667      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
45668      * @param {String} value The data value of the item to select
45669      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
45670      * selected item if it is not currently in view (defaults to true)
45671      * @return {Boolean} True if the value matched an item in the list, else false
45672      */
45673     selectByValue : function(v, scrollIntoView){
45674         if(v !== undefined && v !== null){
45675             var r = this.findRecord(this.valueField || this.displayField, v);
45676             if(r){
45677                 this.select(this.store.indexOf(r), scrollIntoView);
45678                 return true;
45679             }
45680         }
45681         return false;
45682     },
45683
45684     /**
45685      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
45686      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
45687      * @param {Number} index The zero-based index of the list item to select
45688      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
45689      * selected item if it is not currently in view (defaults to true)
45690      */
45691     select : function(index, scrollIntoView){
45692         this.selectedIndex = index;
45693         this.view.select(index);
45694         if(scrollIntoView !== false){
45695             var el = this.view.getNode(index);
45696             if(el){
45697                 this.innerList.scrollChildIntoView(el, false);
45698             }
45699         }
45700     },
45701
45702     // private
45703     selectNext : function(){
45704         var ct = this.store.getCount();
45705         if(ct > 0){
45706             if(this.selectedIndex == -1){
45707                 this.select(0);
45708             }else if(this.selectedIndex < ct-1){
45709                 this.select(this.selectedIndex+1);
45710             }
45711         }
45712     },
45713
45714     // private
45715     selectPrev : function(){
45716         var ct = this.store.getCount();
45717         if(ct > 0){
45718             if(this.selectedIndex == -1){
45719                 this.select(0);
45720             }else if(this.selectedIndex != 0){
45721                 this.select(this.selectedIndex-1);
45722             }
45723         }
45724     },
45725
45726     // private
45727     onKeyUp : function(e){
45728         if(this.editable !== false && !e.isSpecialKey()){
45729             this.lastKey = e.getKey();
45730             this.dqTask.delay(this.queryDelay);
45731         }
45732     },
45733
45734     // private
45735     validateBlur : function(){
45736         return !this.list || !this.list.isVisible();   
45737     },
45738
45739     // private
45740     initQuery : function(){
45741         this.doQuery(this.getRawValue());
45742     },
45743
45744     // private
45745     doForce : function(){
45746         if(this.el.dom.value.length > 0){
45747             this.el.dom.value =
45748                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
45749              
45750         }
45751     },
45752
45753     /**
45754      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
45755      * query allowing the query action to be canceled if needed.
45756      * @param {String} query The SQL query to execute
45757      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
45758      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
45759      * saved in the current store (defaults to false)
45760      */
45761     doQuery : function(q, forceAll){
45762         if(q === undefined || q === null){
45763             q = '';
45764         }
45765         var qe = {
45766             query: q,
45767             forceAll: forceAll,
45768             combo: this,
45769             cancel:false
45770         };
45771         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
45772             return false;
45773         }
45774         q = qe.query;
45775         forceAll = qe.forceAll;
45776         if(forceAll === true || (q.length >= this.minChars)){
45777             if(this.lastQuery != q || this.alwaysQuery){
45778                 this.lastQuery = q;
45779                 if(this.mode == 'local'){
45780                     this.selectedIndex = -1;
45781                     if(forceAll){
45782                         this.store.clearFilter();
45783                     }else{
45784                         this.store.filter(this.displayField, q);
45785                     }
45786                     this.onLoad();
45787                 }else{
45788                     this.store.baseParams[this.queryParam] = q;
45789                     this.store.load({
45790                         params: this.getParams(q)
45791                     });
45792                     this.expand();
45793                 }
45794             }else{
45795                 this.selectedIndex = -1;
45796                 this.onLoad();   
45797             }
45798         }
45799     },
45800
45801     // private
45802     getParams : function(q){
45803         var p = {};
45804         //p[this.queryParam] = q;
45805         if(this.pageSize){
45806             p.start = 0;
45807             p.limit = this.pageSize;
45808         }
45809         return p;
45810     },
45811
45812     /**
45813      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
45814      */
45815     collapse : function(){
45816         if(!this.isExpanded()){
45817             return;
45818         }
45819         this.list.hide();
45820         Roo.get(document).un('mousedown', this.collapseIf, this);
45821         Roo.get(document).un('mousewheel', this.collapseIf, this);
45822         if (!this.editable) {
45823             Roo.get(document).un('keydown', this.listKeyPress, this);
45824         }
45825         this.fireEvent('collapse', this);
45826     },
45827
45828     // private
45829     collapseIf : function(e){
45830         if(!e.within(this.wrap) && !e.within(this.list)){
45831             this.collapse();
45832         }
45833     },
45834
45835     /**
45836      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
45837      */
45838     expand : function(){
45839         if(this.isExpanded() || !this.hasFocus){
45840             return;
45841         }
45842         this.list.alignTo(this.el, this.listAlign);
45843         this.list.show();
45844         Roo.get(document).on('mousedown', this.collapseIf, this);
45845         Roo.get(document).on('mousewheel', this.collapseIf, this);
45846         if (!this.editable) {
45847             Roo.get(document).on('keydown', this.listKeyPress, this);
45848         }
45849         
45850         this.fireEvent('expand', this);
45851     },
45852
45853     // private
45854     // Implements the default empty TriggerField.onTriggerClick function
45855     onTriggerClick : function(){
45856         if(this.disabled){
45857             return;
45858         }
45859         if(this.isExpanded()){
45860             this.collapse();
45861             if (!this.blockFocus) {
45862                 this.el.focus();
45863             }
45864             
45865         }else {
45866             this.hasFocus = true;
45867             if(this.triggerAction == 'all') {
45868                 this.doQuery(this.allQuery, true);
45869             } else {
45870                 this.doQuery(this.getRawValue());
45871             }
45872             if (!this.blockFocus) {
45873                 this.el.focus();
45874             }
45875         }
45876     },
45877     listKeyPress : function(e)
45878     {
45879         //Roo.log('listkeypress');
45880         // scroll to first matching element based on key pres..
45881         if (e.isSpecialKey()) {
45882             return false;
45883         }
45884         var k = String.fromCharCode(e.getKey()).toUpperCase();
45885         //Roo.log(k);
45886         var match  = false;
45887         var csel = this.view.getSelectedNodes();
45888         var cselitem = false;
45889         if (csel.length) {
45890             var ix = this.view.indexOf(csel[0]);
45891             cselitem  = this.store.getAt(ix);
45892             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
45893                 cselitem = false;
45894             }
45895             
45896         }
45897         
45898         this.store.each(function(v) { 
45899             if (cselitem) {
45900                 // start at existing selection.
45901                 if (cselitem.id == v.id) {
45902                     cselitem = false;
45903                 }
45904                 return;
45905             }
45906                 
45907             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
45908                 match = this.store.indexOf(v);
45909                 return false;
45910             }
45911         }, this);
45912         
45913         if (match === false) {
45914             return true; // no more action?
45915         }
45916         // scroll to?
45917         this.view.select(match);
45918         var sn = Roo.get(this.view.getSelectedNodes()[0]);
45919         sn.scrollIntoView(sn.dom.parentNode, false);
45920     },
45921         cleanLeadingSpace : function()
45922         {
45923                 // override textfield strip white space (trigers set on blur)
45924         }
45925
45926     /** 
45927     * @cfg {Boolean} grow 
45928     * @hide 
45929     */
45930     /** 
45931     * @cfg {Number} growMin 
45932     * @hide 
45933     */
45934     /** 
45935     * @cfg {Number} growMax 
45936     * @hide 
45937     */
45938     /**
45939      * @hide
45940      * @method autoSize
45941      */
45942 });/*
45943  * Copyright(c) 2010-2012, Roo J Solutions Limited
45944  *
45945  * Licence LGPL
45946  *
45947  */
45948
45949 /**
45950  * @class Roo.form.ComboBoxArray
45951  * @extends Roo.form.TextField
45952  * A facebook style adder... for lists of email / people / countries  etc...
45953  * pick multiple items from a combo box, and shows each one.
45954  *
45955  *  Fred [x]  Brian [x]  [Pick another |v]
45956  *
45957  *
45958  *  For this to work: it needs various extra information
45959  *    - normal combo problay has
45960  *      name, hiddenName
45961  *    + displayField, valueField
45962  *
45963  *    For our purpose...
45964  *
45965  *
45966  *   If we change from 'extends' to wrapping...
45967  *   
45968  *  
45969  *
45970  
45971  
45972  * @constructor
45973  * Create a new ComboBoxArray.
45974  * @param {Object} config Configuration options
45975  */
45976  
45977
45978 Roo.form.ComboBoxArray = function(config)
45979 {
45980     this.addEvents({
45981         /**
45982          * @event beforeremove
45983          * Fires before remove the value from the list
45984              * @param {Roo.form.ComboBoxArray} _self This combo box array
45985              * @param {Roo.form.ComboBoxArray.Item} item removed item
45986              */
45987         'beforeremove' : true,
45988         /**
45989          * @event remove
45990          * Fires when remove the value from the list
45991              * @param {Roo.form.ComboBoxArray} _self This combo box array
45992              * @param {Roo.form.ComboBoxArray.Item} item removed item
45993              */
45994         'remove' : true
45995         
45996         
45997     });
45998     
45999     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
46000     
46001     this.items = new Roo.util.MixedCollection(false);
46002     
46003     // construct the child combo...
46004     
46005     
46006     
46007     
46008    
46009     
46010 }
46011
46012  
46013 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
46014
46015     /**
46016      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
46017      */
46018     
46019     lastData : false,
46020     
46021     // behavies liek a hiddne field
46022     inputType:      'hidden',
46023     /**
46024      * @cfg {Number} width The width of the box that displays the selected element
46025      */ 
46026     width:          300,
46027
46028     
46029     
46030     /**
46031      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
46032      */
46033     name : false,
46034     /**
46035      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
46036      */
46037     hiddenName : false,
46038       /**
46039      * @cfg {String} seperator    The value seperator normally ',' 
46040      */
46041     seperator : ',',
46042     
46043     
46044         // private the array of items that are displayed..
46045     items  : false,
46046     // private - the hidden field el.
46047     hiddenEl : false,
46048     // private - the filed el..
46049     el : false,
46050     
46051     //validateValue : function() { return true; }, // all values are ok!
46052     //onAddClick: function() { },
46053     
46054     onRender : function(ct, position) 
46055     {
46056         
46057         // create the standard hidden element
46058         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
46059         
46060         
46061         // give fake names to child combo;
46062         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
46063         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
46064         
46065         this.combo = Roo.factory(this.combo, Roo.form);
46066         this.combo.onRender(ct, position);
46067         if (typeof(this.combo.width) != 'undefined') {
46068             this.combo.onResize(this.combo.width,0);
46069         }
46070         
46071         this.combo.initEvents();
46072         
46073         // assigned so form know we need to do this..
46074         this.store          = this.combo.store;
46075         this.valueField     = this.combo.valueField;
46076         this.displayField   = this.combo.displayField ;
46077         
46078         
46079         this.combo.wrap.addClass('x-cbarray-grp');
46080         
46081         var cbwrap = this.combo.wrap.createChild(
46082             {tag: 'div', cls: 'x-cbarray-cb'},
46083             this.combo.el.dom
46084         );
46085         
46086              
46087         this.hiddenEl = this.combo.wrap.createChild({
46088             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
46089         });
46090         this.el = this.combo.wrap.createChild({
46091             tag: 'input',  type:'hidden' , name: this.name, value : ''
46092         });
46093          //   this.el.dom.removeAttribute("name");
46094         
46095         
46096         this.outerWrap = this.combo.wrap;
46097         this.wrap = cbwrap;
46098         
46099         this.outerWrap.setWidth(this.width);
46100         this.outerWrap.dom.removeChild(this.el.dom);
46101         
46102         this.wrap.dom.appendChild(this.el.dom);
46103         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
46104         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
46105         
46106         this.combo.trigger.setStyle('position','relative');
46107         this.combo.trigger.setStyle('left', '0px');
46108         this.combo.trigger.setStyle('top', '2px');
46109         
46110         this.combo.el.setStyle('vertical-align', 'text-bottom');
46111         
46112         //this.trigger.setStyle('vertical-align', 'top');
46113         
46114         // this should use the code from combo really... on('add' ....)
46115         if (this.adder) {
46116             
46117         
46118             this.adder = this.outerWrap.createChild(
46119                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
46120             var _t = this;
46121             this.adder.on('click', function(e) {
46122                 _t.fireEvent('adderclick', this, e);
46123             }, _t);
46124         }
46125         //var _t = this;
46126         //this.adder.on('click', this.onAddClick, _t);
46127         
46128         
46129         this.combo.on('select', function(cb, rec, ix) {
46130             this.addItem(rec.data);
46131             
46132             cb.setValue('');
46133             cb.el.dom.value = '';
46134             //cb.lastData = rec.data;
46135             // add to list
46136             
46137         }, this);
46138          
46139         
46140         
46141             
46142     },
46143     
46144     
46145     getName: function()
46146     {
46147         // returns hidden if it's set..
46148         if (!this.rendered) {return ''};
46149         return  this.hiddenName ? this.hiddenName : this.name;
46150         
46151     },
46152     
46153     
46154     onResize: function(w, h){
46155         
46156         return;
46157         // not sure if this is needed..
46158         //this.combo.onResize(w,h);
46159         
46160         if(typeof w != 'number'){
46161             // we do not handle it!?!?
46162             return;
46163         }
46164         var tw = this.combo.trigger.getWidth();
46165         tw += this.addicon ? this.addicon.getWidth() : 0;
46166         tw += this.editicon ? this.editicon.getWidth() : 0;
46167         var x = w - tw;
46168         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
46169             
46170         this.combo.trigger.setStyle('left', '0px');
46171         
46172         if(this.list && this.listWidth === undefined){
46173             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
46174             this.list.setWidth(lw);
46175             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
46176         }
46177         
46178     
46179         
46180     },
46181     
46182     addItem: function(rec)
46183     {
46184         var valueField = this.combo.valueField;
46185         var displayField = this.combo.displayField;
46186         
46187         if (this.items.indexOfKey(rec[valueField]) > -1) {
46188             //console.log("GOT " + rec.data.id);
46189             return;
46190         }
46191         
46192         var x = new Roo.form.ComboBoxArray.Item({
46193             //id : rec[this.idField],
46194             data : rec,
46195             displayField : displayField ,
46196             tipField : displayField ,
46197             cb : this
46198         });
46199         // use the 
46200         this.items.add(rec[valueField],x);
46201         // add it before the element..
46202         this.updateHiddenEl();
46203         x.render(this.outerWrap, this.wrap.dom);
46204         // add the image handler..
46205     },
46206     
46207     updateHiddenEl : function()
46208     {
46209         this.validate();
46210         if (!this.hiddenEl) {
46211             return;
46212         }
46213         var ar = [];
46214         var idField = this.combo.valueField;
46215         
46216         this.items.each(function(f) {
46217             ar.push(f.data[idField]);
46218         });
46219         this.hiddenEl.dom.value = ar.join(this.seperator);
46220         this.validate();
46221     },
46222     
46223     reset : function()
46224     {
46225         this.items.clear();
46226         
46227         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
46228            el.remove();
46229         });
46230         
46231         this.el.dom.value = '';
46232         if (this.hiddenEl) {
46233             this.hiddenEl.dom.value = '';
46234         }
46235         
46236     },
46237     getValue: function()
46238     {
46239         return this.hiddenEl ? this.hiddenEl.dom.value : '';
46240     },
46241     setValue: function(v) // not a valid action - must use addItems..
46242     {
46243         
46244         this.reset();
46245          
46246         if (this.store.isLocal && (typeof(v) == 'string')) {
46247             // then we can use the store to find the values..
46248             // comma seperated at present.. this needs to allow JSON based encoding..
46249             this.hiddenEl.value  = v;
46250             var v_ar = [];
46251             Roo.each(v.split(this.seperator), function(k) {
46252                 Roo.log("CHECK " + this.valueField + ',' + k);
46253                 var li = this.store.query(this.valueField, k);
46254                 if (!li.length) {
46255                     return;
46256                 }
46257                 var add = {};
46258                 add[this.valueField] = k;
46259                 add[this.displayField] = li.item(0).data[this.displayField];
46260                 
46261                 this.addItem(add);
46262             }, this) 
46263              
46264         }
46265         if (typeof(v) == 'object' ) {
46266             // then let's assume it's an array of objects..
46267             Roo.each(v, function(l) {
46268                 var add = l;
46269                 if (typeof(l) == 'string') {
46270                     add = {};
46271                     add[this.valueField] = l;
46272                     add[this.displayField] = l
46273                 }
46274                 this.addItem(add);
46275             }, this);
46276              
46277         }
46278         
46279         
46280     },
46281     setFromData: function(v)
46282     {
46283         // this recieves an object, if setValues is called.
46284         this.reset();
46285         this.el.dom.value = v[this.displayField];
46286         this.hiddenEl.dom.value = v[this.valueField];
46287         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
46288             return;
46289         }
46290         var kv = v[this.valueField];
46291         var dv = v[this.displayField];
46292         kv = typeof(kv) != 'string' ? '' : kv;
46293         dv = typeof(dv) != 'string' ? '' : dv;
46294         
46295         
46296         var keys = kv.split(this.seperator);
46297         var display = dv.split(this.seperator);
46298         for (var i = 0 ; i < keys.length; i++) {
46299             add = {};
46300             add[this.valueField] = keys[i];
46301             add[this.displayField] = display[i];
46302             this.addItem(add);
46303         }
46304       
46305         
46306     },
46307     
46308     /**
46309      * Validates the combox array value
46310      * @return {Boolean} True if the value is valid, else false
46311      */
46312     validate : function(){
46313         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
46314             this.clearInvalid();
46315             return true;
46316         }
46317         return false;
46318     },
46319     
46320     validateValue : function(value){
46321         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
46322         
46323     },
46324     
46325     /*@
46326      * overide
46327      * 
46328      */
46329     isDirty : function() {
46330         if(this.disabled) {
46331             return false;
46332         }
46333         
46334         try {
46335             var d = Roo.decode(String(this.originalValue));
46336         } catch (e) {
46337             return String(this.getValue()) !== String(this.originalValue);
46338         }
46339         
46340         var originalValue = [];
46341         
46342         for (var i = 0; i < d.length; i++){
46343             originalValue.push(d[i][this.valueField]);
46344         }
46345         
46346         return String(this.getValue()) !== String(originalValue.join(this.seperator));
46347         
46348     }
46349     
46350 });
46351
46352
46353
46354 /**
46355  * @class Roo.form.ComboBoxArray.Item
46356  * @extends Roo.BoxComponent
46357  * A selected item in the list
46358  *  Fred [x]  Brian [x]  [Pick another |v]
46359  * 
46360  * @constructor
46361  * Create a new item.
46362  * @param {Object} config Configuration options
46363  */
46364  
46365 Roo.form.ComboBoxArray.Item = function(config) {
46366     config.id = Roo.id();
46367     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
46368 }
46369
46370 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
46371     data : {},
46372     cb: false,
46373     displayField : false,
46374     tipField : false,
46375      
46376     
46377     defaultAutoCreate : {
46378         tag: 'div',
46379         cls: 'x-cbarray-item',
46380         cn : [ 
46381             { tag: 'div' },
46382             {
46383                 tag: 'img',
46384                 width:16,
46385                 height : 16,
46386                 src : Roo.BLANK_IMAGE_URL ,
46387                 align: 'center'
46388             }
46389         ]
46390         
46391     },
46392     
46393  
46394     onRender : function(ct, position)
46395     {
46396         Roo.form.Field.superclass.onRender.call(this, ct, position);
46397         
46398         if(!this.el){
46399             var cfg = this.getAutoCreate();
46400             this.el = ct.createChild(cfg, position);
46401         }
46402         
46403         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
46404         
46405         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
46406             this.cb.renderer(this.data) :
46407             String.format('{0}',this.data[this.displayField]);
46408         
46409             
46410         this.el.child('div').dom.setAttribute('qtip',
46411                         String.format('{0}',this.data[this.tipField])
46412         );
46413         
46414         this.el.child('img').on('click', this.remove, this);
46415         
46416     },
46417    
46418     remove : function()
46419     {
46420         if(this.cb.disabled){
46421             return;
46422         }
46423         
46424         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
46425             this.cb.items.remove(this);
46426             this.el.child('img').un('click', this.remove, this);
46427             this.el.remove();
46428             this.cb.updateHiddenEl();
46429
46430             this.cb.fireEvent('remove', this.cb, this);
46431         }
46432         
46433     }
46434 });/*
46435  * RooJS Library 1.1.1
46436  * Copyright(c) 2008-2011  Alan Knowles
46437  *
46438  * License - LGPL
46439  */
46440  
46441
46442 /**
46443  * @class Roo.form.ComboNested
46444  * @extends Roo.form.ComboBox
46445  * A combobox for that allows selection of nested items in a list,
46446  * eg.
46447  *
46448  *  Book
46449  *    -> red
46450  *    -> green
46451  *  Table
46452  *    -> square
46453  *      ->red
46454  *      ->green
46455  *    -> rectangle
46456  *      ->green
46457  *      
46458  * 
46459  * @constructor
46460  * Create a new ComboNested
46461  * @param {Object} config Configuration options
46462  */
46463 Roo.form.ComboNested = function(config){
46464     Roo.form.ComboCheck.superclass.constructor.call(this, config);
46465     // should verify some data...
46466     // like
46467     // hiddenName = required..
46468     // displayField = required
46469     // valudField == required
46470     var req= [ 'hiddenName', 'displayField', 'valueField' ];
46471     var _t = this;
46472     Roo.each(req, function(e) {
46473         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
46474             throw "Roo.form.ComboNested : missing value for: " + e;
46475         }
46476     });
46477      
46478     
46479 };
46480
46481 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
46482    
46483     /*
46484      * @config {Number} max Number of columns to show
46485      */
46486     
46487     maxColumns : 3,
46488    
46489     list : null, // the outermost div..
46490     innerLists : null, // the
46491     views : null,
46492     stores : null,
46493     // private
46494     loadingChildren : false,
46495     
46496     onRender : function(ct, position)
46497     {
46498         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
46499         
46500         if(this.hiddenName){
46501             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
46502                     'before', true);
46503             this.hiddenField.value =
46504                 this.hiddenValue !== undefined ? this.hiddenValue :
46505                 this.value !== undefined ? this.value : '';
46506
46507             // prevent input submission
46508             this.el.dom.removeAttribute('name');
46509              
46510              
46511         }
46512         
46513         if(Roo.isGecko){
46514             this.el.dom.setAttribute('autocomplete', 'off');
46515         }
46516
46517         var cls = 'x-combo-list';
46518
46519         this.list = new Roo.Layer({
46520             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
46521         });
46522
46523         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
46524         this.list.setWidth(lw);
46525         this.list.swallowEvent('mousewheel');
46526         this.assetHeight = 0;
46527
46528         if(this.title){
46529             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
46530             this.assetHeight += this.header.getHeight();
46531         }
46532         this.innerLists = [];
46533         this.views = [];
46534         this.stores = [];
46535         for (var i =0 ; i < this.maxColumns; i++) {
46536             this.onRenderList( cls, i);
46537         }
46538         
46539         // always needs footer, as we are going to have an 'OK' button.
46540         this.footer = this.list.createChild({cls:cls+'-ft'});
46541         this.pageTb = new Roo.Toolbar(this.footer);  
46542         var _this = this;
46543         this.pageTb.add(  {
46544             
46545             text: 'Done',
46546             handler: function()
46547             {
46548                 _this.collapse();
46549             }
46550         });
46551         
46552         if ( this.allowBlank && !this.disableClear) {
46553             
46554             this.pageTb.add(new Roo.Toolbar.Fill(), {
46555                 cls: 'x-btn-icon x-btn-clear',
46556                 text: '&#160;',
46557                 handler: function()
46558                 {
46559                     _this.collapse();
46560                     _this.clearValue();
46561                     _this.onSelect(false, -1);
46562                 }
46563             });
46564         }
46565         if (this.footer) {
46566             this.assetHeight += this.footer.getHeight();
46567         }
46568         
46569     },
46570     onRenderList : function (  cls, i)
46571     {
46572         
46573         var lw = Math.floor(
46574                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
46575         );
46576         
46577         this.list.setWidth(lw); // default to '1'
46578
46579         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
46580         //il.on('mouseover', this.onViewOver, this, { list:  i });
46581         //il.on('mousemove', this.onViewMove, this, { list:  i });
46582         il.setWidth(lw);
46583         il.setStyle({ 'overflow-x' : 'hidden'});
46584
46585         if(!this.tpl){
46586             this.tpl = new Roo.Template({
46587                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
46588                 isEmpty: function (value, allValues) {
46589                     //Roo.log(value);
46590                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
46591                     return dl ? 'has-children' : 'no-children'
46592                 }
46593             });
46594         }
46595         
46596         var store  = this.store;
46597         if (i > 0) {
46598             store  = new Roo.data.SimpleStore({
46599                 //fields : this.store.reader.meta.fields,
46600                 reader : this.store.reader,
46601                 data : [ ]
46602             });
46603         }
46604         this.stores[i]  = store;
46605                   
46606         var view = this.views[i] = new Roo.View(
46607             il,
46608             this.tpl,
46609             {
46610                 singleSelect:true,
46611                 store: store,
46612                 selectedClass: this.selectedClass
46613             }
46614         );
46615         view.getEl().setWidth(lw);
46616         view.getEl().setStyle({
46617             position: i < 1 ? 'relative' : 'absolute',
46618             top: 0,
46619             left: (i * lw ) + 'px',
46620             display : i > 0 ? 'none' : 'block'
46621         });
46622         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
46623         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
46624         //view.on('click', this.onViewClick, this, { list : i });
46625
46626         store.on('beforeload', this.onBeforeLoad, this);
46627         store.on('load',  this.onLoad, this, { list  : i});
46628         store.on('loadexception', this.onLoadException, this);
46629
46630         // hide the other vies..
46631         
46632         
46633         
46634     },
46635       
46636     restrictHeight : function()
46637     {
46638         var mh = 0;
46639         Roo.each(this.innerLists, function(il,i) {
46640             var el = this.views[i].getEl();
46641             el.dom.style.height = '';
46642             var inner = el.dom;
46643             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
46644             // only adjust heights on other ones..
46645             mh = Math.max(h, mh);
46646             if (i < 1) {
46647                 
46648                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
46649                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
46650                
46651             }
46652             
46653             
46654         }, this);
46655         
46656         this.list.beginUpdate();
46657         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
46658         this.list.alignTo(this.el, this.listAlign);
46659         this.list.endUpdate();
46660         
46661     },
46662      
46663     
46664     // -- store handlers..
46665     // private
46666     onBeforeLoad : function()
46667     {
46668         if(!this.hasFocus){
46669             return;
46670         }
46671         this.innerLists[0].update(this.loadingText ?
46672                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
46673         this.restrictHeight();
46674         this.selectedIndex = -1;
46675     },
46676     // private
46677     onLoad : function(a,b,c,d)
46678     {
46679         if (!this.loadingChildren) {
46680             // then we are loading the top level. - hide the children
46681             for (var i = 1;i < this.views.length; i++) {
46682                 this.views[i].getEl().setStyle({ display : 'none' });
46683             }
46684             var lw = Math.floor(
46685                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
46686             );
46687         
46688              this.list.setWidth(lw); // default to '1'
46689
46690             
46691         }
46692         if(!this.hasFocus){
46693             return;
46694         }
46695         
46696         if(this.store.getCount() > 0) {
46697             this.expand();
46698             this.restrictHeight();   
46699         } else {
46700             this.onEmptyResults();
46701         }
46702         
46703         if (!this.loadingChildren) {
46704             this.selectActive();
46705         }
46706         /*
46707         this.stores[1].loadData([]);
46708         this.stores[2].loadData([]);
46709         this.views
46710         */    
46711     
46712         //this.el.focus();
46713     },
46714     
46715     
46716     // private
46717     onLoadException : function()
46718     {
46719         this.collapse();
46720         Roo.log(this.store.reader.jsonData);
46721         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
46722             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
46723         }
46724         
46725         
46726     },
46727     // no cleaning of leading spaces on blur here.
46728     cleanLeadingSpace : function(e) { },
46729     
46730
46731     onSelectChange : function (view, sels, opts )
46732     {
46733         var ix = view.getSelectedIndexes();
46734          
46735         if (opts.list > this.maxColumns - 2) {
46736             if (view.store.getCount()<  1) {
46737                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
46738
46739             } else  {
46740                 if (ix.length) {
46741                     // used to clear ?? but if we are loading unselected 
46742                     this.setFromData(view.store.getAt(ix[0]).data);
46743                 }
46744                 
46745             }
46746             
46747             return;
46748         }
46749         
46750         if (!ix.length) {
46751             // this get's fired when trigger opens..
46752            // this.setFromData({});
46753             var str = this.stores[opts.list+1];
46754             str.data.clear(); // removeall wihtout the fire events..
46755             return;
46756         }
46757         
46758         var rec = view.store.getAt(ix[0]);
46759          
46760         this.setFromData(rec.data);
46761         this.fireEvent('select', this, rec, ix[0]);
46762         
46763         var lw = Math.floor(
46764              (
46765                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
46766              ) / this.maxColumns
46767         );
46768         this.loadingChildren = true;
46769         this.stores[opts.list+1].loadDataFromChildren( rec );
46770         this.loadingChildren = false;
46771         var dl = this.stores[opts.list+1]. getTotalCount();
46772         
46773         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
46774         
46775         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
46776         for (var i = opts.list+2; i < this.views.length;i++) {
46777             this.views[i].getEl().setStyle({ display : 'none' });
46778         }
46779         
46780         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
46781         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
46782         
46783         if (this.isLoading) {
46784            // this.selectActive(opts.list);
46785         }
46786          
46787     },
46788     
46789     
46790     
46791     
46792     onDoubleClick : function()
46793     {
46794         this.collapse(); //??
46795     },
46796     
46797      
46798     
46799     
46800     
46801     // private
46802     recordToStack : function(store, prop, value, stack)
46803     {
46804         var cstore = new Roo.data.SimpleStore({
46805             //fields : this.store.reader.meta.fields, // we need array reader.. for
46806             reader : this.store.reader,
46807             data : [ ]
46808         });
46809         var _this = this;
46810         var record  = false;
46811         var srec = false;
46812         if(store.getCount() < 1){
46813             return false;
46814         }
46815         store.each(function(r){
46816             if(r.data[prop] == value){
46817                 record = r;
46818             srec = r;
46819                 return false;
46820             }
46821             if (r.data.cn && r.data.cn.length) {
46822                 cstore.loadDataFromChildren( r);
46823                 var cret = _this.recordToStack(cstore, prop, value, stack);
46824                 if (cret !== false) {
46825                     record = cret;
46826                     srec = r;
46827                     return false;
46828                 }
46829             }
46830              
46831             return true;
46832         });
46833         if (record == false) {
46834             return false
46835         }
46836         stack.unshift(srec);
46837         return record;
46838     },
46839     
46840     /*
46841      * find the stack of stores that match our value.
46842      *
46843      * 
46844      */
46845     
46846     selectActive : function ()
46847     {
46848         // if store is not loaded, then we will need to wait for that to happen first.
46849         var stack = [];
46850         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
46851         for (var i = 0; i < stack.length; i++ ) {
46852             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
46853         }
46854         
46855     }
46856         
46857          
46858     
46859     
46860     
46861     
46862 });/*
46863  * Based on:
46864  * Ext JS Library 1.1.1
46865  * Copyright(c) 2006-2007, Ext JS, LLC.
46866  *
46867  * Originally Released Under LGPL - original licence link has changed is not relivant.
46868  *
46869  * Fork - LGPL
46870  * <script type="text/javascript">
46871  */
46872 /**
46873  * @class Roo.form.Checkbox
46874  * @extends Roo.form.Field
46875  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
46876  * @constructor
46877  * Creates a new Checkbox
46878  * @param {Object} config Configuration options
46879  */
46880 Roo.form.Checkbox = function(config){
46881     Roo.form.Checkbox.superclass.constructor.call(this, config);
46882     this.addEvents({
46883         /**
46884          * @event check
46885          * Fires when the checkbox is checked or unchecked.
46886              * @param {Roo.form.Checkbox} this This checkbox
46887              * @param {Boolean} checked The new checked value
46888              */
46889         check : true
46890     });
46891 };
46892
46893 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
46894     /**
46895      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
46896      */
46897     focusClass : undefined,
46898     /**
46899      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
46900      */
46901     fieldClass: "x-form-field",
46902     /**
46903      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
46904      */
46905     checked: false,
46906     /**
46907      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46908      * {tag: "input", type: "checkbox", autocomplete: "off"})
46909      */
46910     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
46911     /**
46912      * @cfg {String} boxLabel The text that appears beside the checkbox
46913      */
46914     boxLabel : "",
46915     /**
46916      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
46917      */  
46918     inputValue : '1',
46919     /**
46920      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
46921      */
46922      valueOff: '0', // value when not checked..
46923
46924     actionMode : 'viewEl', 
46925     //
46926     // private
46927     itemCls : 'x-menu-check-item x-form-item',
46928     groupClass : 'x-menu-group-item',
46929     inputType : 'hidden',
46930     
46931     
46932     inSetChecked: false, // check that we are not calling self...
46933     
46934     inputElement: false, // real input element?
46935     basedOn: false, // ????
46936     
46937     isFormField: true, // not sure where this is needed!!!!
46938
46939     onResize : function(){
46940         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
46941         if(!this.boxLabel){
46942             this.el.alignTo(this.wrap, 'c-c');
46943         }
46944     },
46945
46946     initEvents : function(){
46947         Roo.form.Checkbox.superclass.initEvents.call(this);
46948         this.el.on("click", this.onClick,  this);
46949         this.el.on("change", this.onClick,  this);
46950     },
46951
46952
46953     getResizeEl : function(){
46954         return this.wrap;
46955     },
46956
46957     getPositionEl : function(){
46958         return this.wrap;
46959     },
46960
46961     // private
46962     onRender : function(ct, position){
46963         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
46964         /*
46965         if(this.inputValue !== undefined){
46966             this.el.dom.value = this.inputValue;
46967         }
46968         */
46969         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
46970         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
46971         var viewEl = this.wrap.createChild({ 
46972             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
46973         this.viewEl = viewEl;   
46974         this.wrap.on('click', this.onClick,  this); 
46975         
46976         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
46977         this.el.on('propertychange', this.setFromHidden,  this);  //ie
46978         
46979         
46980         
46981         if(this.boxLabel){
46982             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
46983         //    viewEl.on('click', this.onClick,  this); 
46984         }
46985         //if(this.checked){
46986             this.setChecked(this.checked);
46987         //}else{
46988             //this.checked = this.el.dom;
46989         //}
46990
46991     },
46992
46993     // private
46994     initValue : Roo.emptyFn,
46995
46996     /**
46997      * Returns the checked state of the checkbox.
46998      * @return {Boolean} True if checked, else false
46999      */
47000     getValue : function(){
47001         if(this.el){
47002             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
47003         }
47004         return this.valueOff;
47005         
47006     },
47007
47008         // private
47009     onClick : function(){ 
47010         if (this.disabled) {
47011             return;
47012         }
47013         this.setChecked(!this.checked);
47014
47015         //if(this.el.dom.checked != this.checked){
47016         //    this.setValue(this.el.dom.checked);
47017        // }
47018     },
47019
47020     /**
47021      * Sets the checked state of the checkbox.
47022      * On is always based on a string comparison between inputValue and the param.
47023      * @param {Boolean/String} value - the value to set 
47024      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
47025      */
47026     setValue : function(v,suppressEvent){
47027         
47028         
47029         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
47030         //if(this.el && this.el.dom){
47031         //    this.el.dom.checked = this.checked;
47032         //    this.el.dom.defaultChecked = this.checked;
47033         //}
47034         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
47035         //this.fireEvent("check", this, this.checked);
47036     },
47037     // private..
47038     setChecked : function(state,suppressEvent)
47039     {
47040         if (this.inSetChecked) {
47041             this.checked = state;
47042             return;
47043         }
47044         
47045     
47046         if(this.wrap){
47047             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
47048         }
47049         this.checked = state;
47050         if(suppressEvent !== true){
47051             this.fireEvent('check', this, state);
47052         }
47053         this.inSetChecked = true;
47054                  
47055                 this.el.dom.value = state ? this.inputValue : this.valueOff;
47056                  
47057         this.inSetChecked = false;
47058         
47059     },
47060     // handle setting of hidden value by some other method!!?!?
47061     setFromHidden: function()
47062     {
47063         if(!this.el){
47064             return;
47065         }
47066         //console.log("SET FROM HIDDEN");
47067         //alert('setFrom hidden');
47068         this.setValue(this.el.dom.value);
47069     },
47070     
47071     onDestroy : function()
47072     {
47073         if(this.viewEl){
47074             Roo.get(this.viewEl).remove();
47075         }
47076          
47077         Roo.form.Checkbox.superclass.onDestroy.call(this);
47078     },
47079     
47080     setBoxLabel : function(str)
47081     {
47082         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
47083     }
47084
47085 });/*
47086  * Based on:
47087  * Ext JS Library 1.1.1
47088  * Copyright(c) 2006-2007, Ext JS, LLC.
47089  *
47090  * Originally Released Under LGPL - original licence link has changed is not relivant.
47091  *
47092  * Fork - LGPL
47093  * <script type="text/javascript">
47094  */
47095  
47096 /**
47097  * @class Roo.form.Radio
47098  * @extends Roo.form.Checkbox
47099  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
47100  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
47101  * @constructor
47102  * Creates a new Radio
47103  * @param {Object} config Configuration options
47104  */
47105 Roo.form.Radio = function(){
47106     Roo.form.Radio.superclass.constructor.apply(this, arguments);
47107 };
47108 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
47109     inputType: 'radio',
47110
47111     /**
47112      * If this radio is part of a group, it will return the selected value
47113      * @return {String}
47114      */
47115     getGroupValue : function(){
47116         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
47117     },
47118     
47119     
47120     onRender : function(ct, position){
47121         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
47122         
47123         if(this.inputValue !== undefined){
47124             this.el.dom.value = this.inputValue;
47125         }
47126          
47127         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
47128         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
47129         //var viewEl = this.wrap.createChild({ 
47130         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
47131         //this.viewEl = viewEl;   
47132         //this.wrap.on('click', this.onClick,  this); 
47133         
47134         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
47135         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
47136         
47137         
47138         
47139         if(this.boxLabel){
47140             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
47141         //    viewEl.on('click', this.onClick,  this); 
47142         }
47143          if(this.checked){
47144             this.el.dom.checked =   'checked' ;
47145         }
47146          
47147     },
47148     /**
47149      * Sets the checked state of the checkbox.
47150      * On is always based on a string comparison between inputValue and the param.
47151      * @param {Boolean/String} value - the value to set 
47152      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
47153      */
47154     setValue : function(v,suppressEvent){
47155         
47156         
47157         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
47158         //if(this.el && this.el.dom){
47159         //    this.el.dom.checked = this.checked;
47160         //    this.el.dom.defaultChecked = this.checked;
47161         //}
47162         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
47163         
47164         this.el.dom.form[this.name].value = v;
47165      
47166         //this.fireEvent("check", this, this.checked);
47167     },
47168     // private..
47169     setChecked : function(state,suppressEvent)
47170     {
47171          
47172         if(this.wrap){
47173             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
47174         }
47175         this.checked = state;
47176         if(suppressEvent !== true){
47177             this.fireEvent('check', this, state);
47178         }
47179                  
47180                   
47181        
47182         
47183     },
47184     reset : function(){
47185         // this.setValue(this.resetValue);
47186         //this.originalValue = this.getValue();
47187         this.clearInvalid();
47188     } 
47189     
47190 });Roo.rtf = {}; // namespace
47191 Roo.rtf.Hex = function(hex)
47192 {
47193     this.hexstr = hex;
47194 };
47195 Roo.rtf.Paragraph = function(opts)
47196 {
47197     this.content = []; ///??? is that used?
47198 };Roo.rtf.Span = function(opts)
47199 {
47200     this.value = opts.value;
47201 };
47202
47203 Roo.rtf.Group = function(parent)
47204 {
47205     // we dont want to acutally store parent - it will make debug a nightmare..
47206     this.content = [];
47207     this.cn  = [];
47208      
47209        
47210     
47211 };
47212
47213 Roo.rtf.Group.prototype = {
47214     ignorable : false,
47215     content: false,
47216     cn: false,
47217     addContent : function(node) {
47218         // could set styles...
47219         this.content.push(node);
47220     },
47221     addChild : function(cn)
47222     {
47223         this.cn.push(cn);
47224     },
47225     // only for images really...
47226     toDataURL : function()
47227     {
47228         var mimetype = false;
47229         switch(true) {
47230             case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0: 
47231                 mimetype = "image/png";
47232                 break;
47233              case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
47234                 mimetype = "image/jpeg";
47235                 break;
47236             default :
47237                 return 'about:blank'; // ?? error?
47238         }
47239         
47240         
47241         var hexstring = this.content[this.content.length-1].value;
47242         
47243         return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
47244             return String.fromCharCode(parseInt(a, 16));
47245         }).join(""));
47246     }
47247     
47248 };
47249 // this looks like it's normally the {rtf{ .... }}
47250 Roo.rtf.Document = function()
47251 {
47252     // we dont want to acutally store parent - it will make debug a nightmare..
47253     this.rtlch  = [];
47254     this.content = [];
47255     this.cn = [];
47256     
47257 };
47258 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, { 
47259     addChild : function(cn)
47260     {
47261         this.cn.push(cn);
47262         switch(cn.type) {
47263             case 'rtlch': // most content seems to be inside this??
47264             case 'listtext':
47265             case 'shpinst':
47266                 this.rtlch.push(cn);
47267                 return;
47268             default:
47269                 this[cn.type] = cn;
47270         }
47271         
47272     },
47273     
47274     getElementsByType : function(type)
47275     {
47276         var ret =  [];
47277         this._getElementsByType(type, ret, this.cn, 'rtf');
47278         return ret;
47279     },
47280     _getElementsByType : function (type, ret, search_array, path)
47281     {
47282         search_array.forEach(function(n,i) {
47283             if (n.type == type) {
47284                 n.path = path + '/' + n.type + ':' + i;
47285                 ret.push(n);
47286             }
47287             if (n.cn.length > 0) {
47288                 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
47289             }
47290         },this);
47291     }
47292     
47293 });
47294  
47295 Roo.rtf.Ctrl = function(opts)
47296 {
47297     this.value = opts.value;
47298     this.param = opts.param;
47299 };
47300 /**
47301  *
47302  *
47303  * based on this https://github.com/iarna/rtf-parser
47304  * it's really only designed to extract pict from pasted RTF 
47305  *
47306  * usage:
47307  *
47308  *  var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
47309  *  
47310  *
47311  */
47312
47313  
47314
47315
47316
47317 Roo.rtf.Parser = function(text) {
47318     //super({objectMode: true})
47319     this.text = '';
47320     this.parserState = this.parseText;
47321     
47322     // these are for interpeter...
47323     this.doc = {};
47324     ///this.parserState = this.parseTop
47325     this.groupStack = [];
47326     this.hexStore = [];
47327     this.doc = false;
47328     
47329     this.groups = []; // where we put the return.
47330     
47331     for (var ii = 0; ii < text.length; ++ii) {
47332         ++this.cpos;
47333         
47334         if (text[ii] === '\n') {
47335             ++this.row;
47336             this.col = 1;
47337         } else {
47338             ++this.col;
47339         }
47340         this.parserState(text[ii]);
47341     }
47342     
47343     
47344     
47345 };
47346 Roo.rtf.Parser.prototype = {
47347     text : '', // string being parsed..
47348     controlWord : '',
47349     controlWordParam :  '',
47350     hexChar : '',
47351     doc : false,
47352     group: false,
47353     groupStack : false,
47354     hexStore : false,
47355     
47356     
47357     cpos : 0, 
47358     row : 1, // reportin?
47359     col : 1, //
47360
47361      
47362     push : function (el)
47363     {
47364         var m = 'cmd'+ el.type;
47365         if (typeof(this[m]) == 'undefined') {
47366             Roo.log('invalid cmd:' + el.type);
47367             return;
47368         }
47369         this[m](el);
47370         //Roo.log(el);
47371     },
47372     flushHexStore : function()
47373     {
47374         if (this.hexStore.length < 1) {
47375             return;
47376         }
47377         var hexstr = this.hexStore.map(
47378             function(cmd) {
47379                 return cmd.value;
47380         }).join('');
47381         
47382         this.group.addContent( new Roo.rtf.Hex( hexstr ));
47383               
47384             
47385         this.hexStore.splice(0)
47386         
47387     },
47388     
47389     cmdgroupstart : function()
47390     {
47391         this.flushHexStore();
47392         if (this.group) {
47393             this.groupStack.push(this.group);
47394         }
47395          // parent..
47396         if (this.doc === false) {
47397             this.group = this.doc = new Roo.rtf.Document();
47398             return;
47399             
47400         }
47401         this.group = new Roo.rtf.Group(this.group);
47402     },
47403     cmdignorable : function()
47404     {
47405         this.flushHexStore();
47406         this.group.ignorable = true;
47407     },
47408     cmdendparagraph : function()
47409     {
47410         this.flushHexStore();
47411         this.group.addContent(new Roo.rtf.Paragraph());
47412     },
47413     cmdgroupend : function ()
47414     {
47415         this.flushHexStore();
47416         var endingGroup = this.group;
47417         
47418         
47419         this.group = this.groupStack.pop();
47420         if (this.group) {
47421             this.group.addChild(endingGroup);
47422         }
47423         
47424         
47425         
47426         var doc = this.group || this.doc;
47427         //if (endingGroup instanceof FontTable) {
47428         //  doc.fonts = endingGroup.table
47429         //} else if (endingGroup instanceof ColorTable) {
47430         //  doc.colors = endingGroup.table
47431         //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
47432         if (endingGroup.ignorable === false) {
47433             //code
47434             this.groups.push(endingGroup);
47435            // Roo.log( endingGroup );
47436         }
47437             //Roo.each(endingGroup.content, function(item)) {
47438             //    doc.addContent(item);
47439             //}
47440             //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
47441         //}
47442     },
47443     cmdtext : function (cmd)
47444     {
47445         this.flushHexStore();
47446         if (!this.group) { // an RTF fragment, missing the {\rtf1 header
47447             //this.group = this.doc
47448             return;  // we really don't care about stray text...
47449         }
47450         this.group.addContent(new Roo.rtf.Span(cmd));
47451     },
47452     cmdcontrolword : function (cmd)
47453     {
47454         this.flushHexStore();
47455         if (!this.group.type) {
47456             this.group.type = cmd.value;
47457             return;
47458         }
47459         this.group.addContent(new Roo.rtf.Ctrl(cmd));
47460         // we actually don't care about ctrl words...
47461         return ;
47462         /*
47463         var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
47464         if (this[method]) {
47465             this[method](cmd.param)
47466         } else {
47467             if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
47468         }
47469         */
47470     },
47471     cmdhexchar : function(cmd) {
47472         this.hexStore.push(cmd);
47473     },
47474     cmderror : function(cmd) {
47475         throw cmd.value;
47476     },
47477     
47478     /*
47479       _flush (done) {
47480         if (this.text !== '\u0000') this.emitText()
47481         done()
47482       }
47483       */
47484       
47485       
47486     parseText : function(c)
47487     {
47488         if (c === '\\') {
47489             this.parserState = this.parseEscapes;
47490         } else if (c === '{') {
47491             this.emitStartGroup();
47492         } else if (c === '}') {
47493             this.emitEndGroup();
47494         } else if (c === '\x0A' || c === '\x0D') {
47495             // cr/lf are noise chars
47496         } else {
47497             this.text += c;
47498         }
47499     },
47500     
47501     parseEscapes: function (c)
47502     {
47503         if (c === '\\' || c === '{' || c === '}') {
47504             this.text += c;
47505             this.parserState = this.parseText;
47506         } else {
47507             this.parserState = this.parseControlSymbol;
47508             this.parseControlSymbol(c);
47509         }
47510     },
47511     parseControlSymbol: function(c)
47512     {
47513         if (c === '~') {
47514             this.text += '\u00a0'; // nbsp
47515             this.parserState = this.parseText
47516         } else if (c === '-') {
47517              this.text += '\u00ad'; // soft hyphen
47518         } else if (c === '_') {
47519             this.text += '\u2011'; // non-breaking hyphen
47520         } else if (c === '*') {
47521             this.emitIgnorable();
47522             this.parserState = this.parseText;
47523         } else if (c === "'") {
47524             this.parserState = this.parseHexChar;
47525         } else if (c === '|') { // formula cacter
47526             this.emitFormula();
47527             this.parserState = this.parseText;
47528         } else if (c === ':') { // subentry in an index entry
47529             this.emitIndexSubEntry();
47530             this.parserState = this.parseText;
47531         } else if (c === '\x0a') {
47532             this.emitEndParagraph();
47533             this.parserState = this.parseText;
47534         } else if (c === '\x0d') {
47535             this.emitEndParagraph();
47536             this.parserState = this.parseText;
47537         } else {
47538             this.parserState = this.parseControlWord;
47539             this.parseControlWord(c);
47540         }
47541     },
47542     parseHexChar: function (c)
47543     {
47544         if (/^[A-Fa-f0-9]$/.test(c)) {
47545             this.hexChar += c;
47546             if (this.hexChar.length >= 2) {
47547               this.emitHexChar();
47548               this.parserState = this.parseText;
47549             }
47550             return;
47551         }
47552         this.emitError("Invalid character \"" + c + "\" in hex literal.");
47553         this.parserState = this.parseText;
47554         
47555     },
47556     parseControlWord : function(c)
47557     {
47558         if (c === ' ') {
47559             this.emitControlWord();
47560             this.parserState = this.parseText;
47561         } else if (/^[-\d]$/.test(c)) {
47562             this.parserState = this.parseControlWordParam;
47563             this.controlWordParam += c;
47564         } else if (/^[A-Za-z]$/.test(c)) {
47565           this.controlWord += c;
47566         } else {
47567           this.emitControlWord();
47568           this.parserState = this.parseText;
47569           this.parseText(c);
47570         }
47571     },
47572     parseControlWordParam : function (c) {
47573         if (/^\d$/.test(c)) {
47574           this.controlWordParam += c;
47575         } else if (c === ' ') {
47576           this.emitControlWord();
47577           this.parserState = this.parseText;
47578         } else {
47579           this.emitControlWord();
47580           this.parserState = this.parseText;
47581           this.parseText(c);
47582         }
47583     },
47584     
47585     
47586     
47587     
47588     emitText : function () {
47589         if (this.text === '') {
47590             return;
47591         }
47592         this.push({
47593             type: 'text',
47594             value: this.text,
47595             pos: this.cpos,
47596             row: this.row,
47597             col: this.col
47598         });
47599         this.text = ''
47600     },
47601     emitControlWord : function ()
47602     {
47603         this.emitText();
47604         if (this.controlWord === '') {
47605             // do we want to track this - it seems just to cause problems.
47606             //this.emitError('empty control word');
47607         } else {
47608             this.push({
47609                   type: 'controlword',
47610                   value: this.controlWord,
47611                   param: this.controlWordParam !== '' && Number(this.controlWordParam),
47612                   pos: this.cpos,
47613                   row: this.row,
47614                   col: this.col
47615             });
47616         }
47617         this.controlWord = '';
47618         this.controlWordParam = '';
47619     },
47620     emitStartGroup : function ()
47621     {
47622         this.emitText();
47623         this.push({
47624             type: 'groupstart',
47625             pos: this.cpos,
47626             row: this.row,
47627             col: this.col
47628         });
47629     },
47630     emitEndGroup : function ()
47631     {
47632         this.emitText();
47633         this.push({
47634             type: 'groupend',
47635             pos: this.cpos,
47636             row: this.row,
47637             col: this.col
47638         });
47639     },
47640     emitIgnorable : function ()
47641     {
47642         this.emitText();
47643         this.push({
47644             type: 'ignorable',
47645             pos: this.cpos,
47646             row: this.row,
47647             col: this.col
47648         });
47649     },
47650     emitHexChar : function ()
47651     {
47652         this.emitText();
47653         this.push({
47654             type: 'hexchar',
47655             value: this.hexChar,
47656             pos: this.cpos,
47657             row: this.row,
47658             col: this.col
47659         });
47660         this.hexChar = ''
47661     },
47662     emitError : function (message)
47663     {
47664       this.emitText();
47665       this.push({
47666             type: 'error',
47667             value: message,
47668             row: this.row,
47669             col: this.col,
47670             char: this.cpos //,
47671             //stack: new Error().stack
47672         });
47673     },
47674     emitEndParagraph : function () {
47675         this.emitText();
47676         this.push({
47677             type: 'endparagraph',
47678             pos: this.cpos,
47679             row: this.row,
47680             col: this.col
47681         });
47682     }
47683      
47684 } ;
47685 Roo.htmleditor = {};
47686  
47687 /**
47688  * @class Roo.htmleditor.Filter
47689  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
47690  * @cfg {DomElement} node The node to iterate and filter
47691  * @cfg {boolean|String|Array} tag Tags to replace 
47692  * @constructor
47693  * Create a new Filter.
47694  * @param {Object} config Configuration options
47695  */
47696
47697
47698
47699 Roo.htmleditor.Filter = function(cfg) {
47700     Roo.apply(this.cfg);
47701     // this does not actually call walk as it's really just a abstract class
47702 }
47703
47704
47705 Roo.htmleditor.Filter.prototype = {
47706     
47707     node: false,
47708     
47709     tag: false,
47710
47711     // overrride to do replace comments.
47712     replaceComment : false,
47713     
47714     // overrride to do replace or do stuff with tags..
47715     replaceTag : false,
47716     
47717     walk : function(dom)
47718     {
47719         Roo.each( Array.from(dom.childNodes), function( e ) {
47720             switch(true) {
47721                 
47722                 case e.nodeType == 8 &&  this.replaceComment  !== false: // comment
47723                     this.replaceComment(e);
47724                     return;
47725                 
47726                 case e.nodeType != 1: //not a node.
47727                     return;
47728                 
47729                 case this.tag === true: // everything
47730                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1:
47731                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":":
47732                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
47733                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
47734                     if (this.replaceTag && false === this.replaceTag(e)) {
47735                         return;
47736                     }
47737                     if (e.hasChildNodes()) {
47738                         this.walk(e);
47739                     }
47740                     return;
47741                 
47742                 default:    // tags .. that do not match.
47743                     if (e.hasChildNodes()) {
47744                         this.walk(e);
47745                     }
47746             }
47747             
47748         }, this);
47749         
47750     },
47751     
47752     
47753     removeNodeKeepChildren : function( node)
47754     {
47755     
47756         ar = Array.from(node.childNodes);
47757         for (var i = 0; i < ar.length; i++) {
47758          
47759             node.removeChild(ar[i]);
47760             // what if we need to walk these???
47761             node.parentNode.insertBefore(ar[i], node);
47762            
47763         }
47764         node.parentNode.removeChild(node);
47765     },
47766
47767     searchTag : function(dom)
47768     {
47769         if(this.tag === false) {
47770             return;
47771         }
47772
47773         var els = dom.getElementsByTagName(this.tag);
47774
47775         Roo.each(Array.from(els), function(e){
47776             if(e.parentNode == null) {
47777                 return;
47778             }
47779             if(this.replaceTag) {
47780                 this.replaceTag(e);
47781             }
47782         }, this);
47783     }
47784 }; 
47785
47786 /**
47787  * @class Roo.htmleditor.FilterAttributes
47788  * clean attributes and  styles including http:// etc.. in attribute
47789  * @constructor
47790 * Run a new Attribute Filter
47791 * @param {Object} config Configuration options
47792  */
47793 Roo.htmleditor.FilterAttributes = function(cfg)
47794 {
47795     Roo.apply(this, cfg);
47796     this.attrib_black = this.attrib_black || [];
47797     this.attrib_white = this.attrib_white || [];
47798
47799     this.attrib_clean = this.attrib_clean || [];
47800     this.style_white = this.style_white || [];
47801     this.style_black = this.style_black || [];
47802     this.walk(cfg.node);
47803 }
47804
47805 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
47806 {
47807     tag: true, // all tags
47808     
47809     attrib_black : false, // array
47810     attrib_clean : false,
47811     attrib_white : false,
47812
47813     style_white : false,
47814     style_black : false,
47815      
47816      
47817     replaceTag : function(node)
47818     {
47819         if (!node.attributes || !node.attributes.length) {
47820             return true;
47821         }
47822         
47823         for (var i = node.attributes.length-1; i > -1 ; i--) {
47824             var a = node.attributes[i];
47825             //console.log(a);
47826             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
47827                 node.removeAttribute(a.name);
47828                 continue;
47829             }
47830             
47831             
47832             
47833             if (a.name.toLowerCase().substr(0,2)=='on')  {
47834                 node.removeAttribute(a.name);
47835                 continue;
47836             }
47837             
47838             
47839             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
47840                 node.removeAttribute(a.name);
47841                 continue;
47842             }
47843             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
47844                 this.cleanAttr(node,a.name,a.value); // fixme..
47845                 continue;
47846             }
47847             if (a.name == 'style') {
47848                 this.cleanStyle(node,a.name,a.value);
47849                 continue;
47850             }
47851             /// clean up MS crap..
47852             // tecnically this should be a list of valid class'es..
47853             
47854             
47855             if (a.name == 'class') {
47856                 if (a.value.match(/^Mso/)) {
47857                     node.removeAttribute('class');
47858                 }
47859                 
47860                 if (a.value.match(/^body$/)) {
47861                     node.removeAttribute('class');
47862                 }
47863                 continue;
47864             }
47865             
47866             
47867             // style cleanup!?
47868             // class cleanup?
47869             
47870         }
47871         return true; // clean children
47872     },
47873         
47874     cleanAttr: function(node, n,v)
47875     {
47876         
47877         if (v.match(/^\./) || v.match(/^\//)) {
47878             return;
47879         }
47880         if (v.match(/^(http|https):\/\//)
47881             || v.match(/^mailto:/) 
47882             || v.match(/^ftp:/)
47883             || v.match(/^data:/)
47884             ) {
47885             return;
47886         }
47887         if (v.match(/^#/)) {
47888             return;
47889         }
47890         if (v.match(/^\{/)) { // allow template editing.
47891             return;
47892         }
47893 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
47894         node.removeAttribute(n);
47895         
47896     },
47897     cleanStyle : function(node,  n,v)
47898     {
47899         if (v.match(/expression/)) { //XSS?? should we even bother..
47900             node.removeAttribute(n);
47901             return;
47902         }
47903         
47904         var parts = v.split(/;/);
47905         var clean = [];
47906         
47907         Roo.each(parts, function(p) {
47908             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
47909             if (!p.length) {
47910                 return true;
47911             }
47912             var l = p.split(':').shift().replace(/\s+/g,'');
47913             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
47914             
47915             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
47916                 return true;
47917             }
47918             //Roo.log()
47919             // only allow 'c whitelisted system attributes'
47920             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
47921                 return true;
47922             }
47923             
47924             
47925             clean.push(p);
47926             return true;
47927         },this);
47928         if (clean.length) { 
47929             node.setAttribute(n, clean.join(';'));
47930         } else {
47931             node.removeAttribute(n);
47932         }
47933         
47934     }
47935         
47936         
47937         
47938     
47939 });/**
47940  * @class Roo.htmleditor.FilterBlack
47941  * remove blacklisted elements.
47942  * @constructor
47943  * Run a new Blacklisted Filter
47944  * @param {Object} config Configuration options
47945  */
47946
47947 Roo.htmleditor.FilterBlack = function(cfg)
47948 {
47949     Roo.apply(this, cfg);
47950     this.walk(cfg.node);
47951 }
47952
47953 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
47954 {
47955     tag : true, // all elements.
47956    
47957     replaceTag : function(n)
47958     {
47959         n.parentNode.removeChild(n);
47960     }
47961 });
47962 /**
47963  * @class Roo.htmleditor.FilterComment
47964  * remove comments.
47965  * @constructor
47966 * Run a new Comments Filter
47967 * @param {Object} config Configuration options
47968  */
47969 Roo.htmleditor.FilterComment = function(cfg)
47970 {
47971     this.walk(cfg.node);
47972 }
47973
47974 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
47975 {
47976   
47977     replaceComment : function(n)
47978     {
47979         n.parentNode.removeChild(n);
47980     }
47981 });/**
47982  * @class Roo.htmleditor.FilterEmpty
47983  * filter empty elements
47984  * @constructor
47985  * Run a new Empty Filter
47986  * @param {Object} config Configuration options
47987  */
47988
47989 Roo.htmleditor.FilterEmpty = function(cfg)
47990 {
47991     // no need to apply config.
47992     this.walk(cfg.node);
47993 }
47994
47995 Roo.extend(Roo.htmleditor.FilterEmpty, Roo.htmleditor.FilterBlack,
47996 {
47997      
47998     tag : true,
47999      
48000  
48001     replaceTag : function(node)
48002     {
48003         // start from leaf node
48004         if(node.hasChildNodes()) {
48005             this.walk(node);
48006         }
48007
48008         // only filter empty leaf element with certain tags
48009         if(
48010             ['B', 'I', 'U', 'S'].indexOf(node.tagName) < 0
48011             ||
48012             node.attributes && node.attributes.length > 0
48013             ||
48014             node.hasChildNodes()
48015         ) {
48016             return false; // don't walk
48017         }
48018
48019         Roo.htmleditor.FilterBlack.prototype.replaceTag.call(this, node);
48020         return false; // don't walk
48021      
48022     }
48023     
48024 });/**
48025  * @class Roo.htmleditor.FilterKeepChildren
48026  * remove tags but keep children
48027  * @constructor
48028  * Run a new Keep Children Filter
48029  * @param {Object} config Configuration options
48030  */
48031
48032 Roo.htmleditor.FilterKeepChildren = function(cfg)
48033 {
48034     Roo.apply(this, cfg);
48035     if (this.tag === false) {
48036         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
48037     }
48038     // hacky?
48039     if ((typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)) {
48040         this.cleanNamespace = true;
48041     }
48042         
48043     this.walk(cfg.node);
48044 }
48045
48046 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
48047 {
48048     cleanNamespace : false, // should really be an option, rather than using ':' inside of this tag.
48049   
48050     replaceTag : function(node)
48051     {
48052         // walk children...
48053         //Roo.log(node.tagName);
48054         var ar = Array.from(node.childNodes);
48055         //remove first..
48056         
48057         for (var i = 0; i < ar.length; i++) {
48058             var e = ar[i];
48059             if (e.nodeType == 1) {
48060                 if (
48061                     (typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1)
48062                     || // array and it matches
48063                     (typeof(this.tag) == 'string' && this.tag == e.tagName)
48064                     ||
48065                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)
48066                     ||
48067                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":")
48068                 ) {
48069                     this.replaceTag(ar[i]); // child is blacklisted as well...
48070                     continue;
48071                 }
48072             }
48073         }  
48074         ar = Array.from(node.childNodes);
48075         for (var i = 0; i < ar.length; i++) {
48076          
48077             node.removeChild(ar[i]);
48078             // what if we need to walk these???
48079             node.parentNode.insertBefore(ar[i], node);
48080             if (this.tag !== false) {
48081                 this.walk(ar[i]);
48082                 
48083             }
48084         }
48085         //Roo.log("REMOVE:" + node.tagName);
48086         node.parentNode.removeChild(node);
48087         return false; // don't walk children
48088         
48089         
48090     }
48091 });/**
48092  * @class Roo.htmleditor.FilterParagraph
48093  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
48094  * like on 'push' to remove the <p> tags and replace them with line breaks.
48095  * @constructor
48096  * Run a new Paragraph Filter
48097  * @param {Object} config Configuration options
48098  */
48099
48100 Roo.htmleditor.FilterParagraph = function(cfg)
48101 {
48102     // no need to apply config.
48103     this.searchTag(cfg.node);
48104 }
48105
48106 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
48107 {
48108     
48109      
48110     tag : 'P',
48111     
48112      
48113     replaceTag : function(node)
48114     {
48115         
48116         if (node.childNodes.length == 1 &&
48117             node.childNodes[0].nodeType == 3 &&
48118             node.childNodes[0].textContent.trim().length < 1
48119             ) {
48120             // remove and replace with '<BR>';
48121             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
48122             return false; // no need to walk..
48123         }
48124
48125         var ar = Array.from(node.childNodes);
48126         for (var i = 0; i < ar.length; i++) {
48127             node.removeChild(ar[i]);
48128             // what if we need to walk these???
48129             node.parentNode.insertBefore(ar[i], node);
48130         }
48131         // now what about this?
48132         // <p> &nbsp; </p>
48133         
48134         // double BR.
48135         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
48136         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
48137         node.parentNode.removeChild(node);
48138         
48139         return false;
48140
48141     }
48142     
48143 });/**
48144  * @class Roo.htmleditor.FilterHashLink
48145  * remove hash link
48146  * @constructor
48147  * Run a new Hash Link Filter
48148  * @param {Object} config Configuration options
48149  */
48150
48151  Roo.htmleditor.FilterHashLink = function(cfg)
48152  {
48153      // no need to apply config.
48154     //  this.walk(cfg.node);
48155     this.searchTag(cfg.node);
48156  }
48157  
48158  Roo.extend(Roo.htmleditor.FilterHashLink, Roo.htmleditor.Filter,
48159  {
48160       
48161      tag : 'A',
48162      
48163       
48164      replaceTag : function(node)
48165      {
48166          for(var i = 0; i < node.attributes.length; i ++) {
48167              var a = node.attributes[i];
48168
48169              if(a.name.toLowerCase() == 'href' && a.value.startsWith('#')) {
48170                  this.removeNodeKeepChildren(node);
48171              }
48172          }
48173          
48174          return false;
48175  
48176      }
48177      
48178  });/**
48179  * @class Roo.htmleditor.FilterSpan
48180  * filter span's with no attributes out..
48181  * @constructor
48182  * Run a new Span Filter
48183  * @param {Object} config Configuration options
48184  */
48185
48186 Roo.htmleditor.FilterSpan = function(cfg)
48187 {
48188     // no need to apply config.
48189     this.searchTag(cfg.node);
48190 }
48191
48192 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
48193 {
48194      
48195     tag : 'SPAN',
48196      
48197  
48198     replaceTag : function(node)
48199     {
48200         if (node.attributes && node.attributes.length > 0) {
48201             return true; // walk if there are any.
48202         }
48203         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
48204         return false;
48205      
48206     }
48207     
48208 });/**
48209  * @class Roo.htmleditor.FilterTableWidth
48210   try and remove table width data - as that frequently messes up other stuff.
48211  * 
48212  *      was cleanTableWidths.
48213  *
48214  * Quite often pasting from word etc.. results in tables with column and widths.
48215  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
48216  *
48217  * @constructor
48218  * Run a new Table Filter
48219  * @param {Object} config Configuration options
48220  */
48221
48222 Roo.htmleditor.FilterTableWidth = function(cfg)
48223 {
48224     // no need to apply config.
48225     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
48226     this.walk(cfg.node);
48227 }
48228
48229 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
48230 {
48231      
48232      
48233     
48234     replaceTag: function(node) {
48235         
48236         
48237       
48238         if (node.hasAttribute('width')) {
48239             node.removeAttribute('width');
48240         }
48241         
48242          
48243         if (node.hasAttribute("style")) {
48244             // pretty basic...
48245             
48246             var styles = node.getAttribute("style").split(";");
48247             var nstyle = [];
48248             Roo.each(styles, function(s) {
48249                 if (!s.match(/:/)) {
48250                     return;
48251                 }
48252                 var kv = s.split(":");
48253                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
48254                     return;
48255                 }
48256                 // what ever is left... we allow.
48257                 nstyle.push(s);
48258             });
48259             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
48260             if (!nstyle.length) {
48261                 node.removeAttribute('style');
48262             }
48263         }
48264         
48265         return true; // continue doing children..
48266     }
48267 });/**
48268  * @class Roo.htmleditor.FilterWord
48269  * try and clean up all the mess that Word generates.
48270  * 
48271  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
48272  
48273  * @constructor
48274  * Run a new Span Filter
48275  * @param {Object} config Configuration options
48276  */
48277
48278 Roo.htmleditor.FilterWord = function(cfg)
48279 {
48280     // no need to apply config.
48281     this.replaceDocBullets(cfg.node);
48282     
48283     this.replaceAname(cfg.node);
48284     // this is disabled as the removal is done by other filters;
48285    // this.walk(cfg.node);
48286     this.replaceImageTable(cfg.node);
48287     
48288 }
48289
48290 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
48291 {
48292     tag: true,
48293      
48294     
48295     /**
48296      * Clean up MS wordisms...
48297      */
48298     replaceTag : function(node)
48299     {
48300          
48301         // no idea what this does - span with text, replaceds with just text.
48302         if(
48303                 node.nodeName == 'SPAN' &&
48304                 !node.hasAttributes() &&
48305                 node.childNodes.length == 1 &&
48306                 node.firstChild.nodeName == "#text"  
48307         ) {
48308             var textNode = node.firstChild;
48309             node.removeChild(textNode);
48310             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
48311                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
48312             }
48313             node.parentNode.insertBefore(textNode, node);
48314             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
48315                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
48316             }
48317             
48318             node.parentNode.removeChild(node);
48319             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
48320         }
48321         
48322    
48323         
48324         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
48325             node.parentNode.removeChild(node);
48326             return false; // dont do chidlren
48327         }
48328         //Roo.log(node.tagName);
48329         // remove - but keep children..
48330         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
48331             //Roo.log('-- removed');
48332             while (node.childNodes.length) {
48333                 var cn = node.childNodes[0];
48334                 node.removeChild(cn);
48335                 node.parentNode.insertBefore(cn, node);
48336                 // move node to parent - and clean it..
48337                 if (cn.nodeType == 1) {
48338                     this.replaceTag(cn);
48339                 }
48340                 
48341             }
48342             node.parentNode.removeChild(node);
48343             /// no need to iterate chidlren = it's got none..
48344             //this.iterateChildren(node, this.cleanWord);
48345             return false; // no need to iterate children.
48346         }
48347         // clean styles
48348         if (node.className.length) {
48349             
48350             var cn = node.className.split(/\W+/);
48351             var cna = [];
48352             Roo.each(cn, function(cls) {
48353                 if (cls.match(/Mso[a-zA-Z]+/)) {
48354                     return;
48355                 }
48356                 cna.push(cls);
48357             });
48358             node.className = cna.length ? cna.join(' ') : '';
48359             if (!cna.length) {
48360                 node.removeAttribute("class");
48361             }
48362         }
48363         
48364         if (node.hasAttribute("lang")) {
48365             node.removeAttribute("lang");
48366         }
48367         
48368         if (node.hasAttribute("style")) {
48369             
48370             var styles = node.getAttribute("style").split(";");
48371             var nstyle = [];
48372             Roo.each(styles, function(s) {
48373                 if (!s.match(/:/)) {
48374                     return;
48375                 }
48376                 var kv = s.split(":");
48377                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
48378                     return;
48379                 }
48380                 // what ever is left... we allow.
48381                 nstyle.push(s);
48382             });
48383             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
48384             if (!nstyle.length) {
48385                 node.removeAttribute('style');
48386             }
48387         }
48388         return true; // do children
48389         
48390         
48391         
48392     },
48393     
48394     styleToObject: function(node)
48395     {
48396         var styles = (node.getAttribute("style") || '').split(";");
48397         var ret = {};
48398         Roo.each(styles, function(s) {
48399             if (!s.match(/:/)) {
48400                 return;
48401             }
48402             var kv = s.split(":");
48403              
48404             // what ever is left... we allow.
48405             ret[kv[0].trim()] = kv[1];
48406         });
48407         return ret;
48408     },
48409     
48410     
48411     replaceAname : function (doc)
48412     {
48413         // replace all the a/name without..
48414         var aa = Array.from(doc.getElementsByTagName('a'));
48415         for (var i = 0; i  < aa.length; i++) {
48416             var a = aa[i];
48417             if (a.hasAttribute("name")) {
48418                 a.removeAttribute("name");
48419             }
48420             if (a.hasAttribute("href")) {
48421                 continue;
48422             }
48423             // reparent children.
48424             this.removeNodeKeepChildren(a);
48425             
48426         }
48427         
48428         
48429         
48430     },
48431
48432     
48433     
48434     replaceDocBullets : function(doc)
48435     {
48436         // this is a bit odd - but it appears some indents use ql-indent-1
48437          //Roo.log(doc.innerHTML);
48438         
48439         var listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpFirst'));
48440         for( var i = 0; i < listpara.length; i ++) {
48441             listpara[i].className = "MsoListParagraph";
48442         }
48443         
48444         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpMiddle'));
48445         for( var i = 0; i < listpara.length; i ++) {
48446             listpara[i].className = "MsoListParagraph";
48447         }
48448         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpLast'));
48449         for( var i = 0; i < listpara.length; i ++) {
48450             listpara[i].className = "MsoListParagraph";
48451         }
48452         listpara =  Array.from(doc.getElementsByClassName('ql-indent-1'));
48453         for( var i = 0; i < listpara.length; i ++) {
48454             listpara[i].className = "MsoListParagraph";
48455         }
48456         
48457         // this is a bit hacky - we had one word document where h2 had a miso-list attribute.
48458         var htwo =  Array.from(doc.getElementsByTagName('h2'));
48459         for( var i = 0; i < htwo.length; i ++) {
48460             if (htwo[i].hasAttribute('style') && htwo[i].getAttribute('style').match(/mso-list:/)) {
48461                 htwo[i].className = "MsoListParagraph";
48462             }
48463         }
48464         listpara =  Array.from(doc.getElementsByClassName('MsoNormal'));
48465         for( var i = 0; i < listpara.length; i ++) {
48466             if (listpara[i].hasAttribute('style') && listpara[i].getAttribute('style').match(/mso-list:/)) {
48467                 listpara[i].className = "MsoListParagraph";
48468             } else {
48469                 listpara[i].className = "MsoNormalx";
48470             }
48471         }
48472        
48473         listpara = doc.getElementsByClassName('MsoListParagraph');
48474         // Roo.log(doc.innerHTML);
48475         
48476         
48477         
48478         while(listpara.length) {
48479             
48480             this.replaceDocBullet(listpara.item(0));
48481         }
48482       
48483     },
48484     
48485      
48486     
48487     replaceDocBullet : function(p)
48488     {
48489         // gather all the siblings.
48490         var ns = p,
48491             parent = p.parentNode,
48492             doc = parent.ownerDocument,
48493             items = [];
48494          
48495         //Roo.log("Parsing: " + p.innerText)    ;
48496         var listtype = 'ul';   
48497         while (ns) {
48498             if (ns.nodeType != 1) {
48499                 ns = ns.nextSibling;
48500                 continue;
48501             }
48502             if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
48503                 //Roo.log("Missing para r q1indent - got:" + ns.className);
48504                 break;
48505             }
48506             var spans = ns.getElementsByTagName('span');
48507             
48508             if (ns.hasAttribute('style') && ns.getAttribute('style').match(/mso-list/)) {
48509                 items.push(ns);
48510                 ns = ns.nextSibling;
48511                 has_list = true;
48512                 if (!spans.length) {
48513                     continue;
48514                 }
48515                 var ff = '';
48516                 var se = spans[0];
48517                 for (var i = 0; i < spans.length;i++) {
48518                     se = spans[i];
48519                     if (se.hasAttribute('style')  && se.hasAttribute('style') && se.style.fontFamily != '') {
48520                         ff = se.style.fontFamily;
48521                         break;
48522                     }
48523                 }
48524                  
48525                     
48526                 //Roo.log("got font family: " + ff);
48527                 if (typeof(ff) != 'undefined' && !ff.match(/(Symbol|Wingdings)/) && "·o".indexOf(se.innerText.trim()) < 0) {
48528                     listtype = 'ol';
48529                 }
48530                 
48531                 continue;
48532             }
48533             //Roo.log("no mso-list?");
48534             
48535             var spans = ns.getElementsByTagName('span');
48536             if (!spans.length) {
48537                 break;
48538             }
48539             var has_list  = false;
48540             for(var i = 0; i < spans.length; i++) {
48541                 if (spans[i].hasAttribute('style') && spans[i].getAttribute('style').match(/mso-list/)) {
48542                     has_list = true;
48543                     break;
48544                 }
48545             }
48546             if (!has_list) {
48547                 break;
48548             }
48549             items.push(ns);
48550             ns = ns.nextSibling;
48551             
48552             
48553         }
48554         if (!items.length) {
48555             ns.className = "";
48556             return;
48557         }
48558         
48559         var ul = parent.ownerDocument.createElement(listtype); // what about number lists...
48560         parent.insertBefore(ul, p);
48561         var lvl = 0;
48562         var stack = [ ul ];
48563         var last_li = false;
48564         
48565         var margin_to_depth = {};
48566         max_margins = -1;
48567         
48568         items.forEach(function(n, ipos) {
48569             //Roo.log("got innertHMLT=" + n.innerHTML);
48570             
48571             var spans = n.getElementsByTagName('span');
48572             if (!spans.length) {
48573                 //Roo.log("No spans found");
48574                  
48575                 parent.removeChild(n);
48576                 
48577                 
48578                 return; // skip it...
48579             }
48580            
48581                 
48582             var num = 1;
48583             var style = {};
48584             for(var i = 0; i < spans.length; i++) {
48585             
48586                 style = this.styleToObject(spans[i]);
48587                 if (typeof(style['mso-list']) == 'undefined') {
48588                     continue;
48589                 }
48590                 if (listtype == 'ol') {
48591                    num = spans[i].innerText.replace(/[^0-9]+]/g,'')  * 1;
48592                 }
48593                 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
48594                 break;
48595             }
48596             //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
48597             style = this.styleToObject(n); // mo-list is from the parent node.
48598             if (typeof(style['mso-list']) == 'undefined') {
48599                 //Roo.log("parent is missing level");
48600                   
48601                 parent.removeChild(n);
48602                  
48603                 return;
48604             }
48605             
48606             var margin = style['margin-left'];
48607             if (typeof(margin_to_depth[margin]) == 'undefined') {
48608                 max_margins++;
48609                 margin_to_depth[margin] = max_margins;
48610             }
48611             nlvl = margin_to_depth[margin] ;
48612              
48613             if (nlvl > lvl) {
48614                 //new indent
48615                 var nul = doc.createElement(listtype); // what about number lists...
48616                 if (!last_li) {
48617                     last_li = doc.createElement('li');
48618                     stack[lvl].appendChild(last_li);
48619                 }
48620                 last_li.appendChild(nul);
48621                 stack[nlvl] = nul;
48622                 
48623             }
48624             lvl = nlvl;
48625             
48626             // not starting at 1..
48627             if (!stack[nlvl].hasAttribute("start") && listtype == "ol") {
48628                 stack[nlvl].setAttribute("start", num);
48629             }
48630             
48631             var nli = stack[nlvl].appendChild(doc.createElement('li'));
48632             last_li = nli;
48633             nli.innerHTML = n.innerHTML;
48634             //Roo.log("innerHTML = " + n.innerHTML);
48635             parent.removeChild(n);
48636             
48637              
48638              
48639             
48640         },this);
48641         
48642         
48643         
48644         
48645     },
48646     
48647     replaceImageTable : function(doc)
48648     {
48649          /*
48650           <table cellpadding=0 cellspacing=0 align=left>
48651   <tr>
48652    <td width=423 height=0></td>
48653   </tr>
48654   <tr>
48655    <td></td>
48656    <td><img width=601 height=401
48657    src="file:///C:/Users/Alan/AppData/Local/Temp/msohtmlclip1/01/clip_image002.jpg"
48658    v:shapes="Picture_x0020_2"></td>
48659   </tr>
48660  </table>
48661  */
48662         var imgs = Array.from(doc.getElementsByTagName('img'));
48663         Roo.each(imgs, function(img) {
48664             var td = img.parentNode;
48665             if (td.nodeName !=  'TD') {
48666                 return;
48667             }
48668             var tr = td.parentNode;
48669             if (tr.nodeName !=  'TR') {
48670                 return;
48671             }
48672             var tbody = tr.parentNode;
48673             if (tbody.nodeName !=  'TBODY') {
48674                 return;
48675             }
48676             var table = tbody.parentNode;
48677             if (table.nodeName !=  'TABLE') {
48678                 return;
48679             }
48680             // first row..
48681             
48682             if (table.getElementsByTagName('tr').length != 2) {
48683                 return;
48684             }
48685             if (table.getElementsByTagName('td').length != 3) {
48686                 return;
48687             }
48688             if (table.innerText.trim() != '') {
48689                 return;
48690             }
48691             var p = table.parentNode;
48692             img.parentNode.removeChild(img);
48693             p.insertBefore(img, table);
48694             p.removeChild(table);
48695             
48696             
48697             
48698         });
48699         
48700       
48701     }
48702     
48703 });
48704 /**
48705  * @class Roo.htmleditor.FilterStyleToTag
48706  * part of the word stuff... - certain 'styles' should be converted to tags.
48707  * eg.
48708  *   font-weight: bold -> bold
48709  *   ?? super / subscrit etc..
48710  * 
48711  * @constructor
48712 * Run a new style to tag filter.
48713 * @param {Object} config Configuration options
48714  */
48715 Roo.htmleditor.FilterStyleToTag = function(cfg)
48716 {
48717     
48718     this.tags = {
48719         B  : [ 'fontWeight' , 'bold', 'font-weight'],
48720         I :  [ 'fontStyle' , 'italic', 'font-style'],
48721         //pre :  [ 'font-style' , 'italic'],
48722         // h1.. h6 ?? font-size?
48723         SUP : [ 'verticalAlign' , 'super', 'vertical-align'],
48724         SUB : [ 'verticalAlign' , 'sub', 'vertical-align']
48725         
48726         
48727     };
48728     
48729     Roo.apply(this, cfg);
48730      
48731     
48732     this.walk(cfg.node);
48733     
48734     
48735     
48736 }
48737
48738
48739 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
48740 {
48741     tag: true, // all tags
48742     
48743     tags : false,
48744     
48745     
48746     replaceTag : function(node)
48747     {
48748         
48749         
48750         if (node.getAttribute("style") === null) {
48751             return true;
48752         }
48753         var inject = [];
48754         for (var k in this.tags) {
48755             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
48756                 inject.push(k);
48757                 node.style.removeProperty(this.tags[k][2]);
48758             }
48759         }
48760         if (!inject.length) {
48761             return true; 
48762         }
48763         var cn = Array.from(node.childNodes);
48764         var nn = node;
48765         Roo.each(inject, function(t) {
48766             var nc = node.ownerDocument.createElement(t);
48767             nn.appendChild(nc);
48768             nn = nc;
48769         });
48770         for(var i = 0;i < cn.length;i++) {
48771             node.removeChild(cn[i]);
48772             nn.appendChild(cn[i]);
48773         }
48774         return true /// iterate thru
48775     }
48776     
48777 })/**
48778  * @class Roo.htmleditor.FilterLongBr
48779  * BR/BR/BR - keep a maximum of 2...
48780  * @constructor
48781  * Run a new Long BR Filter
48782  * @param {Object} config Configuration options
48783  */
48784
48785 Roo.htmleditor.FilterLongBr = function(cfg)
48786 {
48787     // no need to apply config.
48788     this.searchTag(cfg.node);
48789 }
48790
48791 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
48792 {
48793     
48794      
48795     tag : 'BR',
48796     
48797      
48798     replaceTag : function(node)
48799     {
48800         
48801         var ps = node.nextSibling;
48802         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
48803             ps = ps.nextSibling;
48804         }
48805         
48806         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
48807             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
48808             return false;
48809         }
48810         
48811         if (!ps || ps.nodeType != 1) {
48812             return false;
48813         }
48814         
48815         if (!ps || ps.tagName != 'BR') {
48816            
48817             return false;
48818         }
48819         
48820         
48821         
48822         if (!node.previousSibling) {
48823             return false;
48824         }
48825         var ps = node.previousSibling;
48826         
48827         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
48828             ps = ps.previousSibling;
48829         }
48830         if (!ps || ps.nodeType != 1) {
48831             return false;
48832         }
48833         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
48834         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
48835             return false;
48836         }
48837         
48838         node.parentNode.removeChild(node); // remove me...
48839         
48840         return false; // no need to do children
48841
48842     }
48843     
48844 }); 
48845
48846 /**
48847  * @class Roo.htmleditor.FilterBlock
48848  * removes id / data-block and contenteditable that are associated with blocks
48849  * usage should be done on a cloned copy of the dom
48850  * @constructor
48851 * Run a new Attribute Filter { node : xxxx }}
48852 * @param {Object} config Configuration options
48853  */
48854 Roo.htmleditor.FilterBlock = function(cfg)
48855 {
48856     Roo.apply(this, cfg);
48857     var qa = cfg.node.querySelectorAll;
48858     this.removeAttributes('data-block');
48859     this.removeAttributes('contenteditable');
48860     this.removeAttributes('id');
48861     
48862 }
48863
48864 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
48865 {
48866     node: true, // all tags
48867      
48868      
48869     removeAttributes : function(attr)
48870     {
48871         var ar = this.node.querySelectorAll('*[' + attr + ']');
48872         for (var i =0;i<ar.length;i++) {
48873             ar[i].removeAttribute(attr);
48874         }
48875     }
48876         
48877         
48878         
48879     
48880 });
48881 /***
48882  * This is based loosely on tinymce 
48883  * @class Roo.htmleditor.TidySerializer
48884  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
48885  * @constructor
48886  * @method Serializer
48887  * @param {Object} settings Name/value settings object.
48888  */
48889
48890
48891 Roo.htmleditor.TidySerializer = function(settings)
48892 {
48893     Roo.apply(this, settings);
48894     
48895     this.writer = new Roo.htmleditor.TidyWriter(settings);
48896     
48897     
48898
48899 };
48900 Roo.htmleditor.TidySerializer.prototype = {
48901     
48902     /**
48903      * @param {boolean} inner do the inner of the node.
48904      */
48905     inner : false,
48906     
48907     writer : false,
48908     
48909     /**
48910     * Serializes the specified node into a string.
48911     *
48912     * @example
48913     * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
48914     * @method serialize
48915     * @param {DomElement} node Node instance to serialize.
48916     * @return {String} String with HTML based on DOM tree.
48917     */
48918     serialize : function(node) {
48919         
48920         // = settings.validate;
48921         var writer = this.writer;
48922         var self  = this;
48923         this.handlers = {
48924             // #text
48925             3: function(node) {
48926                 
48927                 writer.text(node.nodeValue, node);
48928             },
48929             // #comment
48930             8: function(node) {
48931                 writer.comment(node.nodeValue);
48932             },
48933             // Processing instruction
48934             7: function(node) {
48935                 writer.pi(node.name, node.nodeValue);
48936             },
48937             // Doctype
48938             10: function(node) {
48939                 writer.doctype(node.nodeValue);
48940             },
48941             // CDATA
48942             4: function(node) {
48943                 writer.cdata(node.nodeValue);
48944             },
48945             // Document fragment
48946             11: function(node) {
48947                 node = node.firstChild;
48948                 if (!node) {
48949                     return;
48950                 }
48951                 while(node) {
48952                     self.walk(node);
48953                     node = node.nextSibling
48954                 }
48955             }
48956         };
48957         writer.reset();
48958         1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
48959         return writer.getContent();
48960     },
48961
48962     walk: function(node)
48963     {
48964         var attrName, attrValue, sortedAttrs, i, l, elementRule,
48965             handler = this.handlers[node.nodeType];
48966             
48967         if (handler) {
48968             handler(node);
48969             return;
48970         }
48971     
48972         var name = node.nodeName;
48973         var isEmpty = node.childNodes.length < 1;
48974       
48975         var writer = this.writer;
48976         var attrs = node.attributes;
48977         // Sort attributes
48978         
48979         writer.start(node.nodeName, attrs, isEmpty, node);
48980         if (isEmpty) {
48981             return;
48982         }
48983         node = node.firstChild;
48984         if (!node) {
48985             writer.end(name);
48986             return;
48987         }
48988         while (node) {
48989             this.walk(node);
48990             node = node.nextSibling;
48991         }
48992         writer.end(name);
48993         
48994     
48995     }
48996     // Serialize element and treat all non elements as fragments
48997    
48998 }; 
48999
49000 /***
49001  * This is based loosely on tinymce 
49002  * @class Roo.htmleditor.TidyWriter
49003  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
49004  *
49005  * Known issues?
49006  * - not tested much with 'PRE' formated elements.
49007  * 
49008  *
49009  *
49010  */
49011
49012 Roo.htmleditor.TidyWriter = function(settings)
49013 {
49014     
49015     // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
49016     Roo.apply(this, settings);
49017     this.html = [];
49018     this.state = [];
49019      
49020     this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
49021   
49022 }
49023 Roo.htmleditor.TidyWriter.prototype = {
49024
49025  
49026     state : false,
49027     
49028     indent :  '  ',
49029     
49030     // part of state...
49031     indentstr : '',
49032     in_pre: false,
49033     in_inline : false,
49034     last_inline : false,
49035     encode : false,
49036      
49037     
49038             /**
49039     * Writes the a start element such as <p id="a">.
49040     *
49041     * @method start
49042     * @param {String} name Name of the element.
49043     * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
49044     * @param {Boolean} empty Optional empty state if the tag should end like <br />.
49045     */
49046     start: function(name, attrs, empty, node)
49047     {
49048         var i, l, attr, value;
49049         
49050         // there are some situations where adding line break && indentation will not work. will not work.
49051         // <span / b / i ... formating?
49052         
49053         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
49054         var in_pre    = this.in_pre    || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
49055         
49056         var is_short   = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
49057         
49058         var add_lb = name == 'BR' ? false : in_inline;
49059         
49060         if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
49061             i_inline = false;
49062         }
49063
49064         var indentstr =  this.indentstr;
49065         
49066         // e_inline = elements that can be inline, but still allow \n before and after?
49067         // only 'BR' ??? any others?
49068         
49069         // ADD LINE BEFORE tage
49070         if (!this.in_pre) {
49071             if (in_inline) {
49072                 //code
49073                 if (name == 'BR') {
49074                     this.addLine();
49075                 } else if (this.lastElementEndsWS()) {
49076                     this.addLine();
49077                 } else{
49078                     // otherwise - no new line. (and dont indent.)
49079                     indentstr = '';
49080                 }
49081                 
49082             } else {
49083                 this.addLine();
49084             }
49085         } else {
49086             indentstr = '';
49087         }
49088         
49089         this.html.push(indentstr + '<', name.toLowerCase());
49090         
49091         if (attrs) {
49092             for (i = 0, l = attrs.length; i < l; i++) {
49093                 attr = attrs[i];
49094                 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
49095             }
49096         }
49097      
49098         if (empty) {
49099             if (is_short) {
49100                 this.html[this.html.length] = '/>';
49101             } else {
49102                 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
49103             }
49104             var e_inline = name == 'BR' ? false : this.in_inline;
49105             
49106             if (!e_inline && !this.in_pre) {
49107                 this.addLine();
49108             }
49109             return;
49110         
49111         }
49112         // not empty..
49113         this.html[this.html.length] = '>';
49114         
49115         // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
49116         /*
49117         if (!in_inline && !in_pre) {
49118             var cn = node.firstChild;
49119             while(cn) {
49120                 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
49121                     in_inline = true
49122                     break;
49123                 }
49124                 cn = cn.nextSibling;
49125             }
49126              
49127         }
49128         */
49129         
49130         
49131         this.pushState({
49132             indentstr : in_pre   ? '' : (this.indentstr + this.indent),
49133             in_pre : in_pre,
49134             in_inline :  in_inline
49135         });
49136         // add a line after if we are not in a
49137         
49138         if (!in_inline && !in_pre) {
49139             this.addLine();
49140         }
49141         
49142             
49143          
49144         
49145     },
49146     
49147     lastElementEndsWS : function()
49148     {
49149         var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
49150         if (value === false) {
49151             return true;
49152         }
49153         return value.match(/\s+$/);
49154         
49155     },
49156     
49157     /**
49158      * Writes the a end element such as </p>.
49159      *
49160      * @method end
49161      * @param {String} name Name of the element.
49162      */
49163     end: function(name) {
49164         var value;
49165         this.popState();
49166         var indentstr = '';
49167         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
49168         
49169         if (!this.in_pre && !in_inline) {
49170             this.addLine();
49171             indentstr  = this.indentstr;
49172         }
49173         this.html.push(indentstr + '</', name.toLowerCase(), '>');
49174         this.last_inline = in_inline;
49175         
49176         // pop the indent state..
49177     },
49178     /**
49179      * Writes a text node.
49180      *
49181      * In pre - we should not mess with the contents.
49182      * 
49183      *
49184      * @method text
49185      * @param {String} text String to write out.
49186      * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
49187      */
49188     text: function(in_text, node)
49189     {
49190         // if not in whitespace critical
49191         if (in_text.length < 1) {
49192             return;
49193         }
49194         var text = new XMLSerializer().serializeToString(document.createTextNode(in_text)); // escape it properly?
49195         
49196         if (this.in_pre) {
49197             this.html[this.html.length] =  text;
49198             return;   
49199         }
49200         
49201         if (this.in_inline) {
49202             text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
49203             if (text != ' ') {
49204                 text = text.replace(/\s+/,' ');  // all white space to single white space
49205                 
49206                     
49207                 // if next tag is '<BR>', then we can trim right..
49208                 if (node.nextSibling &&
49209                     node.nextSibling.nodeType == 1 &&
49210                     node.nextSibling.nodeName == 'BR' )
49211                 {
49212                     text = text.replace(/\s+$/g,'');
49213                 }
49214                 // if previous tag was a BR, we can also trim..
49215                 if (node.previousSibling &&
49216                     node.previousSibling.nodeType == 1 &&
49217                     node.previousSibling.nodeName == 'BR' )
49218                 {
49219                     text = this.indentstr +  text.replace(/^\s+/g,'');
49220                 }
49221                 if (text.match(/\n/)) {
49222                     text = text.replace(
49223                         /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
49224                     );
49225                     // remoeve the last whitespace / line break.
49226                     text = text.replace(/\n\s+$/,'');
49227                 }
49228                 // repace long lines
49229                 
49230             }
49231              
49232             this.html[this.html.length] =  text;
49233             return;   
49234         }
49235         // see if previous element was a inline element.
49236         var indentstr = this.indentstr;
49237    
49238         text = text.replace(/\s+/g," "); // all whitespace into single white space.
49239         
49240         // should trim left?
49241         if (node.previousSibling &&
49242             node.previousSibling.nodeType == 1 &&
49243             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
49244         {
49245             indentstr = '';
49246             
49247         } else {
49248             this.addLine();
49249             text = text.replace(/^\s+/,''); // trim left
49250           
49251         }
49252         // should trim right?
49253         if (node.nextSibling &&
49254             node.nextSibling.nodeType == 1 &&
49255             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
49256         {
49257           // noop
49258             
49259         }  else {
49260             text = text.replace(/\s+$/,''); // trim right
49261         }
49262          
49263               
49264         
49265         
49266         
49267         if (text.length < 1) {
49268             return;
49269         }
49270         if (!text.match(/\n/)) {
49271             this.html.push(indentstr + text);
49272             return;
49273         }
49274         
49275         text = this.indentstr + text.replace(
49276             /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
49277         );
49278         // remoeve the last whitespace / line break.
49279         text = text.replace(/\s+$/,''); 
49280         
49281         this.html.push(text);
49282         
49283         // split and indent..
49284         
49285         
49286     },
49287     /**
49288      * Writes a cdata node such as <![CDATA[data]]>.
49289      *
49290      * @method cdata
49291      * @param {String} text String to write out inside the cdata.
49292      */
49293     cdata: function(text) {
49294         this.html.push('<![CDATA[', text, ']]>');
49295     },
49296     /**
49297     * Writes a comment node such as <!-- Comment -->.
49298     *
49299     * @method cdata
49300     * @param {String} text String to write out inside the comment.
49301     */
49302    comment: function(text) {
49303        this.html.push('<!--', text, '-->');
49304    },
49305     /**
49306      * Writes a PI node such as <?xml attr="value" ?>.
49307      *
49308      * @method pi
49309      * @param {String} name Name of the pi.
49310      * @param {String} text String to write out inside the pi.
49311      */
49312     pi: function(name, text) {
49313         text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
49314         this.indent != '' && this.html.push('\n');
49315     },
49316     /**
49317      * Writes a doctype node such as <!DOCTYPE data>.
49318      *
49319      * @method doctype
49320      * @param {String} text String to write out inside the doctype.
49321      */
49322     doctype: function(text) {
49323         this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
49324     },
49325     /**
49326      * Resets the internal buffer if one wants to reuse the writer.
49327      *
49328      * @method reset
49329      */
49330     reset: function() {
49331         this.html.length = 0;
49332         this.state = [];
49333         this.pushState({
49334             indentstr : '',
49335             in_pre : false, 
49336             in_inline : false
49337         })
49338     },
49339     /**
49340      * Returns the contents that got serialized.
49341      *
49342      * @method getContent
49343      * @return {String} HTML contents that got written down.
49344      */
49345     getContent: function() {
49346         return this.html.join('').replace(/\n$/, '');
49347     },
49348     
49349     pushState : function(cfg)
49350     {
49351         this.state.push(cfg);
49352         Roo.apply(this, cfg);
49353     },
49354     
49355     popState : function()
49356     {
49357         if (this.state.length < 1) {
49358             return; // nothing to push
49359         }
49360         var cfg = {
49361             in_pre: false,
49362             indentstr : ''
49363         };
49364         this.state.pop();
49365         if (this.state.length > 0) {
49366             cfg = this.state[this.state.length-1]; 
49367         }
49368         Roo.apply(this, cfg);
49369     },
49370     
49371     addLine: function()
49372     {
49373         if (this.html.length < 1) {
49374             return;
49375         }
49376         
49377         
49378         var value = this.html[this.html.length - 1];
49379         if (value.length > 0 && '\n' !== value) {
49380             this.html.push('\n');
49381         }
49382     }
49383     
49384     
49385 //'pre script noscript style textarea video audio iframe object code'
49386 // shortended... 'area base basefont br col frame hr img input isindex link  meta param embed source wbr track');
49387 // inline 
49388 };
49389
49390 Roo.htmleditor.TidyWriter.inline_elements = [
49391         'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
49392         'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
49393 ];
49394 Roo.htmleditor.TidyWriter.shortend_elements = [
49395     'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
49396     'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
49397 ];
49398
49399 Roo.htmleditor.TidyWriter.whitespace_elements = [
49400     'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
49401 ];/***
49402  * This is based loosely on tinymce 
49403  * @class Roo.htmleditor.TidyEntities
49404  * @static
49405  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
49406  *
49407  * Not 100% sure this is actually used or needed.
49408  */
49409
49410 Roo.htmleditor.TidyEntities = {
49411     
49412     /**
49413      * initialize data..
49414      */
49415     init : function (){
49416      
49417         this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
49418        
49419     },
49420
49421
49422     buildEntitiesLookup: function(items, radix) {
49423         var i, chr, entity, lookup = {};
49424         if (!items) {
49425             return {};
49426         }
49427         items = typeof(items) == 'string' ? items.split(',') : items;
49428         radix = radix || 10;
49429         // Build entities lookup table
49430         for (i = 0; i < items.length; i += 2) {
49431             chr = String.fromCharCode(parseInt(items[i], radix));
49432             // Only add non base entities
49433             if (!this.baseEntities[chr]) {
49434                 entity = '&' + items[i + 1] + ';';
49435                 lookup[chr] = entity;
49436                 lookup[entity] = chr;
49437             }
49438         }
49439         return lookup;
49440         
49441     },
49442     
49443     asciiMap : {
49444             128: '€',
49445             130: '‚',
49446             131: 'ƒ',
49447             132: '„',
49448             133: '…',
49449             134: '†',
49450             135: '‡',
49451             136: 'ˆ',
49452             137: '‰',
49453             138: 'Š',
49454             139: '‹',
49455             140: 'Œ',
49456             142: 'Ž',
49457             145: '‘',
49458             146: '’',
49459             147: '“',
49460             148: '”',
49461             149: '•',
49462             150: '–',
49463             151: '—',
49464             152: '˜',
49465             153: '™',
49466             154: 'š',
49467             155: '›',
49468             156: 'œ',
49469             158: 'ž',
49470             159: 'Ÿ'
49471     },
49472     // Raw entities
49473     baseEntities : {
49474         '"': '&quot;',
49475         // Needs to be escaped since the YUI compressor would otherwise break the code
49476         '\'': '&#39;',
49477         '<': '&lt;',
49478         '>': '&gt;',
49479         '&': '&amp;',
49480         '`': '&#96;'
49481     },
49482     // Reverse lookup table for raw entities
49483     reverseEntities : {
49484         '&lt;': '<',
49485         '&gt;': '>',
49486         '&amp;': '&',
49487         '&quot;': '"',
49488         '&apos;': '\''
49489     },
49490     
49491     attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
49492     textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
49493     rawCharsRegExp : /[<>&\"\']/g,
49494     entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
49495     namedEntities  : false,
49496     namedEntitiesData : [ 
49497         '50',
49498         'nbsp',
49499         '51',
49500         'iexcl',
49501         '52',
49502         'cent',
49503         '53',
49504         'pound',
49505         '54',
49506         'curren',
49507         '55',
49508         'yen',
49509         '56',
49510         'brvbar',
49511         '57',
49512         'sect',
49513         '58',
49514         'uml',
49515         '59',
49516         'copy',
49517         '5a',
49518         'ordf',
49519         '5b',
49520         'laquo',
49521         '5c',
49522         'not',
49523         '5d',
49524         'shy',
49525         '5e',
49526         'reg',
49527         '5f',
49528         'macr',
49529         '5g',
49530         'deg',
49531         '5h',
49532         'plusmn',
49533         '5i',
49534         'sup2',
49535         '5j',
49536         'sup3',
49537         '5k',
49538         'acute',
49539         '5l',
49540         'micro',
49541         '5m',
49542         'para',
49543         '5n',
49544         'middot',
49545         '5o',
49546         'cedil',
49547         '5p',
49548         'sup1',
49549         '5q',
49550         'ordm',
49551         '5r',
49552         'raquo',
49553         '5s',
49554         'frac14',
49555         '5t',
49556         'frac12',
49557         '5u',
49558         'frac34',
49559         '5v',
49560         'iquest',
49561         '60',
49562         'Agrave',
49563         '61',
49564         'Aacute',
49565         '62',
49566         'Acirc',
49567         '63',
49568         'Atilde',
49569         '64',
49570         'Auml',
49571         '65',
49572         'Aring',
49573         '66',
49574         'AElig',
49575         '67',
49576         'Ccedil',
49577         '68',
49578         'Egrave',
49579         '69',
49580         'Eacute',
49581         '6a',
49582         'Ecirc',
49583         '6b',
49584         'Euml',
49585         '6c',
49586         'Igrave',
49587         '6d',
49588         'Iacute',
49589         '6e',
49590         'Icirc',
49591         '6f',
49592         'Iuml',
49593         '6g',
49594         'ETH',
49595         '6h',
49596         'Ntilde',
49597         '6i',
49598         'Ograve',
49599         '6j',
49600         'Oacute',
49601         '6k',
49602         'Ocirc',
49603         '6l',
49604         'Otilde',
49605         '6m',
49606         'Ouml',
49607         '6n',
49608         'times',
49609         '6o',
49610         'Oslash',
49611         '6p',
49612         'Ugrave',
49613         '6q',
49614         'Uacute',
49615         '6r',
49616         'Ucirc',
49617         '6s',
49618         'Uuml',
49619         '6t',
49620         'Yacute',
49621         '6u',
49622         'THORN',
49623         '6v',
49624         'szlig',
49625         '70',
49626         'agrave',
49627         '71',
49628         'aacute',
49629         '72',
49630         'acirc',
49631         '73',
49632         'atilde',
49633         '74',
49634         'auml',
49635         '75',
49636         'aring',
49637         '76',
49638         'aelig',
49639         '77',
49640         'ccedil',
49641         '78',
49642         'egrave',
49643         '79',
49644         'eacute',
49645         '7a',
49646         'ecirc',
49647         '7b',
49648         'euml',
49649         '7c',
49650         'igrave',
49651         '7d',
49652         'iacute',
49653         '7e',
49654         'icirc',
49655         '7f',
49656         'iuml',
49657         '7g',
49658         'eth',
49659         '7h',
49660         'ntilde',
49661         '7i',
49662         'ograve',
49663         '7j',
49664         'oacute',
49665         '7k',
49666         'ocirc',
49667         '7l',
49668         'otilde',
49669         '7m',
49670         'ouml',
49671         '7n',
49672         'divide',
49673         '7o',
49674         'oslash',
49675         '7p',
49676         'ugrave',
49677         '7q',
49678         'uacute',
49679         '7r',
49680         'ucirc',
49681         '7s',
49682         'uuml',
49683         '7t',
49684         'yacute',
49685         '7u',
49686         'thorn',
49687         '7v',
49688         'yuml',
49689         'ci',
49690         'fnof',
49691         'sh',
49692         'Alpha',
49693         'si',
49694         'Beta',
49695         'sj',
49696         'Gamma',
49697         'sk',
49698         'Delta',
49699         'sl',
49700         'Epsilon',
49701         'sm',
49702         'Zeta',
49703         'sn',
49704         'Eta',
49705         'so',
49706         'Theta',
49707         'sp',
49708         'Iota',
49709         'sq',
49710         'Kappa',
49711         'sr',
49712         'Lambda',
49713         'ss',
49714         'Mu',
49715         'st',
49716         'Nu',
49717         'su',
49718         'Xi',
49719         'sv',
49720         'Omicron',
49721         't0',
49722         'Pi',
49723         't1',
49724         'Rho',
49725         't3',
49726         'Sigma',
49727         't4',
49728         'Tau',
49729         't5',
49730         'Upsilon',
49731         't6',
49732         'Phi',
49733         't7',
49734         'Chi',
49735         't8',
49736         'Psi',
49737         't9',
49738         'Omega',
49739         'th',
49740         'alpha',
49741         'ti',
49742         'beta',
49743         'tj',
49744         'gamma',
49745         'tk',
49746         'delta',
49747         'tl',
49748         'epsilon',
49749         'tm',
49750         'zeta',
49751         'tn',
49752         'eta',
49753         'to',
49754         'theta',
49755         'tp',
49756         'iota',
49757         'tq',
49758         'kappa',
49759         'tr',
49760         'lambda',
49761         'ts',
49762         'mu',
49763         'tt',
49764         'nu',
49765         'tu',
49766         'xi',
49767         'tv',
49768         'omicron',
49769         'u0',
49770         'pi',
49771         'u1',
49772         'rho',
49773         'u2',
49774         'sigmaf',
49775         'u3',
49776         'sigma',
49777         'u4',
49778         'tau',
49779         'u5',
49780         'upsilon',
49781         'u6',
49782         'phi',
49783         'u7',
49784         'chi',
49785         'u8',
49786         'psi',
49787         'u9',
49788         'omega',
49789         'uh',
49790         'thetasym',
49791         'ui',
49792         'upsih',
49793         'um',
49794         'piv',
49795         '812',
49796         'bull',
49797         '816',
49798         'hellip',
49799         '81i',
49800         'prime',
49801         '81j',
49802         'Prime',
49803         '81u',
49804         'oline',
49805         '824',
49806         'frasl',
49807         '88o',
49808         'weierp',
49809         '88h',
49810         'image',
49811         '88s',
49812         'real',
49813         '892',
49814         'trade',
49815         '89l',
49816         'alefsym',
49817         '8cg',
49818         'larr',
49819         '8ch',
49820         'uarr',
49821         '8ci',
49822         'rarr',
49823         '8cj',
49824         'darr',
49825         '8ck',
49826         'harr',
49827         '8dl',
49828         'crarr',
49829         '8eg',
49830         'lArr',
49831         '8eh',
49832         'uArr',
49833         '8ei',
49834         'rArr',
49835         '8ej',
49836         'dArr',
49837         '8ek',
49838         'hArr',
49839         '8g0',
49840         'forall',
49841         '8g2',
49842         'part',
49843         '8g3',
49844         'exist',
49845         '8g5',
49846         'empty',
49847         '8g7',
49848         'nabla',
49849         '8g8',
49850         'isin',
49851         '8g9',
49852         'notin',
49853         '8gb',
49854         'ni',
49855         '8gf',
49856         'prod',
49857         '8gh',
49858         'sum',
49859         '8gi',
49860         'minus',
49861         '8gn',
49862         'lowast',
49863         '8gq',
49864         'radic',
49865         '8gt',
49866         'prop',
49867         '8gu',
49868         'infin',
49869         '8h0',
49870         'ang',
49871         '8h7',
49872         'and',
49873         '8h8',
49874         'or',
49875         '8h9',
49876         'cap',
49877         '8ha',
49878         'cup',
49879         '8hb',
49880         'int',
49881         '8hk',
49882         'there4',
49883         '8hs',
49884         'sim',
49885         '8i5',
49886         'cong',
49887         '8i8',
49888         'asymp',
49889         '8j0',
49890         'ne',
49891         '8j1',
49892         'equiv',
49893         '8j4',
49894         'le',
49895         '8j5',
49896         'ge',
49897         '8k2',
49898         'sub',
49899         '8k3',
49900         'sup',
49901         '8k4',
49902         'nsub',
49903         '8k6',
49904         'sube',
49905         '8k7',
49906         'supe',
49907         '8kl',
49908         'oplus',
49909         '8kn',
49910         'otimes',
49911         '8l5',
49912         'perp',
49913         '8m5',
49914         'sdot',
49915         '8o8',
49916         'lceil',
49917         '8o9',
49918         'rceil',
49919         '8oa',
49920         'lfloor',
49921         '8ob',
49922         'rfloor',
49923         '8p9',
49924         'lang',
49925         '8pa',
49926         'rang',
49927         '9ea',
49928         'loz',
49929         '9j0',
49930         'spades',
49931         '9j3',
49932         'clubs',
49933         '9j5',
49934         'hearts',
49935         '9j6',
49936         'diams',
49937         'ai',
49938         'OElig',
49939         'aj',
49940         'oelig',
49941         'b0',
49942         'Scaron',
49943         'b1',
49944         'scaron',
49945         'bo',
49946         'Yuml',
49947         'm6',
49948         'circ',
49949         'ms',
49950         'tilde',
49951         '802',
49952         'ensp',
49953         '803',
49954         'emsp',
49955         '809',
49956         'thinsp',
49957         '80c',
49958         'zwnj',
49959         '80d',
49960         'zwj',
49961         '80e',
49962         'lrm',
49963         '80f',
49964         'rlm',
49965         '80j',
49966         'ndash',
49967         '80k',
49968         'mdash',
49969         '80o',
49970         'lsquo',
49971         '80p',
49972         'rsquo',
49973         '80q',
49974         'sbquo',
49975         '80s',
49976         'ldquo',
49977         '80t',
49978         'rdquo',
49979         '80u',
49980         'bdquo',
49981         '810',
49982         'dagger',
49983         '811',
49984         'Dagger',
49985         '81g',
49986         'permil',
49987         '81p',
49988         'lsaquo',
49989         '81q',
49990         'rsaquo',
49991         '85c',
49992         'euro'
49993     ],
49994
49995          
49996     /**
49997      * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
49998      *
49999      * @method encodeRaw
50000      * @param {String} text Text to encode.
50001      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
50002      * @return {String} Entity encoded text.
50003      */
50004     encodeRaw: function(text, attr)
50005     {
50006         var t = this;
50007         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
50008             return t.baseEntities[chr] || chr;
50009         });
50010     },
50011     /**
50012      * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
50013      * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
50014      * and is exposed as the DOMUtils.encode function.
50015      *
50016      * @method encodeAllRaw
50017      * @param {String} text Text to encode.
50018      * @return {String} Entity encoded text.
50019      */
50020     encodeAllRaw: function(text) {
50021         var t = this;
50022         return ('' + text).replace(this.rawCharsRegExp, function(chr) {
50023             return t.baseEntities[chr] || chr;
50024         });
50025     },
50026     /**
50027      * Encodes the specified string using numeric entities. The core entities will be
50028      * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
50029      *
50030      * @method encodeNumeric
50031      * @param {String} text Text to encode.
50032      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
50033      * @return {String} Entity encoded text.
50034      */
50035     encodeNumeric: function(text, attr) {
50036         var t = this;
50037         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
50038             // Multi byte sequence convert it to a single entity
50039             if (chr.length > 1) {
50040                 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
50041             }
50042             return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
50043         });
50044     },
50045     /**
50046      * Encodes the specified string using named entities. The core entities will be encoded
50047      * as named ones but all non lower ascii characters will be encoded into named entities.
50048      *
50049      * @method encodeNamed
50050      * @param {String} text Text to encode.
50051      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
50052      * @param {Object} entities Optional parameter with entities to use.
50053      * @return {String} Entity encoded text.
50054      */
50055     encodeNamed: function(text, attr, entities) {
50056         var t = this;
50057         entities = entities || this.namedEntities;
50058         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
50059             return t.baseEntities[chr] || entities[chr] || chr;
50060         });
50061     },
50062     /**
50063      * Returns an encode function based on the name(s) and it's optional entities.
50064      *
50065      * @method getEncodeFunc
50066      * @param {String} name Comma separated list of encoders for example named,numeric.
50067      * @param {String} entities Optional parameter with entities to use instead of the built in set.
50068      * @return {function} Encode function to be used.
50069      */
50070     getEncodeFunc: function(name, entities) {
50071         entities = this.buildEntitiesLookup(entities) || this.namedEntities;
50072         var t = this;
50073         function encodeNamedAndNumeric(text, attr) {
50074             return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
50075                 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
50076             });
50077         }
50078
50079         function encodeCustomNamed(text, attr) {
50080             return t.encodeNamed(text, attr, entities);
50081         }
50082         // Replace + with , to be compatible with previous TinyMCE versions
50083         name = this.makeMap(name.replace(/\+/g, ','));
50084         // Named and numeric encoder
50085         if (name.named && name.numeric) {
50086             return this.encodeNamedAndNumeric;
50087         }
50088         // Named encoder
50089         if (name.named) {
50090             // Custom names
50091             if (entities) {
50092                 return encodeCustomNamed;
50093             }
50094             return this.encodeNamed;
50095         }
50096         // Numeric
50097         if (name.numeric) {
50098             return this.encodeNumeric;
50099         }
50100         // Raw encoder
50101         return this.encodeRaw;
50102     },
50103     /**
50104      * Decodes the specified string, this will replace entities with raw UTF characters.
50105      *
50106      * @method decode
50107      * @param {String} text Text to entity decode.
50108      * @return {String} Entity decoded string.
50109      */
50110     decode: function(text)
50111     {
50112         var  t = this;
50113         return text.replace(this.entityRegExp, function(all, numeric) {
50114             if (numeric) {
50115                 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
50116                 // Support upper UTF
50117                 if (numeric > 65535) {
50118                     numeric -= 65536;
50119                     return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
50120                 }
50121                 return t.asciiMap[numeric] || String.fromCharCode(numeric);
50122             }
50123             return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
50124         });
50125     },
50126     nativeDecode : function (text) {
50127         return text;
50128     },
50129     makeMap : function (items, delim, map) {
50130                 var i;
50131                 items = items || [];
50132                 delim = delim || ',';
50133                 if (typeof items == "string") {
50134                         items = items.split(delim);
50135                 }
50136                 map = map || {};
50137                 i = items.length;
50138                 while (i--) {
50139                         map[items[i]] = {};
50140                 }
50141                 return map;
50142         }
50143 };
50144     
50145     
50146     
50147 Roo.htmleditor.TidyEntities.init();
50148 /**
50149  * @class Roo.htmleditor.KeyEnter
50150  * Handle Enter press..
50151  * @cfg {Roo.HtmlEditorCore} core the editor.
50152  * @constructor
50153  * Create a new Filter.
50154  * @param {Object} config Configuration options
50155  */
50156
50157
50158
50159
50160
50161 Roo.htmleditor.KeyEnter = function(cfg) {
50162     Roo.apply(this, cfg);
50163     // this does not actually call walk as it's really just a abstract class
50164  
50165     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
50166 }
50167
50168 //Roo.htmleditor.KeyEnter.i = 0;
50169
50170
50171 Roo.htmleditor.KeyEnter.prototype = {
50172     
50173     core : false,
50174     
50175     keypress : function(e)
50176     {
50177         if (e.charCode != 13 && e.charCode != 10) {
50178             Roo.log([e.charCode,e]);
50179             return true;
50180         }
50181         e.preventDefault();
50182         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
50183         var doc = this.core.doc;
50184           //add a new line
50185        
50186     
50187         var sel = this.core.getSelection();
50188         var range = sel.getRangeAt(0);
50189         var n = range.commonAncestorContainer;
50190         var pc = range.closest([ 'ol', 'ul']);
50191         var pli = range.closest('li');
50192         if (!pc || e.ctrlKey) {
50193             // on it list, or ctrl pressed.
50194             if (!e.ctrlKey) {
50195                 sel.insertNode('br', 'after'); 
50196             } else {
50197                 // only do this if we have ctrl key..
50198                 var br = doc.createElement('br');
50199                 br.className = 'clear';
50200                 br.setAttribute('style', 'clear: both');
50201                 sel.insertNode(br, 'after'); 
50202             }
50203             
50204          
50205             this.core.undoManager.addEvent();
50206             this.core.fireEditorEvent(e);
50207             return false;
50208         }
50209         
50210         // deal with <li> insetion
50211         if (pli.innerText.trim() == '' &&
50212             pli.previousSibling &&
50213             pli.previousSibling.nodeName == 'LI' &&
50214             pli.previousSibling.innerText.trim() ==  '') {
50215             pli.parentNode.removeChild(pli.previousSibling);
50216             sel.cursorAfter(pc);
50217             this.core.undoManager.addEvent();
50218             this.core.fireEditorEvent(e);
50219             return false;
50220         }
50221     
50222         var li = doc.createElement('LI');
50223         li.innerHTML = '&nbsp;';
50224         if (!pli || !pli.firstSibling) {
50225             pc.appendChild(li);
50226         } else {
50227             pli.parentNode.insertBefore(li, pli.firstSibling);
50228         }
50229         sel.cursorText (li.firstChild);
50230       
50231         this.core.undoManager.addEvent();
50232         this.core.fireEditorEvent(e);
50233
50234         return false;
50235         
50236     
50237         
50238         
50239          
50240     }
50241 };
50242      
50243 /**
50244  * @class Roo.htmleditor.Block
50245  * Base class for html editor blocks - do not use it directly .. extend it..
50246  * @cfg {DomElement} node The node to apply stuff to.
50247  * @cfg {String} friendly_name the name that appears in the context bar about this block
50248  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
50249  
50250  * @constructor
50251  * Create a new Filter.
50252  * @param {Object} config Configuration options
50253  */
50254
50255 Roo.htmleditor.Block  = function(cfg)
50256 {
50257     // do nothing .. should not be called really.
50258 }
50259 /**
50260  * factory method to get the block from an element (using cache if necessary)
50261  * @static
50262  * @param {HtmlElement} the dom element
50263  */
50264 Roo.htmleditor.Block.factory = function(node)
50265 {
50266     var cc = Roo.htmleditor.Block.cache;
50267     var id = Roo.get(node).id;
50268     if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
50269         Roo.htmleditor.Block.cache[id].readElement(node);
50270         return Roo.htmleditor.Block.cache[id];
50271     }
50272     var db  = node.getAttribute('data-block');
50273     if (!db) {
50274         db = node.nodeName.toLowerCase().toUpperCaseFirst();
50275     }
50276     var cls = Roo.htmleditor['Block' + db];
50277     if (typeof(cls) == 'undefined') {
50278         //Roo.log(node.getAttribute('data-block'));
50279         Roo.log("OOps missing block : " + 'Block' + db);
50280         return false;
50281     }
50282     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
50283     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
50284 };
50285
50286 /**
50287  * initalize all Elements from content that are 'blockable'
50288  * @static
50289  * @param the body element
50290  */
50291 Roo.htmleditor.Block.initAll = function(body, type)
50292 {
50293     if (typeof(type) == 'undefined') {
50294         var ia = Roo.htmleditor.Block.initAll;
50295         ia(body,'table');
50296         ia(body,'td');
50297         ia(body,'figure');
50298         return;
50299     }
50300     Roo.each(Roo.get(body).query(type), function(e) {
50301         Roo.htmleditor.Block.factory(e);    
50302     },this);
50303 };
50304 // question goes here... do we need to clear out this cache sometimes?
50305 // or show we make it relivant to the htmleditor.
50306 Roo.htmleditor.Block.cache = {};
50307
50308 Roo.htmleditor.Block.prototype = {
50309     
50310     node : false,
50311     
50312      // used by context menu
50313     friendly_name : 'Based Block',
50314     
50315     // text for button to delete this element
50316     deleteTitle : false,
50317     
50318     context : false,
50319     /**
50320      * Update a node with values from this object
50321      * @param {DomElement} node
50322      */
50323     updateElement : function(node)
50324     {
50325         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
50326     },
50327      /**
50328      * convert to plain HTML for calling insertAtCursor..
50329      */
50330     toHTML : function()
50331     {
50332         return Roo.DomHelper.markup(this.toObject());
50333     },
50334     /**
50335      * used by readEleemnt to extract data from a node
50336      * may need improving as it's pretty basic
50337      
50338      * @param {DomElement} node
50339      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
50340      * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
50341      * @param {String} style the style property - eg. text-align
50342      */
50343     getVal : function(node, tag, attr, style)
50344     {
50345         var n = node;
50346         if (tag !== true && n.tagName != tag.toUpperCase()) {
50347             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
50348             // but kiss for now.
50349             n = node.getElementsByTagName(tag).item(0);
50350         }
50351         if (!n) {
50352             return '';
50353         }
50354         if (attr === false) {
50355             return n;
50356         }
50357         if (attr == 'html') {
50358             return n.innerHTML;
50359         }
50360         if (attr == 'style') {
50361             return n.style[style]; 
50362         }
50363         
50364         return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
50365             
50366     },
50367     /**
50368      * create a DomHelper friendly object - for use with 
50369      * Roo.DomHelper.markup / overwrite / etc..
50370      * (override this)
50371      */
50372     toObject : function()
50373     {
50374         return {};
50375     },
50376       /**
50377      * Read a node that has a 'data-block' property - and extract the values from it.
50378      * @param {DomElement} node - the node
50379      */
50380     readElement : function(node)
50381     {
50382         
50383     } 
50384     
50385     
50386 };
50387
50388  
50389
50390 /**
50391  * @class Roo.htmleditor.BlockFigure
50392  * Block that has an image and a figcaption
50393  * @cfg {String} image_src the url for the image
50394  * @cfg {String} align (left|right) alignment for the block default left
50395  * @cfg {String} caption the text to appear below  (and in the alt tag)
50396  * @cfg {String} caption_display (block|none) display or not the caption
50397  * @cfg {String|number} image_width the width of the image number or %?
50398  * @cfg {String|number} image_height the height of the image number or %?
50399  * 
50400  * @constructor
50401  * Create a new Filter.
50402  * @param {Object} config Configuration options
50403  */
50404
50405 Roo.htmleditor.BlockFigure = function(cfg)
50406 {
50407     if (cfg.node) {
50408         this.readElement(cfg.node);
50409         this.updateElement(cfg.node);
50410     }
50411     Roo.apply(this, cfg);
50412 }
50413 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
50414  
50415     
50416     // setable values.
50417     image_src: '',
50418     align: 'center',
50419     caption : '',
50420     caption_display : 'block',
50421     width : '100%',
50422     cls : '',
50423     href: '',
50424     video_url : '',
50425     
50426     // margin: '2%', not used
50427     
50428     text_align: 'left', //   (left|right) alignment for the text caption default left. - not used at present
50429
50430     
50431     // used by context menu
50432     friendly_name : 'Image with caption',
50433     deleteTitle : "Delete Image and Caption",
50434     
50435     contextMenu : function(toolbar)
50436     {
50437         
50438         var block = function() {
50439             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
50440         };
50441         
50442         
50443         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
50444         
50445         var syncValue = toolbar.editorcore.syncValue;
50446         
50447         var fields = {};
50448         
50449         return [
50450              {
50451                 xtype : 'TextItem',
50452                 text : "Source: ",
50453                 xns : rooui.Toolbar  //Boostrap?
50454             },
50455             {
50456                 xtype : 'Button',
50457                 text: 'Change Image URL',
50458                  
50459                 listeners : {
50460                     click: function (btn, state)
50461                     {
50462                         var b = block();
50463                         
50464                         Roo.MessageBox.show({
50465                             title : "Image Source URL",
50466                             msg : "Enter the url for the image",
50467                             buttons: Roo.MessageBox.OKCANCEL,
50468                             fn: function(btn, val){
50469                                 if (btn != 'ok') {
50470                                     return;
50471                                 }
50472                                 b.image_src = val;
50473                                 b.updateElement();
50474                                 syncValue();
50475                                 toolbar.editorcore.onEditorEvent();
50476                             },
50477                             minWidth:250,
50478                             prompt:true,
50479                             //multiline: multiline,
50480                             modal : true,
50481                             value : b.image_src
50482                         });
50483                     }
50484                 },
50485                 xns : rooui.Toolbar
50486             },
50487          
50488             {
50489                 xtype : 'Button',
50490                 text: 'Change Link URL',
50491                  
50492                 listeners : {
50493                     click: function (btn, state)
50494                     {
50495                         var b = block();
50496                         
50497                         Roo.MessageBox.show({
50498                             title : "Link URL",
50499                             msg : "Enter the url for the link - leave blank to have no link",
50500                             buttons: Roo.MessageBox.OKCANCEL,
50501                             fn: function(btn, val){
50502                                 if (btn != 'ok') {
50503                                     return;
50504                                 }
50505                                 b.href = val;
50506                                 b.updateElement();
50507                                 syncValue();
50508                                 toolbar.editorcore.onEditorEvent();
50509                             },
50510                             minWidth:250,
50511                             prompt:true,
50512                             //multiline: multiline,
50513                             modal : true,
50514                             value : b.href
50515                         });
50516                     }
50517                 },
50518                 xns : rooui.Toolbar
50519             },
50520             {
50521                 xtype : 'Button',
50522                 text: 'Show Video URL',
50523                  
50524                 listeners : {
50525                     click: function (btn, state)
50526                     {
50527                         Roo.MessageBox.alert("Video URL",
50528                             block().video_url == '' ? 'This image is not linked ot a video' :
50529                                 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
50530                     }
50531                 },
50532                 xns : rooui.Toolbar
50533             },
50534             
50535             
50536             {
50537                 xtype : 'TextItem',
50538                 text : "Width: ",
50539                 xns : rooui.Toolbar  //Boostrap?
50540             },
50541             {
50542                 xtype : 'ComboBox',
50543                 allowBlank : false,
50544                 displayField : 'val',
50545                 editable : true,
50546                 listWidth : 100,
50547                 triggerAction : 'all',
50548                 typeAhead : true,
50549                 valueField : 'val',
50550                 width : 70,
50551                 name : 'width',
50552                 listeners : {
50553                     select : function (combo, r, index)
50554                     {
50555                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
50556                         var b = block();
50557                         b.width = r.get('val');
50558                         b.updateElement();
50559                         syncValue();
50560                         toolbar.editorcore.onEditorEvent();
50561                     }
50562                 },
50563                 xns : rooui.form,
50564                 store : {
50565                     xtype : 'SimpleStore',
50566                     data : [
50567                         ['100%'],
50568                         ['80%'],
50569                         ['50%'],
50570                         ['20%'],
50571                         ['10%']
50572                     ],
50573                     fields : [ 'val'],
50574                     xns : Roo.data
50575                 }
50576             },
50577             {
50578                 xtype : 'TextItem',
50579                 text : "Align: ",
50580                 xns : rooui.Toolbar  //Boostrap?
50581             },
50582             {
50583                 xtype : 'ComboBox',
50584                 allowBlank : false,
50585                 displayField : 'val',
50586                 editable : true,
50587                 listWidth : 100,
50588                 triggerAction : 'all',
50589                 typeAhead : true,
50590                 valueField : 'val',
50591                 width : 70,
50592                 name : 'align',
50593                 listeners : {
50594                     select : function (combo, r, index)
50595                     {
50596                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
50597                         var b = block();
50598                         b.align = r.get('val');
50599                         b.updateElement();
50600                         syncValue();
50601                         toolbar.editorcore.onEditorEvent();
50602                     }
50603                 },
50604                 xns : rooui.form,
50605                 store : {
50606                     xtype : 'SimpleStore',
50607                     data : [
50608                         ['left'],
50609                         ['right'],
50610                         ['center']
50611                     ],
50612                     fields : [ 'val'],
50613                     xns : Roo.data
50614                 }
50615             },
50616             
50617               
50618             {
50619                 xtype : 'Button',
50620                 text: 'Hide Caption',
50621                 name : 'caption_display',
50622                 pressed : false,
50623                 enableToggle : true,
50624                 setValue : function(v) {
50625                     // this trigger toggle.
50626                      
50627                     this.setText(v ? "Hide Caption" : "Show Caption");
50628                     this.setPressed(v != 'block');
50629                 },
50630                 listeners : {
50631                     toggle: function (btn, state)
50632                     {
50633                         var b  = block();
50634                         b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
50635                         this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
50636                         b.updateElement();
50637                         syncValue();
50638                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
50639                         toolbar.editorcore.onEditorEvent();
50640                     }
50641                 },
50642                 xns : rooui.Toolbar
50643             }
50644         ];
50645         
50646     },
50647     /**
50648      * create a DomHelper friendly object - for use with
50649      * Roo.DomHelper.markup / overwrite / etc..
50650      */
50651     toObject : function()
50652     {
50653         var d = document.createElement('div');
50654         d.innerHTML = this.caption;
50655         
50656         var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0; 
50657         
50658         var iw = this.align == 'center' ? this.width : '100%';
50659         var img =   {
50660             tag : 'img',
50661             contenteditable : 'false',
50662             src : this.image_src,
50663             alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
50664             style: {
50665                 width : iw,
50666                 maxWidth : iw + ' !important', // this is not getting rendered?
50667                 margin : m  
50668                 
50669             },
50670             width: this.align == 'center' ?  this.width : '100%' 
50671
50672         };
50673         
50674         /*
50675         '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
50676                     '<a href="{2}">' + 
50677                         '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' + 
50678                     '</a>' + 
50679                 '</div>',
50680         */
50681                 
50682         if (this.href.length > 0) {
50683             img = {
50684                 tag : 'a',
50685                 href: this.href,
50686                 contenteditable : 'true',
50687                 cn : [
50688                     img
50689                 ]
50690             };
50691         }
50692         
50693         
50694         if (this.video_url.length > 0) {
50695             img = {
50696                 tag : 'div',
50697                 cls : this.cls,
50698                 frameborder : 0,
50699                 allowfullscreen : true,
50700                 width : 420,  // these are for video tricks - that we replace the outer
50701                 height : 315,
50702                 src : this.video_url,
50703                 cn : [
50704                     img
50705                 ]
50706             };
50707         }
50708
50709
50710   
50711         var ret =   {
50712             tag: 'figure',
50713             'data-block' : 'Figure',
50714             'data-width' : this.width,
50715             'data-caption' : this.caption, 
50716             'data-caption-display' : this.caption_display,
50717             contenteditable : 'false',
50718             
50719             style : {
50720                 display: 'block',
50721                 float :  this.align ,
50722                 maxWidth :  this.align == 'center' ? '100% !important' : (this.width + ' !important'),
50723                 width : this.align == 'center' ? '100%' : this.width,
50724                 margin:  '0px',
50725                 padding: this.align == 'center' ? '0' : '0 10px' ,
50726                 textAlign : this.align   // seems to work for email..
50727                 
50728             },
50729             
50730             align : this.align,
50731             cn : [
50732                 img
50733             ]
50734         };
50735
50736         // show figcaption only if caption_display is 'block'
50737         if(this.caption_display == 'block') {
50738             ret['cn'].push({
50739                 tag: 'figcaption',
50740                 style : {
50741                     textAlign : 'left',
50742                     fontSize : '16px',
50743                     lineHeight : '24px',
50744                     display : this.caption_display,
50745                     maxWidth : (this.align == 'center' ?  this.width : '100%' ) + ' !important',
50746                     margin: m,
50747                     width: this.align == 'center' ?  this.width : '100%' 
50748                 
50749                      
50750                 },
50751                 cls : this.cls.length > 0 ? (this.cls  + '-thumbnail' ) : '',
50752                 cn : [
50753                     {
50754                         tag: 'div',
50755                         style  : {
50756                             marginTop : '16px',
50757                             textAlign : 'start'
50758                         },
50759                         align: 'left',
50760                         cn : [
50761                             {
50762                                 // we can not rely on yahoo syndication to use CSS elements - so have to use  '<i>' to encase stuff.
50763                                 tag : 'i',
50764                                 contenteditable : Roo.htmleditor.BlockFigure.caption_edit,
50765                                 html : this.caption.length ? this.caption : "Caption" // fake caption
50766                             }
50767                             
50768                         ]
50769                     }
50770                     
50771                 ]
50772                 
50773             });
50774         }
50775         return ret;
50776          
50777     },
50778     
50779     readElement : function(node)
50780     {
50781         // this should not really come from the link...
50782         this.video_url = this.getVal(node, 'div', 'src');
50783         this.cls = this.getVal(node, 'div', 'class');
50784         this.href = this.getVal(node, 'a', 'href');
50785         
50786         
50787         this.image_src = this.getVal(node, 'img', 'src');
50788          
50789         this.align = this.getVal(node, 'figure', 'align');
50790
50791         // caption display is stored in figure
50792         this.caption_display = this.getVal(node, true, 'data-caption-display');
50793
50794         // backward compatible
50795         // it was stored in figcaption
50796         if(this.caption_display == '') {
50797             this.caption_display = this.getVal(node, 'figcaption', 'data-display');
50798         }
50799
50800         // read caption from figcaption
50801         var figcaption = this.getVal(node, 'figcaption', false);
50802
50803         if (figcaption !== '') {
50804             this.caption = this.getVal(figcaption, 'i', 'html');
50805         }
50806                 
50807
50808         // read caption from data-caption in figure if no caption from figcaption
50809         var dc = this.getVal(node, true, 'data-caption');
50810
50811         if(this.caption_display == 'none' && dc && dc.length){
50812             this.caption = dc;
50813         }
50814
50815         //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
50816         this.width = this.getVal(node, true, 'data-width');
50817         //this.margin = this.getVal(node, 'figure', 'style', 'margin');
50818         
50819     },
50820     removeNode : function()
50821     {
50822         return this.node;
50823     }
50824     
50825   
50826    
50827      
50828     
50829     
50830     
50831     
50832 });
50833
50834 Roo.apply(Roo.htmleditor.BlockFigure, {
50835     caption_edit : true
50836 });
50837
50838  
50839
50840 /**
50841  * @class Roo.htmleditor.BlockTable
50842  * Block that manages a table
50843  * 
50844  * @constructor
50845  * Create a new Filter.
50846  * @param {Object} config Configuration options
50847  */
50848
50849 Roo.htmleditor.BlockTable = function(cfg)
50850 {
50851     if (cfg.node) {
50852         this.readElement(cfg.node);
50853         this.updateElement(cfg.node);
50854     }
50855     Roo.apply(this, cfg);
50856     if (!cfg.node) {
50857         this.rows = [];
50858         for(var r = 0; r < this.no_row; r++) {
50859             this.rows[r] = [];
50860             for(var c = 0; c < this.no_col; c++) {
50861                 this.rows[r][c] = this.emptyCell();
50862             }
50863         }
50864     }
50865     
50866     
50867 }
50868 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
50869  
50870     rows : false,
50871     no_col : 1,
50872     no_row : 1,
50873     
50874     
50875     width: '100%',
50876     
50877     // used by context menu
50878     friendly_name : 'Table',
50879     deleteTitle : 'Delete Table',
50880     // context menu is drawn once..
50881     
50882     contextMenu : function(toolbar)
50883     {
50884         
50885         var block = function() {
50886             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
50887         };
50888         
50889         
50890         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
50891         
50892         var syncValue = toolbar.editorcore.syncValue;
50893         
50894         var fields = {};
50895         
50896         return [
50897             {
50898                 xtype : 'TextItem',
50899                 text : "Width: ",
50900                 xns : rooui.Toolbar  //Boostrap?
50901             },
50902             {
50903                 xtype : 'ComboBox',
50904                 allowBlank : false,
50905                 displayField : 'val',
50906                 editable : true,
50907                 listWidth : 100,
50908                 triggerAction : 'all',
50909                 typeAhead : true,
50910                 valueField : 'val',
50911                 width : 100,
50912                 name : 'width',
50913                 listeners : {
50914                     select : function (combo, r, index)
50915                     {
50916                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
50917                         var b = block();
50918                         b.width = r.get('val');
50919                         b.updateElement();
50920                         syncValue();
50921                         toolbar.editorcore.onEditorEvent();
50922                     }
50923                 },
50924                 xns : rooui.form,
50925                 store : {
50926                     xtype : 'SimpleStore',
50927                     data : [
50928                         ['100%'],
50929                         ['auto']
50930                     ],
50931                     fields : [ 'val'],
50932                     xns : Roo.data
50933                 }
50934             },
50935             // -------- Cols
50936             
50937             {
50938                 xtype : 'TextItem',
50939                 text : "Columns: ",
50940                 xns : rooui.Toolbar  //Boostrap?
50941             },
50942          
50943             {
50944                 xtype : 'Button',
50945                 text: '-',
50946                 listeners : {
50947                     click : function (_self, e)
50948                     {
50949                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
50950                         block().removeColumn();
50951                         syncValue();
50952                         toolbar.editorcore.onEditorEvent();
50953                     }
50954                 },
50955                 xns : rooui.Toolbar
50956             },
50957             {
50958                 xtype : 'Button',
50959                 text: '+',
50960                 listeners : {
50961                     click : function (_self, e)
50962                     {
50963                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
50964                         block().addColumn();
50965                         syncValue();
50966                         toolbar.editorcore.onEditorEvent();
50967                     }
50968                 },
50969                 xns : rooui.Toolbar
50970             },
50971             // -------- ROWS
50972             {
50973                 xtype : 'TextItem',
50974                 text : "Rows: ",
50975                 xns : rooui.Toolbar  //Boostrap?
50976             },
50977          
50978             {
50979                 xtype : 'Button',
50980                 text: '-',
50981                 listeners : {
50982                     click : function (_self, e)
50983                     {
50984                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
50985                         block().removeRow();
50986                         syncValue();
50987                         toolbar.editorcore.onEditorEvent();
50988                     }
50989                 },
50990                 xns : rooui.Toolbar
50991             },
50992             {
50993                 xtype : 'Button',
50994                 text: '+',
50995                 listeners : {
50996                     click : function (_self, e)
50997                     {
50998                         block().addRow();
50999                         syncValue();
51000                         toolbar.editorcore.onEditorEvent();
51001                     }
51002                 },
51003                 xns : rooui.Toolbar
51004             },
51005             // -------- ROWS
51006             {
51007                 xtype : 'Button',
51008                 text: 'Reset Column Widths',
51009                 listeners : {
51010                     
51011                     click : function (_self, e)
51012                     {
51013                         block().resetWidths();
51014                         syncValue();
51015                         toolbar.editorcore.onEditorEvent();
51016                     }
51017                 },
51018                 xns : rooui.Toolbar
51019             } 
51020             
51021             
51022             
51023         ];
51024         
51025     },
51026     
51027     
51028   /**
51029      * create a DomHelper friendly object - for use with
51030      * Roo.DomHelper.markup / overwrite / etc..
51031      * ?? should it be called with option to hide all editing features?
51032      */
51033     toObject : function()
51034     {
51035         
51036         var ret = {
51037             tag : 'table',
51038             contenteditable : 'false', // this stops cell selection from picking the table.
51039             'data-block' : 'Table',
51040             style : {
51041                 width:  this.width,
51042                 border : 'solid 1px #000', // ??? hard coded?
51043                 'border-collapse' : 'collapse' 
51044             },
51045             cn : [
51046                 { tag : 'tbody' , cn : [] }
51047             ]
51048         };
51049         
51050         // do we have a head = not really 
51051         var ncols = 0;
51052         Roo.each(this.rows, function( row ) {
51053             var tr = {
51054                 tag: 'tr',
51055                 style : {
51056                     margin: '6px',
51057                     border : 'solid 1px #000',
51058                     textAlign : 'left' 
51059                 },
51060                 cn : [ ]
51061             };
51062             
51063             ret.cn[0].cn.push(tr);
51064             // does the row have any properties? ?? height?
51065             var nc = 0;
51066             Roo.each(row, function( cell ) {
51067                 
51068                 var td = {
51069                     tag : 'td',
51070                     contenteditable :  'true',
51071                     'data-block' : 'Td',
51072                     html : cell.html,
51073                     style : cell.style
51074                 };
51075                 if (cell.colspan > 1) {
51076                     td.colspan = cell.colspan ;
51077                     nc += cell.colspan;
51078                 } else {
51079                     nc++;
51080                 }
51081                 if (cell.rowspan > 1) {
51082                     td.rowspan = cell.rowspan ;
51083                 }
51084                 
51085                 
51086                 // widths ?
51087                 tr.cn.push(td);
51088                     
51089                 
51090             }, this);
51091             ncols = Math.max(nc, ncols);
51092             
51093             
51094         }, this);
51095         // add the header row..
51096         
51097         ncols++;
51098          
51099         
51100         return ret;
51101          
51102     },
51103     
51104     readElement : function(node)
51105     {
51106         node  = node ? node : this.node ;
51107         this.width = this.getVal(node, true, 'style', 'width') || '100%';
51108         
51109         this.rows = [];
51110         this.no_row = 0;
51111         var trs = Array.from(node.rows);
51112         trs.forEach(function(tr) {
51113             var row =  [];
51114             this.rows.push(row);
51115             
51116             this.no_row++;
51117             var no_column = 0;
51118             Array.from(tr.cells).forEach(function(td) {
51119                 
51120                 var add = {
51121                     colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
51122                     rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
51123                     style : td.hasAttribute('style') ? td.getAttribute('style') : '',
51124                     html : td.innerHTML
51125                 };
51126                 no_column += add.colspan;
51127                      
51128                 
51129                 row.push(add);
51130                 
51131                 
51132             },this);
51133             this.no_col = Math.max(this.no_col, no_column);
51134             
51135             
51136         },this);
51137         
51138         
51139     },
51140     normalizeRows: function()
51141     {
51142         var ret= [];
51143         var rid = -1;
51144         this.rows.forEach(function(row) {
51145             rid++;
51146             ret[rid] = [];
51147             row = this.normalizeRow(row);
51148             var cid = 0;
51149             row.forEach(function(c) {
51150                 while (typeof(ret[rid][cid]) != 'undefined') {
51151                     cid++;
51152                 }
51153                 if (typeof(ret[rid]) == 'undefined') {
51154                     ret[rid] = [];
51155                 }
51156                 ret[rid][cid] = c;
51157                 c.row = rid;
51158                 c.col = cid;
51159                 if (c.rowspan < 2) {
51160                     return;
51161                 }
51162                 
51163                 for(var i = 1 ;i < c.rowspan; i++) {
51164                     if (typeof(ret[rid+i]) == 'undefined') {
51165                         ret[rid+i] = [];
51166                     }
51167                     ret[rid+i][cid] = c;
51168                 }
51169             });
51170         }, this);
51171         return ret;
51172     
51173     },
51174     
51175     normalizeRow: function(row)
51176     {
51177         var ret= [];
51178         row.forEach(function(c) {
51179             if (c.colspan < 2) {
51180                 ret.push(c);
51181                 return;
51182             }
51183             for(var i =0 ;i < c.colspan; i++) {
51184                 ret.push(c);
51185             }
51186         });
51187         return ret;
51188     
51189     },
51190     
51191     deleteColumn : function(sel)
51192     {
51193         if (!sel || sel.type != 'col') {
51194             return;
51195         }
51196         if (this.no_col < 2) {
51197             return;
51198         }
51199         
51200         this.rows.forEach(function(row) {
51201             var cols = this.normalizeRow(row);
51202             var col = cols[sel.col];
51203             if (col.colspan > 1) {
51204                 col.colspan --;
51205             } else {
51206                 row.remove(col);
51207             }
51208             
51209         }, this);
51210         this.no_col--;
51211         
51212     },
51213     removeColumn : function()
51214     {
51215         this.deleteColumn({
51216             type: 'col',
51217             col : this.no_col-1
51218         });
51219         this.updateElement();
51220     },
51221     
51222      
51223     addColumn : function()
51224     {
51225         
51226         this.rows.forEach(function(row) {
51227             row.push(this.emptyCell());
51228            
51229         }, this);
51230         this.updateElement();
51231     },
51232     
51233     deleteRow : function(sel)
51234     {
51235         if (!sel || sel.type != 'row') {
51236             return;
51237         }
51238         
51239         if (this.no_row < 2) {
51240             return;
51241         }
51242         
51243         var rows = this.normalizeRows();
51244         
51245         
51246         rows[sel.row].forEach(function(col) {
51247             if (col.rowspan > 1) {
51248                 col.rowspan--;
51249             } else {
51250                 col.remove = 1; // flage it as removed.
51251             }
51252             
51253         }, this);
51254         var newrows = [];
51255         this.rows.forEach(function(row) {
51256             newrow = [];
51257             row.forEach(function(c) {
51258                 if (typeof(c.remove) == 'undefined') {
51259                     newrow.push(c);
51260                 }
51261                 
51262             });
51263             if (newrow.length > 0) {
51264                 newrows.push(row);
51265             }
51266         });
51267         this.rows =  newrows;
51268         
51269         
51270         
51271         this.no_row--;
51272         this.updateElement();
51273         
51274     },
51275     removeRow : function()
51276     {
51277         this.deleteRow({
51278             type: 'row',
51279             row : this.no_row-1
51280         });
51281         
51282     },
51283     
51284      
51285     addRow : function()
51286     {
51287         
51288         var row = [];
51289         for (var i = 0; i < this.no_col; i++ ) {
51290             
51291             row.push(this.emptyCell());
51292            
51293         }
51294         this.rows.push(row);
51295         this.updateElement();
51296         
51297     },
51298      
51299     // the default cell object... at present...
51300     emptyCell : function() {
51301         return (new Roo.htmleditor.BlockTd({})).toObject();
51302         
51303      
51304     },
51305     
51306     removeNode : function()
51307     {
51308         return this.node;
51309     },
51310     
51311     
51312     
51313     resetWidths : function()
51314     {
51315         Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
51316             var nn = Roo.htmleditor.Block.factory(n);
51317             nn.width = '';
51318             nn.updateElement(n);
51319         });
51320     }
51321     
51322     
51323     
51324     
51325 })
51326
51327 /**
51328  *
51329  * editing a TD?
51330  *
51331  * since selections really work on the table cell, then editing really should work from there
51332  *
51333  * The original plan was to support merging etc... - but that may not be needed yet..
51334  *
51335  * So this simple version will support:
51336  *   add/remove cols
51337  *   adjust the width +/-
51338  *   reset the width...
51339  *   
51340  *
51341  */
51342
51343
51344  
51345
51346 /**
51347  * @class Roo.htmleditor.BlockTable
51348  * Block that manages a table
51349  * 
51350  * @constructor
51351  * Create a new Filter.
51352  * @param {Object} config Configuration options
51353  */
51354
51355 Roo.htmleditor.BlockTd = function(cfg)
51356 {
51357     if (cfg.node) {
51358         this.readElement(cfg.node);
51359         this.updateElement(cfg.node);
51360     }
51361     Roo.apply(this, cfg);
51362      
51363     
51364     
51365 }
51366 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
51367  
51368     node : false,
51369     
51370     width: '',
51371     textAlign : 'left',
51372     valign : 'top',
51373     
51374     colspan : 1,
51375     rowspan : 1,
51376     
51377     
51378     // used by context menu
51379     friendly_name : 'Table Cell',
51380     deleteTitle : false, // use our customer delete
51381     
51382     // context menu is drawn once..
51383     
51384     contextMenu : function(toolbar)
51385     {
51386         
51387         var cell = function() {
51388             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
51389         };
51390         
51391         var table = function() {
51392             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
51393         };
51394         
51395         var lr = false;
51396         var saveSel = function()
51397         {
51398             lr = toolbar.editorcore.getSelection().getRangeAt(0);
51399         }
51400         var restoreSel = function()
51401         {
51402             if (lr) {
51403                 (function() {
51404                     toolbar.editorcore.focus();
51405                     var cr = toolbar.editorcore.getSelection();
51406                     cr.removeAllRanges();
51407                     cr.addRange(lr);
51408                     toolbar.editorcore.onEditorEvent();
51409                 }).defer(10, this);
51410                 
51411                 
51412             }
51413         }
51414         
51415         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
51416         
51417         var syncValue = toolbar.editorcore.syncValue;
51418         
51419         var fields = {};
51420         
51421         return [
51422             {
51423                 xtype : 'Button',
51424                 text : 'Edit Table',
51425                 listeners : {
51426                     click : function() {
51427                         var t = toolbar.tb.selectedNode.closest('table');
51428                         toolbar.editorcore.selectNode(t);
51429                         toolbar.editorcore.onEditorEvent();                        
51430                     }
51431                 }
51432                 
51433             },
51434               
51435            
51436              
51437             {
51438                 xtype : 'TextItem',
51439                 text : "Column Width: ",
51440                  xns : rooui.Toolbar 
51441                
51442             },
51443             {
51444                 xtype : 'Button',
51445                 text: '-',
51446                 listeners : {
51447                     click : function (_self, e)
51448                     {
51449                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
51450                         cell().shrinkColumn();
51451                         syncValue();
51452                          toolbar.editorcore.onEditorEvent();
51453                     }
51454                 },
51455                 xns : rooui.Toolbar
51456             },
51457             {
51458                 xtype : 'Button',
51459                 text: '+',
51460                 listeners : {
51461                     click : function (_self, e)
51462                     {
51463                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
51464                         cell().growColumn();
51465                         syncValue();
51466                         toolbar.editorcore.onEditorEvent();
51467                     }
51468                 },
51469                 xns : rooui.Toolbar
51470             },
51471             
51472             {
51473                 xtype : 'TextItem',
51474                 text : "Vertical Align: ",
51475                 xns : rooui.Toolbar  //Boostrap?
51476             },
51477             {
51478                 xtype : 'ComboBox',
51479                 allowBlank : false,
51480                 displayField : 'val',
51481                 editable : true,
51482                 listWidth : 100,
51483                 triggerAction : 'all',
51484                 typeAhead : true,
51485                 valueField : 'val',
51486                 width : 100,
51487                 name : 'valign',
51488                 listeners : {
51489                     select : function (combo, r, index)
51490                     {
51491                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
51492                         var b = cell();
51493                         b.valign = r.get('val');
51494                         b.updateElement();
51495                         syncValue();
51496                         toolbar.editorcore.onEditorEvent();
51497                     }
51498                 },
51499                 xns : rooui.form,
51500                 store : {
51501                     xtype : 'SimpleStore',
51502                     data : [
51503                         ['top'],
51504                         ['middle'],
51505                         ['bottom'] // there are afew more... 
51506                     ],
51507                     fields : [ 'val'],
51508                     xns : Roo.data
51509                 }
51510             },
51511             
51512             {
51513                 xtype : 'TextItem',
51514                 text : "Merge Cells: ",
51515                  xns : rooui.Toolbar 
51516                
51517             },
51518             
51519             
51520             {
51521                 xtype : 'Button',
51522                 text: 'Right',
51523                 listeners : {
51524                     click : function (_self, e)
51525                     {
51526                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
51527                         cell().mergeRight();
51528                         //block().growColumn();
51529                         syncValue();
51530                         toolbar.editorcore.onEditorEvent();
51531                     }
51532                 },
51533                 xns : rooui.Toolbar
51534             },
51535              
51536             {
51537                 xtype : 'Button',
51538                 text: 'Below',
51539                 listeners : {
51540                     click : function (_self, e)
51541                     {
51542                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
51543                         cell().mergeBelow();
51544                         //block().growColumn();
51545                         syncValue();
51546                         toolbar.editorcore.onEditorEvent();
51547                     }
51548                 },
51549                 xns : rooui.Toolbar
51550             },
51551             {
51552                 xtype : 'TextItem',
51553                 text : "| ",
51554                  xns : rooui.Toolbar 
51555                
51556             },
51557             
51558             {
51559                 xtype : 'Button',
51560                 text: 'Split',
51561                 listeners : {
51562                     click : function (_self, e)
51563                     {
51564                         //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
51565                         cell().split();
51566                         syncValue();
51567                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
51568                         toolbar.editorcore.onEditorEvent();
51569                                              
51570                     }
51571                 },
51572                 xns : rooui.Toolbar
51573             },
51574             {
51575                 xtype : 'Fill',
51576                 xns : rooui.Toolbar 
51577                
51578             },
51579         
51580           
51581             {
51582                 xtype : 'Button',
51583                 text: 'Delete',
51584                  
51585                 xns : rooui.Toolbar,
51586                 menu : {
51587                     xtype : 'Menu',
51588                     xns : rooui.menu,
51589                     items : [
51590                         {
51591                             xtype : 'Item',
51592                             html: 'Column',
51593                             listeners : {
51594                                 click : function (_self, e)
51595                                 {
51596                                     var t = table();
51597                                     
51598                                     cell().deleteColumn();
51599                                     syncValue();
51600                                     toolbar.editorcore.selectNode(t.node);
51601                                     toolbar.editorcore.onEditorEvent();   
51602                                 }
51603                             },
51604                             xns : rooui.menu
51605                         },
51606                         {
51607                             xtype : 'Item',
51608                             html: 'Row',
51609                             listeners : {
51610                                 click : function (_self, e)
51611                                 {
51612                                     var t = table();
51613                                     cell().deleteRow();
51614                                     syncValue();
51615                                     
51616                                     toolbar.editorcore.selectNode(t.node);
51617                                     toolbar.editorcore.onEditorEvent();   
51618                                                          
51619                                 }
51620                             },
51621                             xns : rooui.menu
51622                         },
51623                        {
51624                             xtype : 'Separator',
51625                             xns : rooui.menu
51626                         },
51627                         {
51628                             xtype : 'Item',
51629                             html: 'Table',
51630                             listeners : {
51631                                 click : function (_self, e)
51632                                 {
51633                                     var t = table();
51634                                     var nn = t.node.nextSibling || t.node.previousSibling;
51635                                     t.node.parentNode.removeChild(t.node);
51636                                     if (nn) { 
51637                                         toolbar.editorcore.selectNode(nn, true);
51638                                     }
51639                                     toolbar.editorcore.onEditorEvent();   
51640                                                          
51641                                 }
51642                             },
51643                             xns : rooui.menu
51644                         }
51645                     ]
51646                 }
51647             }
51648             
51649             // align... << fixme
51650             
51651         ];
51652         
51653     },
51654     
51655     
51656   /**
51657      * create a DomHelper friendly object - for use with
51658      * Roo.DomHelper.markup / overwrite / etc..
51659      * ?? should it be called with option to hide all editing features?
51660      */
51661  /**
51662      * create a DomHelper friendly object - for use with
51663      * Roo.DomHelper.markup / overwrite / etc..
51664      * ?? should it be called with option to hide all editing features?
51665      */
51666     toObject : function()
51667     {
51668         var ret = {
51669             tag : 'td',
51670             contenteditable : 'true', // this stops cell selection from picking the table.
51671             'data-block' : 'Td',
51672             valign : this.valign,
51673             style : {  
51674                 'text-align' :  this.textAlign,
51675                 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
51676                 'border-collapse' : 'collapse',
51677                 padding : '6px', // 8 for desktop / 4 for mobile
51678                 'vertical-align': this.valign
51679             },
51680             html : this.html
51681         };
51682         if (this.width != '') {
51683             ret.width = this.width;
51684             ret.style.width = this.width;
51685         }
51686         
51687         
51688         if (this.colspan > 1) {
51689             ret.colspan = this.colspan ;
51690         } 
51691         if (this.rowspan > 1) {
51692             ret.rowspan = this.rowspan ;
51693         }
51694         
51695            
51696         
51697         return ret;
51698          
51699     },
51700     
51701     readElement : function(node)
51702     {
51703         node  = node ? node : this.node ;
51704         this.width = node.style.width;
51705         this.colspan = Math.max(1,1*node.getAttribute('colspan'));
51706         this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
51707         this.html = node.innerHTML;
51708         if (node.style.textAlign != '') {
51709             this.textAlign = node.style.textAlign;
51710         }
51711         
51712         
51713     },
51714      
51715     // the default cell object... at present...
51716     emptyCell : function() {
51717         return {
51718             colspan :  1,
51719             rowspan :  1,
51720             textAlign : 'left',
51721             html : "&nbsp;" // is this going to be editable now?
51722         };
51723      
51724     },
51725     
51726     removeNode : function()
51727     {
51728         return this.node.closest('table');
51729          
51730     },
51731     
51732     cellData : false,
51733     
51734     colWidths : false,
51735     
51736     toTableArray  : function()
51737     {
51738         var ret = [];
51739         var tab = this.node.closest('tr').closest('table');
51740         Array.from(tab.rows).forEach(function(r, ri){
51741             ret[ri] = [];
51742         });
51743         var rn = 0;
51744         this.colWidths = [];
51745         var all_auto = true;
51746         Array.from(tab.rows).forEach(function(r, ri){
51747             
51748             var cn = 0;
51749             Array.from(r.cells).forEach(function(ce, ci){
51750                 var c =  {
51751                     cell : ce,
51752                     row : rn,
51753                     col: cn,
51754                     colspan : ce.colSpan,
51755                     rowspan : ce.rowSpan
51756                 };
51757                 if (ce.isEqualNode(this.node)) {
51758                     this.cellData = c;
51759                 }
51760                 // if we have been filled up by a row?
51761                 if (typeof(ret[rn][cn]) != 'undefined') {
51762                     while(typeof(ret[rn][cn]) != 'undefined') {
51763                         cn++;
51764                     }
51765                     c.col = cn;
51766                 }
51767                 
51768                 if (typeof(this.colWidths[cn]) == 'undefined' && c.colspan < 2) {
51769                     this.colWidths[cn] =   ce.style.width;
51770                     if (this.colWidths[cn] != '') {
51771                         all_auto = false;
51772                     }
51773                 }
51774                 
51775                 
51776                 if (c.colspan < 2 && c.rowspan < 2 ) {
51777                     ret[rn][cn] = c;
51778                     cn++;
51779                     return;
51780                 }
51781                 for(var j = 0; j < c.rowspan; j++) {
51782                     if (typeof(ret[rn+j]) == 'undefined') {
51783                         continue; // we have a problem..
51784                     }
51785                     ret[rn+j][cn] = c;
51786                     for(var i = 0; i < c.colspan; i++) {
51787                         ret[rn+j][cn+i] = c;
51788                     }
51789                 }
51790                 
51791                 cn += c.colspan;
51792             }, this);
51793             rn++;
51794         }, this);
51795         
51796         // initalize widths.?
51797         // either all widths or no widths..
51798         if (all_auto) {
51799             this.colWidths[0] = false; // no widths flag.
51800         }
51801         
51802         
51803         return ret;
51804         
51805     },
51806     
51807     
51808     
51809     
51810     mergeRight: function()
51811     {
51812          
51813         // get the contents of the next cell along..
51814         var tr = this.node.closest('tr');
51815         var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
51816         if (i >= tr.childNodes.length - 1) {
51817             return; // no cells on right to merge with.
51818         }
51819         var table = this.toTableArray();
51820         
51821         if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
51822             return; // nothing right?
51823         }
51824         var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
51825         // right cell - must be same rowspan and on the same row.
51826         if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
51827             return; // right hand side is not same rowspan.
51828         }
51829         
51830         
51831         
51832         this.node.innerHTML += ' ' + rc.cell.innerHTML;
51833         tr.removeChild(rc.cell);
51834         this.colspan += rc.colspan;
51835         this.node.setAttribute('colspan', this.colspan);
51836
51837         var table = this.toTableArray();
51838         this.normalizeWidths(table);
51839         this.updateWidths(table);
51840     },
51841     
51842     
51843     mergeBelow : function()
51844     {
51845         var table = this.toTableArray();
51846         if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
51847             return; // no row below
51848         }
51849         if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
51850             return; // nothing right?
51851         }
51852         var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
51853         
51854         if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
51855             return; // right hand side is not same rowspan.
51856         }
51857         this.node.innerHTML =  this.node.innerHTML + rc.cell.innerHTML ;
51858         rc.cell.parentNode.removeChild(rc.cell);
51859         this.rowspan += rc.rowspan;
51860         this.node.setAttribute('rowspan', this.rowspan);
51861     },
51862     
51863     split: function()
51864     {
51865         if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
51866             return;
51867         }
51868         var table = this.toTableArray();
51869         var cd = this.cellData;
51870         this.rowspan = 1;
51871         this.colspan = 1;
51872         
51873         for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
51874              
51875             
51876             for(var c = cd.col; c < cd.col + cd.colspan; c++) {
51877                 if (r == cd.row && c == cd.col) {
51878                     this.node.removeAttribute('rowspan');
51879                     this.node.removeAttribute('colspan');
51880                 }
51881                  
51882                 var ntd = this.node.cloneNode(); // which col/row should be 0..
51883                 ntd.removeAttribute('id'); 
51884                 ntd.style.width  = this.colWidths[c];
51885                 ntd.innerHTML = '';
51886                 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
51887             }
51888             
51889         }
51890         this.redrawAllCells(table);
51891         
51892     },
51893     
51894     
51895     
51896     redrawAllCells: function(table)
51897     {
51898         
51899          
51900         var tab = this.node.closest('tr').closest('table');
51901         var ctr = tab.rows[0].parentNode;
51902         Array.from(tab.rows).forEach(function(r, ri){
51903             
51904             Array.from(r.cells).forEach(function(ce, ci){
51905                 ce.parentNode.removeChild(ce);
51906             });
51907             r.parentNode.removeChild(r);
51908         });
51909         for(var r = 0 ; r < table.length; r++) {
51910             var re = tab.rows[r];
51911             
51912             var re = tab.ownerDocument.createElement('tr');
51913             ctr.appendChild(re);
51914             for(var c = 0 ; c < table[r].length; c++) {
51915                 if (table[r][c].cell === false) {
51916                     continue;
51917                 }
51918                 
51919                 re.appendChild(table[r][c].cell);
51920                  
51921                 table[r][c].cell = false;
51922             }
51923         }
51924         
51925     },
51926     updateWidths : function(table)
51927     {
51928         for(var r = 0 ; r < table.length; r++) {
51929            
51930             for(var c = 0 ; c < table[r].length; c++) {
51931                 if (table[r][c].cell === false) {
51932                     continue;
51933                 }
51934                 
51935                 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
51936                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
51937                     el.width = Math.floor(this.colWidths[c])  +'%';
51938                     el.updateElement(el.node);
51939                 }
51940                 if (this.colWidths[0] != false && table[r][c].colspan > 1) {
51941                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
51942                     var width = 0;
51943                     var lv = false;
51944                     for(var i = 0; i < table[r][c].colspan; i ++) {
51945                         if (typeof(this.colWidths[c + i]) != 'undefined') {
51946                             lv = this.colWidths[c + i];
51947                         } else {
51948                             this.colWidths[c + i] = lv;
51949                         }
51950                         width += Math.floor(this.colWidths[c + i]);
51951                     }
51952                     el.width = width  +'%';
51953                     el.updateElement(el.node);
51954                 }
51955                 table[r][c].cell = false; // done
51956             }
51957         }
51958     },
51959     normalizeWidths : function(table)
51960     {
51961         if (this.colWidths[0] === false) {
51962             var nw = 100.0 / this.colWidths.length;
51963             this.colWidths.forEach(function(w,i) {
51964                 this.colWidths[i] = nw;
51965             },this);
51966             return;
51967         }
51968     
51969         var t = 0, missing = [];
51970         
51971         this.colWidths.forEach(function(w,i) {
51972             //if you mix % and
51973             this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
51974             var add =  this.colWidths[i];
51975             if (add > 0) {
51976                 t+=add;
51977                 return;
51978             }
51979             missing.push(i);
51980             
51981             
51982         },this);
51983         var nc = this.colWidths.length;
51984         if (missing.length) {
51985             var mult = (nc - missing.length) / (1.0 * nc);
51986             var t = mult * t;
51987             var ew = (100 -t) / (1.0 * missing.length);
51988             this.colWidths.forEach(function(w,i) {
51989                 if (w > 0) {
51990                     this.colWidths[i] = w * mult;
51991                     return;
51992                 }
51993                 
51994                 this.colWidths[i] = ew;
51995             }, this);
51996             // have to make up numbers..
51997              
51998         }
51999         // now we should have all the widths..
52000         
52001     
52002     },
52003     
52004     shrinkColumn : function()
52005     {
52006         var table = this.toTableArray();
52007         this.normalizeWidths(table);
52008         var col = this.cellData.col;
52009         var nw = this.colWidths[col] * 0.8;
52010         if (nw < 5) {
52011             return;
52012         }
52013         var otherAdd = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
52014         this.colWidths.forEach(function(w,i) {
52015             if (i == col) {
52016                  this.colWidths[i] = nw;
52017                 return;
52018             }
52019             if (typeof(this.colWidths[i]) == 'undefined') {
52020                 this.colWidths[i] = otherAdd;
52021             } else {
52022                 this.colWidths[i] += otherAdd;
52023             }
52024         }, this);
52025         this.updateWidths(table);
52026          
52027     },
52028     growColumn : function()
52029     {
52030         var table = this.toTableArray();
52031         this.normalizeWidths(table);
52032         var col = this.cellData.col;
52033         var nw = this.colWidths[col] * 1.2;
52034         if (nw > 90) {
52035             return;
52036         }
52037         var otherSub = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
52038         this.colWidths.forEach(function(w,i) {
52039             if (i == col) {
52040                 this.colWidths[i] = nw;
52041                 return;
52042             }
52043             if (typeof(this.colWidths[i]) == 'undefined') {
52044                 this.colWidths[i] = otherSub;
52045             } else {
52046                 this.colWidths[i] -= otherSub;
52047             }
52048             
52049         }, this);
52050         this.updateWidths(table);
52051          
52052     },
52053     deleteRow : function()
52054     {
52055         // delete this rows 'tr'
52056         // if any of the cells in this row have a rowspan > 1 && row!= this row..
52057         // then reduce the rowspan.
52058         var table = this.toTableArray();
52059         // this.cellData.row;
52060         for (var i =0;i< table[this.cellData.row].length ; i++) {
52061             var c = table[this.cellData.row][i];
52062             if (c.row != this.cellData.row) {
52063                 
52064                 c.rowspan--;
52065                 c.cell.setAttribute('rowspan', c.rowspan);
52066                 continue;
52067             }
52068             if (c.rowspan > 1) {
52069                 c.rowspan--;
52070                 c.cell.setAttribute('rowspan', c.rowspan);
52071             }
52072         }
52073         table.splice(this.cellData.row,1);
52074         this.redrawAllCells(table);
52075         
52076     },
52077     deleteColumn : function()
52078     {
52079         var table = this.toTableArray();
52080         
52081         for (var i =0;i< table.length ; i++) {
52082             var c = table[i][this.cellData.col];
52083             if (c.col != this.cellData.col) {
52084                 table[i][this.cellData.col].colspan--;
52085             } else if (c.colspan > 1) {
52086                 c.colspan--;
52087                 c.cell.setAttribute('colspan', c.colspan);
52088             }
52089             table[i].splice(this.cellData.col,1);
52090         }
52091         
52092         this.redrawAllCells(table);
52093     }
52094     
52095     
52096     
52097     
52098 })
52099
52100 //<script type="text/javascript">
52101
52102 /*
52103  * Based  Ext JS Library 1.1.1
52104  * Copyright(c) 2006-2007, Ext JS, LLC.
52105  * LGPL
52106  *
52107  */
52108  
52109 /**
52110  * @class Roo.HtmlEditorCore
52111  * @extends Roo.Component
52112  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
52113  *
52114  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
52115  */
52116
52117 Roo.HtmlEditorCore = function(config){
52118     
52119     
52120     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
52121     
52122     
52123     this.addEvents({
52124         /**
52125          * @event initialize
52126          * Fires when the editor is fully initialized (including the iframe)
52127          * @param {Roo.HtmlEditorCore} this
52128          */
52129         initialize: true,
52130         /**
52131          * @event activate
52132          * Fires when the editor is first receives the focus. Any insertion must wait
52133          * until after this event.
52134          * @param {Roo.HtmlEditorCore} this
52135          */
52136         activate: true,
52137          /**
52138          * @event beforesync
52139          * Fires before the textarea is updated with content from the editor iframe. Return false
52140          * to cancel the sync.
52141          * @param {Roo.HtmlEditorCore} this
52142          * @param {String} html
52143          */
52144         beforesync: true,
52145          /**
52146          * @event beforepush
52147          * Fires before the iframe editor is updated with content from the textarea. Return false
52148          * to cancel the push.
52149          * @param {Roo.HtmlEditorCore} this
52150          * @param {String} html
52151          */
52152         beforepush: true,
52153          /**
52154          * @event sync
52155          * Fires when the textarea is updated with content from the editor iframe.
52156          * @param {Roo.HtmlEditorCore} this
52157          * @param {String} html
52158          */
52159         sync: true,
52160          /**
52161          * @event push
52162          * Fires when the iframe editor is updated with content from the textarea.
52163          * @param {Roo.HtmlEditorCore} this
52164          * @param {String} html
52165          */
52166         push: true,
52167         
52168         /**
52169          * @event editorevent
52170          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
52171          * @param {Roo.HtmlEditorCore} this
52172          */
52173         editorevent: true 
52174         
52175         
52176     });
52177     
52178     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
52179     
52180     // defaults : white / black...
52181     this.applyBlacklists();
52182     
52183     
52184     
52185 };
52186
52187
52188 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
52189
52190
52191      /**
52192      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
52193      */
52194     
52195     owner : false,
52196     
52197      /**
52198      * @cfg {String} css styling for resizing. (used on bootstrap only)
52199      */
52200     resize : false,
52201      /**
52202      * @cfg {Number} height (in pixels)
52203      */   
52204     height: 300,
52205    /**
52206      * @cfg {Number} width (in pixels)
52207      */   
52208     width: 500,
52209      /**
52210      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
52211      *         if you are doing an email editor, this probably needs disabling, it's designed
52212      */
52213     autoClean: true,
52214     
52215     /**
52216      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
52217      */
52218     enableBlocks : true,
52219     /**
52220      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
52221      * 
52222      */
52223     stylesheets: false,
52224      /**
52225      * @cfg {String} language default en - language of text (usefull for rtl languages)
52226      * 
52227      */
52228     language: 'en',
52229     
52230     /**
52231      * @cfg {boolean} allowComments - default false - allow comments in HTML source
52232      *          - by default they are stripped - if you are editing email you may need this.
52233      */
52234     allowComments: false,
52235     // id of frame..
52236     frameId: false,
52237     
52238     // private properties
52239     validationEvent : false,
52240     deferHeight: true,
52241     initialized : false,
52242     activated : false,
52243     sourceEditMode : false,
52244     onFocus : Roo.emptyFn,
52245     iframePad:3,
52246     hideMode:'offsets',
52247     
52248     clearUp: true,
52249     
52250     // blacklist + whitelisted elements..
52251     black: false,
52252     white: false,
52253      
52254     bodyCls : '',
52255
52256     
52257     undoManager : false,
52258     /**
52259      * Protected method that will not generally be called directly. It
52260      * is called when the editor initializes the iframe with HTML contents. Override this method if you
52261      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
52262      */
52263     getDocMarkup : function(){
52264         // body styles..
52265         var st = '';
52266         
52267         // inherit styels from page...?? 
52268         if (this.stylesheets === false) {
52269             
52270             Roo.get(document.head).select('style').each(function(node) {
52271                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
52272             });
52273             
52274             Roo.get(document.head).select('link').each(function(node) { 
52275                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
52276             });
52277             
52278         } else if (!this.stylesheets.length) {
52279                 // simple..
52280                 st = '<style type="text/css">' +
52281                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
52282                    '</style>';
52283         } else {
52284             for (var i in this.stylesheets) {
52285                 if (typeof(this.stylesheets[i]) != 'string') {
52286                     continue;
52287                 }
52288                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
52289             }
52290             
52291         }
52292         
52293         st +=  '<style type="text/css">' +
52294             'IMG { cursor: pointer } ' +
52295         '</style>';
52296         
52297         st += '<meta name="google" content="notranslate">';
52298         
52299         var cls = 'notranslate roo-htmleditor-body';
52300         
52301         if(this.bodyCls.length){
52302             cls += ' ' + this.bodyCls;
52303         }
52304         
52305         return '<html  class="notranslate" translate="no"><head>' + st  +
52306             //<style type="text/css">' +
52307             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
52308             //'</style>' +
52309             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
52310     },
52311
52312     // private
52313     onRender : function(ct, position)
52314     {
52315         var _t = this;
52316         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
52317         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
52318         
52319         
52320         this.el.dom.style.border = '0 none';
52321         this.el.dom.setAttribute('tabIndex', -1);
52322         this.el.addClass('x-hidden hide');
52323         
52324         
52325         
52326         if(Roo.isIE){ // fix IE 1px bogus margin
52327             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
52328         }
52329        
52330         
52331         this.frameId = Roo.id();
52332         
52333         var ifcfg = {
52334             tag: 'iframe',
52335             cls: 'form-control', // bootstrap..
52336             id: this.frameId,
52337             name: this.frameId,
52338             frameBorder : 'no',
52339             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
52340         };
52341         if (this.resize) {
52342             ifcfg.style = { resize : this.resize };
52343         }
52344         
52345         var iframe = this.owner.wrap.createChild(ifcfg, this.el); 
52346         
52347         
52348         this.iframe = iframe.dom;
52349
52350         this.assignDocWin();
52351         
52352         this.doc.designMode = 'on';
52353        
52354         this.doc.open();
52355         this.doc.write(this.getDocMarkup());
52356         this.doc.close();
52357
52358         
52359         var task = { // must defer to wait for browser to be ready
52360             run : function(){
52361                 //console.log("run task?" + this.doc.readyState);
52362                 this.assignDocWin();
52363                 if(this.doc.body || this.doc.readyState == 'complete'){
52364                     try {
52365                         this.doc.designMode="on";
52366                         
52367                     } catch (e) {
52368                         return;
52369                     }
52370                     Roo.TaskMgr.stop(task);
52371                     this.initEditor.defer(10, this);
52372                 }
52373             },
52374             interval : 10,
52375             duration: 10000,
52376             scope: this
52377         };
52378         Roo.TaskMgr.start(task);
52379
52380     },
52381
52382     // private
52383     onResize : function(w, h)
52384     {
52385          Roo.log('resize: ' +w + ',' + h );
52386         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
52387         if(!this.iframe){
52388             return;
52389         }
52390         if(typeof w == 'number'){
52391             
52392             this.iframe.style.width = w + 'px';
52393         }
52394         if(typeof h == 'number'){
52395             
52396             this.iframe.style.height = h + 'px';
52397             if(this.doc){
52398                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
52399             }
52400         }
52401         
52402     },
52403
52404     /**
52405      * Toggles the editor between standard and source edit mode.
52406      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
52407      */
52408     toggleSourceEdit : function(sourceEditMode){
52409         
52410         this.sourceEditMode = sourceEditMode === true;
52411         
52412         if(this.sourceEditMode){
52413  
52414             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
52415             
52416         }else{
52417             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
52418             //this.iframe.className = '';
52419             this.deferFocus();
52420         }
52421         //this.setSize(this.owner.wrap.getSize());
52422         //this.fireEvent('editmodechange', this, this.sourceEditMode);
52423     },
52424
52425     
52426   
52427
52428     /**
52429      * Protected method that will not generally be called directly. If you need/want
52430      * custom HTML cleanup, this is the method you should override.
52431      * @param {String} html The HTML to be cleaned
52432      * return {String} The cleaned HTML
52433      */
52434     cleanHtml : function(html)
52435     {
52436         html = String(html);
52437         if(html.length > 5){
52438             if(Roo.isSafari){ // strip safari nonsense
52439                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
52440             }
52441         }
52442         if(html == '&nbsp;'){
52443             html = '';
52444         }
52445         return html;
52446     },
52447
52448     /**
52449      * HTML Editor -> Textarea
52450      * Protected method that will not generally be called directly. Syncs the contents
52451      * of the editor iframe with the textarea.
52452      */
52453     syncValue : function()
52454     {
52455         //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
52456         if(this.initialized){
52457             
52458             if (this.undoManager) {
52459                 this.undoManager.addEvent();
52460             }
52461
52462             
52463             var bd = (this.doc.body || this.doc.documentElement);
52464            
52465             
52466             var sel = this.win.getSelection();
52467             
52468             var div = document.createElement('div');
52469             div.innerHTML = bd.innerHTML;
52470             var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
52471             if (gtx.length > 0) {
52472                 var rm = gtx.item(0).parentNode;
52473                 rm.parentNode.removeChild(rm);
52474             }
52475             
52476            
52477             if (this.enableBlocks) {
52478                 Array.from(bd.getElementsByTagName('img')).forEach(function(img) {
52479                     var fig = img.closest('figure');
52480                     if (fig) {
52481                         var bf = new Roo.htmleditor.BlockFigure({
52482                             node : fig
52483                         });
52484                         bf.updateElement();
52485                     }
52486                     
52487                 });
52488                 new Roo.htmleditor.FilterBlock({ node : div });
52489             }
52490             
52491             var html = div.innerHTML;
52492             
52493             //?? tidy?
52494             if (this.autoClean) {
52495                 new Roo.htmleditor.FilterBlack({ node : div, tag : this.black});
52496                 new Roo.htmleditor.FilterAttributes({
52497                     node : div,
52498                     attrib_white : [
52499                             'href',
52500                             'src',
52501                             'name',
52502                             'align',
52503                             'colspan',
52504                             'rowspan',
52505                             'data-display',
52506                             'data-caption-display',
52507                             'data-width',
52508                             'data-caption',
52509                             'start' ,
52510                             'style',
52511                             // youtube embed.
52512                             'class',
52513                             'allowfullscreen',
52514                             'frameborder',
52515                             'width',
52516                             'height',
52517                             'alt'
52518                             ],
52519                     attrib_clean : ['href', 'src' ] 
52520                 });
52521                 new Roo.htmleditor.FilterEmpty({ node : div});
52522                 
52523                 var tidy = new Roo.htmleditor.TidySerializer({
52524                     inner:  true
52525                 });
52526                 html  = tidy.serialize(div);
52527                 
52528             }
52529             
52530             
52531             if(Roo.isSafari){
52532                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
52533                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
52534                 if(m && m[1]){
52535                     html = '<div style="'+m[0]+'">' + html + '</div>';
52536                 }
52537             }
52538             html = this.cleanHtml(html);
52539             // fix up the special chars.. normaly like back quotes in word...
52540             // however we do not want to do this with chinese..
52541             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
52542                 
52543                 var cc = match.charCodeAt();
52544
52545                 // Get the character value, handling surrogate pairs
52546                 if (match.length == 2) {
52547                     // It's a surrogate pair, calculate the Unicode code point
52548                     var high = match.charCodeAt(0) - 0xD800;
52549                     var low  = match.charCodeAt(1) - 0xDC00;
52550                     cc = (high * 0x400) + low + 0x10000;
52551                 }  else if (
52552                     (cc >= 0x4E00 && cc < 0xA000 ) ||
52553                     (cc >= 0x3400 && cc < 0x4E00 ) ||
52554                     (cc >= 0xf900 && cc < 0xfb00 )
52555                 ) {
52556                         return match;
52557                 }  
52558          
52559                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
52560                 return "&#" + cc + ";";
52561                 
52562                 
52563             });
52564             
52565             
52566              
52567             if(this.owner.fireEvent('beforesync', this, html) !== false){
52568                 this.el.dom.value = html;
52569                 this.owner.fireEvent('sync', this, html);
52570             }
52571         }
52572     },
52573
52574     /**
52575      * TEXTAREA -> EDITABLE
52576      * Protected method that will not generally be called directly. Pushes the value of the textarea
52577      * into the iframe editor.
52578      */
52579     pushValue : function()
52580     {
52581         //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
52582         if(this.initialized){
52583             var v = this.el.dom.value.trim();
52584             
52585             
52586             if(this.owner.fireEvent('beforepush', this, v) !== false){
52587                 var d = (this.doc.body || this.doc.documentElement);
52588                 d.innerHTML = v;
52589                  
52590                 this.el.dom.value = d.innerHTML;
52591                 this.owner.fireEvent('push', this, v);
52592             }
52593             if (this.autoClean) {
52594                 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
52595                 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
52596             }
52597             if (this.enableBlocks) {
52598                 Roo.htmleditor.Block.initAll(this.doc.body);
52599             }
52600             
52601             this.updateLanguage();
52602             
52603             var lc = this.doc.body.lastChild;
52604             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
52605                 // add an extra line at the end.
52606                 this.doc.body.appendChild(this.doc.createElement('br'));
52607             }
52608             
52609             
52610         }
52611     },
52612
52613     // private
52614     deferFocus : function(){
52615         this.focus.defer(10, this);
52616     },
52617
52618     // doc'ed in Field
52619     focus : function(){
52620         if(this.win && !this.sourceEditMode){
52621             this.win.focus();
52622         }else{
52623             this.el.focus();
52624         }
52625     },
52626     
52627     assignDocWin: function()
52628     {
52629         var iframe = this.iframe;
52630         
52631          if(Roo.isIE){
52632             this.doc = iframe.contentWindow.document;
52633             this.win = iframe.contentWindow;
52634         } else {
52635 //            if (!Roo.get(this.frameId)) {
52636 //                return;
52637 //            }
52638 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
52639 //            this.win = Roo.get(this.frameId).dom.contentWindow;
52640             
52641             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
52642                 return;
52643             }
52644             
52645             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
52646             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
52647         }
52648     },
52649     
52650     // private
52651     initEditor : function(){
52652         //console.log("INIT EDITOR");
52653         this.assignDocWin();
52654         
52655         
52656         
52657         this.doc.designMode="on";
52658         this.doc.open();
52659         this.doc.write(this.getDocMarkup());
52660         this.doc.close();
52661         
52662         var dbody = (this.doc.body || this.doc.documentElement);
52663         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
52664         // this copies styles from the containing element into thsi one..
52665         // not sure why we need all of this..
52666         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
52667         
52668         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
52669         //ss['background-attachment'] = 'fixed'; // w3c
52670         dbody.bgProperties = 'fixed'; // ie
52671         dbody.setAttribute("translate", "no");
52672         
52673         //Roo.DomHelper.applyStyles(dbody, ss);
52674         Roo.EventManager.on(this.doc, {
52675              
52676             'mouseup': this.onEditorEvent,
52677             'dblclick': this.onEditorEvent,
52678             'click': this.onEditorEvent,
52679             'keyup': this.onEditorEvent,
52680             
52681             buffer:100,
52682             scope: this
52683         });
52684         Roo.EventManager.on(this.doc, {
52685             'paste': this.onPasteEvent,
52686             scope : this
52687         });
52688         if(Roo.isGecko){
52689             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
52690         }
52691         //??? needed???
52692         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
52693             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
52694         }
52695         this.initialized = true;
52696
52697         
52698         // initialize special key events - enter
52699         new Roo.htmleditor.KeyEnter({core : this});
52700         
52701          
52702         
52703         this.owner.fireEvent('initialize', this);
52704         this.pushValue();
52705     },
52706     // this is to prevent a href clicks resulting in a redirect?
52707    
52708     onPasteEvent : function(e,v)
52709     {
52710         // I think we better assume paste is going to be a dirty load of rubish from word..
52711         
52712         // even pasting into a 'email version' of this widget will have to clean up that mess.
52713         var cd = (e.browserEvent.clipboardData || window.clipboardData);
52714         
52715         // check what type of paste - if it's an image, then handle it differently.
52716         if (cd.files && cd.files.length > 0 && cd.types.indexOf('text/html') < 0) {
52717             // pasting images? 
52718             var urlAPI = (window.createObjectURL && window) || 
52719                 (window.URL && URL.revokeObjectURL && URL) || 
52720                 (window.webkitURL && webkitURL);
52721             
52722             var r = new FileReader();
52723             var t = this;
52724             r.addEventListener('load',function()
52725             {
52726                 
52727                 var d = (new DOMParser().parseFromString('<img src="' + r.result+ '">', 'text/html')).body;
52728                 // is insert asycn?
52729                 if (t.enableBlocks) {
52730                     
52731                     Array.from(d.getElementsByTagName('img')).forEach(function(img) {
52732                         if (img.closest('figure')) { // assume!! that it's aready
52733                             return;
52734                         }
52735                         var fig  = new Roo.htmleditor.BlockFigure({
52736                             image_src  : img.src
52737                         });
52738                         fig.updateElement(img); // replace it..
52739                         
52740                     });
52741                 }
52742                 t.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
52743                 t.owner.fireEvent('paste', this);
52744             });
52745             r.readAsDataURL(cd.files[0]);
52746             
52747             e.preventDefault();
52748             
52749             return false;
52750         }
52751         if (cd.types.indexOf('text/html') < 0 ) {
52752             return false;
52753         }
52754         var images = [];
52755         var html = cd.getData('text/html'); // clipboard event
52756         if (cd.types.indexOf('text/rtf') > -1) {
52757             var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
52758             images = parser.doc ? parser.doc.getElementsByType('pict') : [];
52759         }
52760         // Roo.log(images);
52761         // Roo.log(imgs);
52762         // fixme..
52763         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
52764                        .map(function(g) { return g.toDataURL(); })
52765                        .filter(function(g) { return g != 'about:blank'; });
52766         
52767         //Roo.log(html);
52768         html = this.cleanWordChars(html);
52769         
52770         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
52771         
52772         
52773         var sn = this.getParentElement();
52774         // check if d contains a table, and prevent nesting??
52775         //Roo.log(d.getElementsByTagName('table'));
52776         //Roo.log(sn);
52777         //Roo.log(sn.closest('table'));
52778         if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
52779             e.preventDefault();
52780             this.insertAtCursor("You can not nest tables");
52781             //Roo.log("prevent?"); // fixme - 
52782             return false;
52783         }
52784         
52785         
52786         
52787         if (images.length > 0) {
52788             // replace all v:imagedata - with img.
52789             var ar = Array.from(d.getElementsByTagName('v:imagedata'));
52790             Roo.each(ar, function(node) {
52791                 node.parentNode.insertBefore(d.ownerDocument.createElement('img'), node );
52792                 node.parentNode.removeChild(node);
52793             });
52794             
52795             
52796             Roo.each(d.getElementsByTagName('img'), function(img, i) {
52797                 img.setAttribute('src', images[i]);
52798             });
52799         }
52800         if (this.autoClean) {
52801             new Roo.htmleditor.FilterWord({ node : d });
52802             
52803             new Roo.htmleditor.FilterStyleToTag({ node : d });
52804             new Roo.htmleditor.FilterAttributes({
52805                 node : d,
52806                 attrib_white : [
52807                     'href',
52808                     'src',
52809                     'name',
52810                     'align',
52811                     'colspan',
52812                     'rowspan' 
52813                 /*  THESE ARE NOT ALLWOED FOR PASTE
52814                  *    'data-display',
52815                     'data-caption-display',
52816                     'data-width',
52817                     'data-caption',
52818                     'start' ,
52819                     'style',
52820                     // youtube embed.
52821                     'class',
52822                     'allowfullscreen',
52823                     'frameborder',
52824                     'width',
52825                     'height',
52826                     'alt'
52827                     */
52828                     ],
52829                 attrib_clean : ['href', 'src' ] 
52830             });
52831             new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
52832             // should be fonts..
52833             new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', ':' ]} );
52834             new Roo.htmleditor.FilterParagraph({ node : d });
52835             new Roo.htmleditor.FilterHashLink({node : d});
52836             new Roo.htmleditor.FilterSpan({ node : d });
52837             new Roo.htmleditor.FilterLongBr({ node : d });
52838             new Roo.htmleditor.FilterComment({ node : d });
52839             new Roo.htmleditor.FilterEmpty({ node : d});
52840             
52841             
52842         }
52843         if (this.enableBlocks) {
52844                 
52845             Array.from(d.getElementsByTagName('img')).forEach(function(img) {
52846                 if (img.closest('figure')) { // assume!! that it's aready
52847                     return;
52848                 }
52849                 var fig  = new Roo.htmleditor.BlockFigure({
52850                     image_src  : img.src
52851                 });
52852                 fig.updateElement(img); // replace it..
52853                 
52854             });
52855         }
52856         
52857         
52858         this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
52859         if (this.enableBlocks) {
52860             Roo.htmleditor.Block.initAll(this.doc.body);
52861         }
52862          
52863         
52864         e.preventDefault();
52865         this.owner.fireEvent('paste', this);
52866         return false;
52867         // default behaveiour should be our local cleanup paste? (optional?)
52868         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
52869         //this.owner.fireEvent('paste', e, v);
52870     },
52871     // private
52872     onDestroy : function(){
52873         
52874         
52875         
52876         if(this.rendered){
52877             
52878             //for (var i =0; i < this.toolbars.length;i++) {
52879             //    // fixme - ask toolbars for heights?
52880             //    this.toolbars[i].onDestroy();
52881            // }
52882             
52883             //this.wrap.dom.innerHTML = '';
52884             //this.wrap.remove();
52885         }
52886     },
52887
52888     // private
52889     onFirstFocus : function(){
52890         
52891         this.assignDocWin();
52892         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
52893         
52894         this.activated = true;
52895          
52896     
52897         if(Roo.isGecko){ // prevent silly gecko errors
52898             this.win.focus();
52899             var s = this.win.getSelection();
52900             if(!s.focusNode || s.focusNode.nodeType != 3){
52901                 var r = s.getRangeAt(0);
52902                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
52903                 r.collapse(true);
52904                 this.deferFocus();
52905             }
52906             try{
52907                 this.execCmd('useCSS', true);
52908                 this.execCmd('styleWithCSS', false);
52909             }catch(e){}
52910         }
52911         this.owner.fireEvent('activate', this);
52912     },
52913
52914     // private
52915     adjustFont: function(btn){
52916         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
52917         //if(Roo.isSafari){ // safari
52918         //    adjust *= 2;
52919        // }
52920         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
52921         if(Roo.isSafari){ // safari
52922             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
52923             v =  (v < 10) ? 10 : v;
52924             v =  (v > 48) ? 48 : v;
52925             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
52926             
52927         }
52928         
52929         
52930         v = Math.max(1, v+adjust);
52931         
52932         this.execCmd('FontSize', v  );
52933     },
52934
52935     onEditorEvent : function(e)
52936     {
52937          
52938         
52939         if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
52940             return; // we do not handle this.. (undo manager does..)
52941         }
52942         // clicking a 'block'?
52943         
52944         // in theory this detects if the last element is not a br, then we try and do that.
52945         // its so clicking in space at bottom triggers adding a br and moving the cursor.
52946         if (e &&
52947             e.target.nodeName == 'BODY' &&
52948             e.type == "mouseup" &&
52949             this.doc.body.lastChild
52950            ) {
52951             var lc = this.doc.body.lastChild;
52952             // gtx-trans is google translate plugin adding crap.
52953             while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
52954                 lc = lc.previousSibling;
52955             }
52956             if (lc.nodeType == 1 && lc.nodeName != 'BR') {
52957             // if last element is <BR> - then dont do anything.
52958             
52959                 var ns = this.doc.createElement('br');
52960                 this.doc.body.appendChild(ns);
52961                 range = this.doc.createRange();
52962                 range.setStartAfter(ns);
52963                 range.collapse(true);
52964                 var sel = this.win.getSelection();
52965                 sel.removeAllRanges();
52966                 sel.addRange(range);
52967             }
52968         }
52969         
52970         
52971         
52972         this.fireEditorEvent(e);
52973       //  this.updateToolbar();
52974         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
52975     },
52976     
52977     fireEditorEvent: function(e)
52978     {
52979         this.owner.fireEvent('editorevent', this, e);
52980     },
52981
52982     insertTag : function(tg)
52983     {
52984         // could be a bit smarter... -> wrap the current selected tRoo..
52985         if (tg.toLowerCase() == 'span' ||
52986             tg.toLowerCase() == 'code' ||
52987             tg.toLowerCase() == 'sup' ||
52988             tg.toLowerCase() == 'sub' 
52989             ) {
52990             
52991             range = this.createRange(this.getSelection());
52992             var wrappingNode = this.doc.createElement(tg.toLowerCase());
52993             wrappingNode.appendChild(range.extractContents());
52994             range.insertNode(wrappingNode);
52995
52996             return;
52997             
52998             
52999             
53000         }
53001         this.execCmd("formatblock",   tg);
53002         this.undoManager.addEvent(); 
53003     },
53004     
53005     insertText : function(txt)
53006     {
53007         
53008         
53009         var range = this.createRange();
53010         range.deleteContents();
53011                //alert(Sender.getAttribute('label'));
53012                
53013         range.insertNode(this.doc.createTextNode(txt));
53014         this.undoManager.addEvent();
53015     } ,
53016     
53017      
53018
53019     /**
53020      * Executes a Midas editor command on the editor document and performs necessary focus and
53021      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
53022      * @param {String} cmd The Midas command
53023      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
53024      */
53025     relayCmd : function(cmd, value)
53026     {
53027         
53028         switch (cmd) {
53029             case 'justifyleft':
53030             case 'justifyright':
53031             case 'justifycenter':
53032                 // if we are in a cell, then we will adjust the
53033                 var n = this.getParentElement();
53034                 var td = n.closest('td');
53035                 if (td) {
53036                     var bl = Roo.htmleditor.Block.factory(td);
53037                     bl.textAlign = cmd.replace('justify','');
53038                     bl.updateElement();
53039                     this.owner.fireEvent('editorevent', this);
53040                     return;
53041                 }
53042                 this.execCmd('styleWithCSS', true); // 
53043                 break;
53044             case 'bold':
53045             case 'italic':
53046             case 'underline':                
53047                 // if there is no selection, then we insert, and set the curson inside it..
53048                 this.execCmd('styleWithCSS', false); 
53049                 break;
53050                 
53051         
53052             default:
53053                 break;
53054         }
53055         
53056         
53057         this.win.focus();
53058         this.execCmd(cmd, value);
53059         this.owner.fireEvent('editorevent', this);
53060         //this.updateToolbar();
53061         this.owner.deferFocus();
53062     },
53063
53064     /**
53065      * Executes a Midas editor command directly on the editor document.
53066      * For visual commands, you should use {@link #relayCmd} instead.
53067      * <b>This should only be called after the editor is initialized.</b>
53068      * @param {String} cmd The Midas command
53069      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
53070      */
53071     execCmd : function(cmd, value){
53072         this.doc.execCommand(cmd, false, value === undefined ? null : value);
53073         this.syncValue();
53074     },
53075  
53076  
53077    
53078     /**
53079      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
53080      * to insert tRoo.
53081      * @param {String} text | dom node.. 
53082      */
53083     insertAtCursor : function(text)
53084     {
53085         
53086         if(!this.activated){
53087             return;
53088         }
53089          
53090         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
53091             this.win.focus();
53092             
53093             
53094             // from jquery ui (MIT licenced)
53095             var range, node;
53096             var win = this.win;
53097             
53098             if (win.getSelection && win.getSelection().getRangeAt) {
53099                 
53100                 // delete the existing?
53101                 
53102                 this.createRange(this.getSelection()).deleteContents();
53103                 range = win.getSelection().getRangeAt(0);
53104                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
53105                 range.insertNode(node);
53106                 range = range.cloneRange();
53107                 range.collapse(false);
53108                  
53109                 win.getSelection().removeAllRanges();
53110                 win.getSelection().addRange(range);
53111                 
53112                 
53113                 
53114             } else if (win.document.selection && win.document.selection.createRange) {
53115                 // no firefox support
53116                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
53117                 win.document.selection.createRange().pasteHTML(txt);
53118             
53119             } else {
53120                 // no firefox support
53121                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
53122                 this.execCmd('InsertHTML', txt);
53123             } 
53124             this.syncValue();
53125             
53126             this.deferFocus();
53127         }
53128     },
53129  // private
53130     mozKeyPress : function(e){
53131         if(e.ctrlKey){
53132             var c = e.getCharCode(), cmd;
53133           
53134             if(c > 0){
53135                 c = String.fromCharCode(c).toLowerCase();
53136                 switch(c){
53137                     case 'b':
53138                         cmd = 'bold';
53139                         break;
53140                     case 'i':
53141                         cmd = 'italic';
53142                         break;
53143                     
53144                     case 'u':
53145                         cmd = 'underline';
53146                         break;
53147                     
53148                     //case 'v':
53149                       //  this.cleanUpPaste.defer(100, this);
53150                       //  return;
53151                         
53152                 }
53153                 if(cmd){
53154                     
53155                     this.relayCmd(cmd);
53156                     //this.win.focus();
53157                     //this.execCmd(cmd);
53158                     //this.deferFocus();
53159                     e.preventDefault();
53160                 }
53161                 
53162             }
53163         }
53164     },
53165
53166     // private
53167     fixKeys : function(){ // load time branching for fastest keydown performance
53168         
53169         
53170         if(Roo.isIE){
53171             return function(e){
53172                 var k = e.getKey(), r;
53173                 if(k == e.TAB){
53174                     e.stopEvent();
53175                     r = this.doc.selection.createRange();
53176                     if(r){
53177                         r.collapse(true);
53178                         r.pasteHTML('&#160;&#160;&#160;&#160;');
53179                         this.deferFocus();
53180                     }
53181                     return;
53182                 }
53183                 /// this is handled by Roo.htmleditor.KeyEnter
53184                  /*
53185                 if(k == e.ENTER){
53186                     r = this.doc.selection.createRange();
53187                     if(r){
53188                         var target = r.parentElement();
53189                         if(!target || target.tagName.toLowerCase() != 'li'){
53190                             e.stopEvent();
53191                             r.pasteHTML('<br/>');
53192                             r.collapse(false);
53193                             r.select();
53194                         }
53195                     }
53196                 }
53197                 */
53198                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
53199                 //    this.cleanUpPaste.defer(100, this);
53200                 //    return;
53201                 //}
53202                 
53203                 
53204             };
53205         }else if(Roo.isOpera){
53206             return function(e){
53207                 var k = e.getKey();
53208                 if(k == e.TAB){
53209                     e.stopEvent();
53210                     this.win.focus();
53211                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
53212                     this.deferFocus();
53213                 }
53214                
53215                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
53216                 //    this.cleanUpPaste.defer(100, this);
53217                  //   return;
53218                 //}
53219                 
53220             };
53221         }else if(Roo.isSafari){
53222             return function(e){
53223                 var k = e.getKey();
53224                 
53225                 if(k == e.TAB){
53226                     e.stopEvent();
53227                     this.execCmd('InsertText','\t');
53228                     this.deferFocus();
53229                     return;
53230                 }
53231                  this.mozKeyPress(e);
53232                 
53233                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
53234                  //   this.cleanUpPaste.defer(100, this);
53235                  //   return;
53236                // }
53237                 
53238              };
53239         }
53240     }(),
53241     
53242     getAllAncestors: function()
53243     {
53244         var p = this.getSelectedNode();
53245         var a = [];
53246         if (!p) {
53247             a.push(p); // push blank onto stack..
53248             p = this.getParentElement();
53249         }
53250         
53251         
53252         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
53253             a.push(p);
53254             p = p.parentNode;
53255         }
53256         a.push(this.doc.body);
53257         return a;
53258     },
53259     lastSel : false,
53260     lastSelNode : false,
53261     
53262     
53263     getSelection : function() 
53264     {
53265         this.assignDocWin();
53266         return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
53267     },
53268     /**
53269      * Select a dom node
53270      * @param {DomElement} node the node to select
53271      */
53272     selectNode : function(node, collapse)
53273     {
53274         var nodeRange = node.ownerDocument.createRange();
53275         try {
53276             nodeRange.selectNode(node);
53277         } catch (e) {
53278             nodeRange.selectNodeContents(node);
53279         }
53280         if (collapse === true) {
53281             nodeRange.collapse(true);
53282         }
53283         //
53284         var s = this.win.getSelection();
53285         s.removeAllRanges();
53286         s.addRange(nodeRange);
53287     },
53288     
53289     getSelectedNode: function() 
53290     {
53291         // this may only work on Gecko!!!
53292         
53293         // should we cache this!!!!
53294         
53295          
53296          
53297         var range = this.createRange(this.getSelection()).cloneRange();
53298         
53299         if (Roo.isIE) {
53300             var parent = range.parentElement();
53301             while (true) {
53302                 var testRange = range.duplicate();
53303                 testRange.moveToElementText(parent);
53304                 if (testRange.inRange(range)) {
53305                     break;
53306                 }
53307                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
53308                     break;
53309                 }
53310                 parent = parent.parentElement;
53311             }
53312             return parent;
53313         }
53314         
53315         // is ancestor a text element.
53316         var ac =  range.commonAncestorContainer;
53317         if (ac.nodeType == 3) {
53318             ac = ac.parentNode;
53319         }
53320         
53321         var ar = ac.childNodes;
53322          
53323         var nodes = [];
53324         var other_nodes = [];
53325         var has_other_nodes = false;
53326         for (var i=0;i<ar.length;i++) {
53327             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
53328                 continue;
53329             }
53330             // fullly contained node.
53331             
53332             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
53333                 nodes.push(ar[i]);
53334                 continue;
53335             }
53336             
53337             // probably selected..
53338             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
53339                 other_nodes.push(ar[i]);
53340                 continue;
53341             }
53342             // outer..
53343             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
53344                 continue;
53345             }
53346             
53347             
53348             has_other_nodes = true;
53349         }
53350         if (!nodes.length && other_nodes.length) {
53351             nodes= other_nodes;
53352         }
53353         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
53354             return false;
53355         }
53356         
53357         return nodes[0];
53358     },
53359     
53360     
53361     createRange: function(sel)
53362     {
53363         // this has strange effects when using with 
53364         // top toolbar - not sure if it's a great idea.
53365         //this.editor.contentWindow.focus();
53366         if (typeof sel != "undefined") {
53367             try {
53368                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
53369             } catch(e) {
53370                 return this.doc.createRange();
53371             }
53372         } else {
53373             return this.doc.createRange();
53374         }
53375     },
53376     getParentElement: function()
53377     {
53378         
53379         this.assignDocWin();
53380         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
53381         
53382         var range = this.createRange(sel);
53383          
53384         try {
53385             var p = range.commonAncestorContainer;
53386             while (p.nodeType == 3) { // text node
53387                 p = p.parentNode;
53388             }
53389             return p;
53390         } catch (e) {
53391             return null;
53392         }
53393     
53394     },
53395     /***
53396      *
53397      * Range intersection.. the hard stuff...
53398      *  '-1' = before
53399      *  '0' = hits..
53400      *  '1' = after.
53401      *         [ -- selected range --- ]
53402      *   [fail]                        [fail]
53403      *
53404      *    basically..
53405      *      if end is before start or  hits it. fail.
53406      *      if start is after end or hits it fail.
53407      *
53408      *   if either hits (but other is outside. - then it's not 
53409      *   
53410      *    
53411      **/
53412     
53413     
53414     // @see http://www.thismuchiknow.co.uk/?p=64.
53415     rangeIntersectsNode : function(range, node)
53416     {
53417         var nodeRange = node.ownerDocument.createRange();
53418         try {
53419             nodeRange.selectNode(node);
53420         } catch (e) {
53421             nodeRange.selectNodeContents(node);
53422         }
53423     
53424         var rangeStartRange = range.cloneRange();
53425         rangeStartRange.collapse(true);
53426     
53427         var rangeEndRange = range.cloneRange();
53428         rangeEndRange.collapse(false);
53429     
53430         var nodeStartRange = nodeRange.cloneRange();
53431         nodeStartRange.collapse(true);
53432     
53433         var nodeEndRange = nodeRange.cloneRange();
53434         nodeEndRange.collapse(false);
53435     
53436         return rangeStartRange.compareBoundaryPoints(
53437                  Range.START_TO_START, nodeEndRange) == -1 &&
53438                rangeEndRange.compareBoundaryPoints(
53439                  Range.START_TO_START, nodeStartRange) == 1;
53440         
53441          
53442     },
53443     rangeCompareNode : function(range, node)
53444     {
53445         var nodeRange = node.ownerDocument.createRange();
53446         try {
53447             nodeRange.selectNode(node);
53448         } catch (e) {
53449             nodeRange.selectNodeContents(node);
53450         }
53451         
53452         
53453         range.collapse(true);
53454     
53455         nodeRange.collapse(true);
53456      
53457         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
53458         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
53459          
53460         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
53461         
53462         var nodeIsBefore   =  ss == 1;
53463         var nodeIsAfter    = ee == -1;
53464         
53465         if (nodeIsBefore && nodeIsAfter) {
53466             return 0; // outer
53467         }
53468         if (!nodeIsBefore && nodeIsAfter) {
53469             return 1; //right trailed.
53470         }
53471         
53472         if (nodeIsBefore && !nodeIsAfter) {
53473             return 2;  // left trailed.
53474         }
53475         // fully contined.
53476         return 3;
53477     },
53478  
53479     cleanWordChars : function(input) {// change the chars to hex code
53480         
53481        var swapCodes  = [ 
53482             [    8211, "&#8211;" ], 
53483             [    8212, "&#8212;" ], 
53484             [    8216,  "'" ],  
53485             [    8217, "'" ],  
53486             [    8220, '"' ],  
53487             [    8221, '"' ],  
53488             [    8226, "*" ],  
53489             [    8230, "..." ]
53490         ]; 
53491         var output = input;
53492         Roo.each(swapCodes, function(sw) { 
53493             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
53494             
53495             output = output.replace(swapper, sw[1]);
53496         });
53497         
53498         return output;
53499     },
53500     
53501      
53502     
53503         
53504     
53505     cleanUpChild : function (node)
53506     {
53507         
53508         new Roo.htmleditor.FilterComment({node : node});
53509         new Roo.htmleditor.FilterAttributes({
53510                 node : node,
53511                 attrib_black : this.ablack,
53512                 attrib_clean : this.aclean,
53513                 style_white : this.cwhite,
53514                 style_black : this.cblack
53515         });
53516         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
53517         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
53518          
53519         
53520     },
53521     
53522     /**
53523      * Clean up MS wordisms...
53524      * @deprecated - use filter directly
53525      */
53526     cleanWord : function(node)
53527     {
53528         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
53529         new Roo.htmleditor.FilterKeepChildren({node : node ? node : this.doc.body, tag : [ 'FONT', ':' ]} );
53530         
53531     },
53532    
53533     
53534     /**
53535
53536      * @deprecated - use filters
53537      */
53538     cleanTableWidths : function(node)
53539     {
53540         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
53541         
53542  
53543     },
53544     
53545      
53546         
53547     applyBlacklists : function()
53548     {
53549         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
53550         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
53551         
53552         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
53553         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
53554         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
53555         
53556         this.white = [];
53557         this.black = [];
53558         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
53559             if (b.indexOf(tag) > -1) {
53560                 return;
53561             }
53562             this.white.push(tag);
53563             
53564         }, this);
53565         
53566         Roo.each(w, function(tag) {
53567             if (b.indexOf(tag) > -1) {
53568                 return;
53569             }
53570             if (this.white.indexOf(tag) > -1) {
53571                 return;
53572             }
53573             this.white.push(tag);
53574             
53575         }, this);
53576         
53577         
53578         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
53579             if (w.indexOf(tag) > -1) {
53580                 return;
53581             }
53582             this.black.push(tag);
53583             
53584         }, this);
53585         
53586         Roo.each(b, function(tag) {
53587             if (w.indexOf(tag) > -1) {
53588                 return;
53589             }
53590             if (this.black.indexOf(tag) > -1) {
53591                 return;
53592             }
53593             this.black.push(tag);
53594             
53595         }, this);
53596         
53597         
53598         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
53599         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
53600         
53601         this.cwhite = [];
53602         this.cblack = [];
53603         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
53604             if (b.indexOf(tag) > -1) {
53605                 return;
53606             }
53607             this.cwhite.push(tag);
53608             
53609         }, this);
53610         
53611         Roo.each(w, function(tag) {
53612             if (b.indexOf(tag) > -1) {
53613                 return;
53614             }
53615             if (this.cwhite.indexOf(tag) > -1) {
53616                 return;
53617             }
53618             this.cwhite.push(tag);
53619             
53620         }, this);
53621         
53622         
53623         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
53624             if (w.indexOf(tag) > -1) {
53625                 return;
53626             }
53627             this.cblack.push(tag);
53628             
53629         }, this);
53630         
53631         Roo.each(b, function(tag) {
53632             if (w.indexOf(tag) > -1) {
53633                 return;
53634             }
53635             if (this.cblack.indexOf(tag) > -1) {
53636                 return;
53637             }
53638             this.cblack.push(tag);
53639             
53640         }, this);
53641     },
53642     
53643     setStylesheets : function(stylesheets)
53644     {
53645         if(typeof(stylesheets) == 'string'){
53646             Roo.get(this.iframe.contentDocument.head).createChild({
53647                 tag : 'link',
53648                 rel : 'stylesheet',
53649                 type : 'text/css',
53650                 href : stylesheets
53651             });
53652             
53653             return;
53654         }
53655         var _this = this;
53656      
53657         Roo.each(stylesheets, function(s) {
53658             if(!s.length){
53659                 return;
53660             }
53661             
53662             Roo.get(_this.iframe.contentDocument.head).createChild({
53663                 tag : 'link',
53664                 rel : 'stylesheet',
53665                 type : 'text/css',
53666                 href : s
53667             });
53668         });
53669
53670         
53671     },
53672     
53673     
53674     updateLanguage : function()
53675     {
53676         if (!this.iframe || !this.iframe.contentDocument) {
53677             return;
53678         }
53679         Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
53680     },
53681     
53682     
53683     removeStylesheets : function()
53684     {
53685         var _this = this;
53686         
53687         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
53688             s.remove();
53689         });
53690     },
53691     
53692     setStyle : function(style)
53693     {
53694         Roo.get(this.iframe.contentDocument.head).createChild({
53695             tag : 'style',
53696             type : 'text/css',
53697             html : style
53698         });
53699
53700         return;
53701     }
53702     
53703     // hide stuff that is not compatible
53704     /**
53705      * @event blur
53706      * @hide
53707      */
53708     /**
53709      * @event change
53710      * @hide
53711      */
53712     /**
53713      * @event focus
53714      * @hide
53715      */
53716     /**
53717      * @event specialkey
53718      * @hide
53719      */
53720     /**
53721      * @cfg {String} fieldClass @hide
53722      */
53723     /**
53724      * @cfg {String} focusClass @hide
53725      */
53726     /**
53727      * @cfg {String} autoCreate @hide
53728      */
53729     /**
53730      * @cfg {String} inputType @hide
53731      */
53732     /**
53733      * @cfg {String} invalidClass @hide
53734      */
53735     /**
53736      * @cfg {String} invalidText @hide
53737      */
53738     /**
53739      * @cfg {String} msgFx @hide
53740      */
53741     /**
53742      * @cfg {String} validateOnBlur @hide
53743      */
53744 });
53745
53746 Roo.HtmlEditorCore.white = [
53747         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
53748         
53749        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
53750        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
53751        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
53752        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
53753        'TABLE',   'UL',         'XMP', 
53754        
53755        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
53756       'THEAD',   'TR', 
53757      
53758       'DIR', 'MENU', 'OL', 'UL', 'DL',
53759        
53760       'EMBED',  'OBJECT'
53761 ];
53762
53763
53764 Roo.HtmlEditorCore.black = [
53765     //    'embed',  'object', // enable - backend responsiblity to clean thiese
53766         'APPLET', // 
53767         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
53768         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
53769         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
53770         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
53771         //'FONT' // CLEAN LATER..
53772         'COLGROUP', 'COL'   // messy tables.
53773         
53774         
53775 ];
53776 Roo.HtmlEditorCore.clean = [ // ?? needed???
53777      'SCRIPT', 'STYLE', 'TITLE', 'XML'
53778 ];
53779 Roo.HtmlEditorCore.tag_remove = [
53780     'FONT', 'TBODY'  
53781 ];
53782 // attributes..
53783
53784 Roo.HtmlEditorCore.ablack = [
53785     'on'
53786 ];
53787     
53788 Roo.HtmlEditorCore.aclean = [ 
53789     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
53790 ];
53791
53792 // protocols..
53793 Roo.HtmlEditorCore.pwhite= [
53794         'http',  'https',  'mailto'
53795 ];
53796
53797 // white listed style attributes.
53798 Roo.HtmlEditorCore.cwhite= [
53799       //  'text-align', /// default is to allow most things..
53800       
53801          
53802 //        'font-size'//??
53803 ];
53804
53805 // black listed style attributes.
53806 Roo.HtmlEditorCore.cblack= [
53807       //  'font-size' -- this can be set by the project 
53808 ];
53809
53810
53811
53812
53813     //<script type="text/javascript">
53814
53815 /*
53816  * Ext JS Library 1.1.1
53817  * Copyright(c) 2006-2007, Ext JS, LLC.
53818  * Licence LGPL
53819  * 
53820  */
53821  
53822  
53823 Roo.form.HtmlEditor = function(config){
53824     
53825     
53826     
53827     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
53828     
53829     if (!this.toolbars) {
53830         this.toolbars = [];
53831     }
53832     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
53833     
53834     
53835 };
53836
53837 /**
53838  * @class Roo.form.HtmlEditor
53839  * @extends Roo.form.Field
53840  * Provides a lightweight HTML Editor component.
53841  *
53842  * This has been tested on Fireforx / Chrome.. IE may not be so great..
53843  * 
53844  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
53845  * supported by this editor.</b><br/><br/>
53846  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
53847  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
53848  */
53849 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
53850     /**
53851      * @cfg {Boolean} clearUp
53852      */
53853     clearUp : true,
53854       /**
53855      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
53856      */
53857     toolbars : false,
53858    
53859      /**
53860      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
53861      *                        Roo.resizable.
53862      */
53863     resizable : false,
53864      /**
53865      * @cfg {Number} height (in pixels)
53866      */   
53867     height: 300,
53868    /**
53869      * @cfg {Number} width (in pixels)
53870      */   
53871     width: 500,
53872     
53873     /**
53874      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea  rootURL + '/roojs1/css/undoreset.css',   .
53875      * 
53876      */
53877     stylesheets: false,
53878     
53879     
53880      /**
53881      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
53882      * 
53883      */
53884     cblack: false,
53885     /**
53886      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
53887      * 
53888      */
53889     cwhite: false,
53890     
53891      /**
53892      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
53893      * 
53894      */
53895     black: false,
53896     /**
53897      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
53898      * 
53899      */
53900     white: false,
53901     /**
53902      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
53903      */
53904     allowComments: false,
53905     /**
53906      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
53907      */
53908     enableBlocks : true,
53909     
53910     /**
53911      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
53912      *         if you are doing an email editor, this probably needs disabling, it's designed
53913      */
53914     autoClean: true,
53915     /**
53916      * @cfg {string} bodyCls default '' default classes to add to body of editable area - usually undoreset is a good start..
53917      */
53918     bodyCls : '',
53919     /**
53920      * @cfg {String} language default en - language of text (usefull for rtl languages)
53921      * 
53922      */
53923     language: 'en',
53924     
53925      
53926     // id of frame..
53927     frameId: false,
53928     
53929     // private properties
53930     validationEvent : false,
53931     deferHeight: true,
53932     initialized : false,
53933     activated : false,
53934     
53935     onFocus : Roo.emptyFn,
53936     iframePad:3,
53937     hideMode:'offsets',
53938     
53939     actionMode : 'container', // defaults to hiding it...
53940     
53941     defaultAutoCreate : { // modified by initCompnoent..
53942         tag: "textarea",
53943         style:"width:500px;height:300px;",
53944         autocomplete: "new-password"
53945     },
53946
53947     // private
53948     initComponent : function(){
53949         this.addEvents({
53950             /**
53951              * @event initialize
53952              * Fires when the editor is fully initialized (including the iframe)
53953              * @param {HtmlEditor} this
53954              */
53955             initialize: true,
53956             /**
53957              * @event activate
53958              * Fires when the editor is first receives the focus. Any insertion must wait
53959              * until after this event.
53960              * @param {HtmlEditor} this
53961              */
53962             activate: true,
53963              /**
53964              * @event beforesync
53965              * Fires before the textarea is updated with content from the editor iframe. Return false
53966              * to cancel the sync.
53967              * @param {HtmlEditor} this
53968              * @param {String} html
53969              */
53970             beforesync: true,
53971              /**
53972              * @event beforepush
53973              * Fires before the iframe editor is updated with content from the textarea. Return false
53974              * to cancel the push.
53975              * @param {HtmlEditor} this
53976              * @param {String} html
53977              */
53978             beforepush: true,
53979              /**
53980              * @event sync
53981              * Fires when the textarea is updated with content from the editor iframe.
53982              * @param {HtmlEditor} this
53983              * @param {String} html
53984              */
53985             sync: true,
53986              /**
53987              * @event push
53988              * Fires when the iframe editor is updated with content from the textarea.
53989              * @param {HtmlEditor} this
53990              * @param {String} html
53991              */
53992             push: true,
53993              /**
53994              * @event editmodechange
53995              * Fires when the editor switches edit modes
53996              * @param {HtmlEditor} this
53997              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
53998              */
53999             editmodechange: true,
54000             /**
54001              * @event editorevent
54002              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
54003              * @param {HtmlEditor} this
54004              */
54005             editorevent: true,
54006             /**
54007              * @event firstfocus
54008              * Fires when on first focus - needed by toolbars..
54009              * @param {HtmlEditor} this
54010              */
54011             firstfocus: true,
54012             /**
54013              * @event autosave
54014              * Auto save the htmlEditor value as a file into Events
54015              * @param {HtmlEditor} this
54016              */
54017             autosave: true,
54018             /**
54019              * @event savedpreview
54020              * preview the saved version of htmlEditor
54021              * @param {HtmlEditor} this
54022              */
54023             savedpreview: true,
54024             
54025             /**
54026             * @event stylesheetsclick
54027             * Fires when press the Sytlesheets button
54028             * @param {Roo.HtmlEditorCore} this
54029             */
54030             stylesheetsclick: true,
54031             /**
54032             * @event paste
54033             * Fires when press user pastes into the editor
54034             * @param {Roo.HtmlEditorCore} this
54035             */
54036             paste: true 
54037             
54038         });
54039         this.defaultAutoCreate =  {
54040             tag: "textarea",
54041             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
54042             autocomplete: "new-password"
54043         };
54044     },
54045
54046     /**
54047      * Protected method that will not generally be called directly. It
54048      * is called when the editor creates its toolbar. Override this method if you need to
54049      * add custom toolbar buttons.
54050      * @param {HtmlEditor} editor
54051      */
54052     createToolbar : function(editor){
54053         Roo.log("create toolbars");
54054         if (!editor.toolbars || !editor.toolbars.length) {
54055             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
54056         }
54057         
54058         for (var i =0 ; i < editor.toolbars.length;i++) {
54059             editor.toolbars[i] = Roo.factory(
54060                     typeof(editor.toolbars[i]) == 'string' ?
54061                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
54062                 Roo.form.HtmlEditor);
54063             editor.toolbars[i].init(editor);
54064         }
54065          
54066         
54067     },
54068     /**
54069      * get the Context selected node
54070      * @returns {DomElement|boolean} selected node if active or false if none
54071      * 
54072      */
54073     getSelectedNode : function()
54074     {
54075         if (this.toolbars.length < 2 || !this.toolbars[1].tb) {
54076             return false;
54077         }
54078         return this.toolbars[1].tb.selectedNode;
54079     
54080     },
54081     // private
54082     onRender : function(ct, position)
54083     {
54084         var _t = this;
54085         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
54086         
54087         this.wrap = this.el.wrap({
54088             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
54089         });
54090         
54091         this.editorcore.onRender(ct, position);
54092          
54093         if (this.resizable) {
54094             this.resizeEl = new Roo.Resizable(this.wrap, {
54095                 pinned : true,
54096                 wrap: true,
54097                 dynamic : true,
54098                 minHeight : this.height,
54099                 height: this.height,
54100                 handles : this.resizable,
54101                 width: this.width,
54102                 listeners : {
54103                     resize : function(r, w, h) {
54104                         _t.onResize(w,h); // -something
54105                     }
54106                 }
54107             });
54108             
54109         }
54110         this.createToolbar(this);
54111        
54112         
54113         if(!this.width){
54114             this.setSize(this.wrap.getSize());
54115         }
54116         if (this.resizeEl) {
54117             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
54118             // should trigger onReize..
54119         }
54120         
54121         this.keyNav = new Roo.KeyNav(this.el, {
54122             
54123             "tab" : function(e){
54124                 e.preventDefault();
54125                 
54126                 var value = this.getValue();
54127                 
54128                 var start = this.el.dom.selectionStart;
54129                 var end = this.el.dom.selectionEnd;
54130                 
54131                 if(!e.shiftKey){
54132                     
54133                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
54134                     this.el.dom.setSelectionRange(end + 1, end + 1);
54135                     return;
54136                 }
54137                 
54138                 var f = value.substring(0, start).split("\t");
54139                 
54140                 if(f.pop().length != 0){
54141                     return;
54142                 }
54143                 
54144                 this.setValue(f.join("\t") + value.substring(end));
54145                 this.el.dom.setSelectionRange(start - 1, start - 1);
54146                 
54147             },
54148             
54149             "home" : function(e){
54150                 e.preventDefault();
54151                 
54152                 var curr = this.el.dom.selectionStart;
54153                 var lines = this.getValue().split("\n");
54154                 
54155                 if(!lines.length){
54156                     return;
54157                 }
54158                 
54159                 if(e.ctrlKey){
54160                     this.el.dom.setSelectionRange(0, 0);
54161                     return;
54162                 }
54163                 
54164                 var pos = 0;
54165                 
54166                 for (var i = 0; i < lines.length;i++) {
54167                     pos += lines[i].length;
54168                     
54169                     if(i != 0){
54170                         pos += 1;
54171                     }
54172                     
54173                     if(pos < curr){
54174                         continue;
54175                     }
54176                     
54177                     pos -= lines[i].length;
54178                     
54179                     break;
54180                 }
54181                 
54182                 if(!e.shiftKey){
54183                     this.el.dom.setSelectionRange(pos, pos);
54184                     return;
54185                 }
54186                 
54187                 this.el.dom.selectionStart = pos;
54188                 this.el.dom.selectionEnd = curr;
54189             },
54190             
54191             "end" : function(e){
54192                 e.preventDefault();
54193                 
54194                 var curr = this.el.dom.selectionStart;
54195                 var lines = this.getValue().split("\n");
54196                 
54197                 if(!lines.length){
54198                     return;
54199                 }
54200                 
54201                 if(e.ctrlKey){
54202                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
54203                     return;
54204                 }
54205                 
54206                 var pos = 0;
54207                 
54208                 for (var i = 0; i < lines.length;i++) {
54209                     
54210                     pos += lines[i].length;
54211                     
54212                     if(i != 0){
54213                         pos += 1;
54214                     }
54215                     
54216                     if(pos < curr){
54217                         continue;
54218                     }
54219                     
54220                     break;
54221                 }
54222                 
54223                 if(!e.shiftKey){
54224                     this.el.dom.setSelectionRange(pos, pos);
54225                     return;
54226                 }
54227                 
54228                 this.el.dom.selectionStart = curr;
54229                 this.el.dom.selectionEnd = pos;
54230             },
54231
54232             scope : this,
54233
54234             doRelay : function(foo, bar, hname){
54235                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
54236             },
54237
54238             forceKeyDown: true
54239         });
54240         
54241 //        if(this.autosave && this.w){
54242 //            this.autoSaveFn = setInterval(this.autosave, 1000);
54243 //        }
54244     },
54245
54246     // private
54247     onResize : function(w, h)
54248     {
54249         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
54250         var ew = false;
54251         var eh = false;
54252         
54253         if(this.el ){
54254             if(typeof w == 'number'){
54255                 var aw = w - this.wrap.getFrameWidth('lr');
54256                 this.el.setWidth(this.adjustWidth('textarea', aw));
54257                 ew = aw;
54258             }
54259             if(typeof h == 'number'){
54260                 var tbh = 0;
54261                 for (var i =0; i < this.toolbars.length;i++) {
54262                     // fixme - ask toolbars for heights?
54263                     tbh += this.toolbars[i].tb.el.getHeight();
54264                     if (this.toolbars[i].footer) {
54265                         tbh += this.toolbars[i].footer.el.getHeight();
54266                     }
54267                 }
54268                 
54269                 
54270                 
54271                 
54272                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
54273                 ah -= 5; // knock a few pixes off for look..
54274 //                Roo.log(ah);
54275                 this.el.setHeight(this.adjustWidth('textarea', ah));
54276                 var eh = ah;
54277             }
54278         }
54279         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
54280         this.editorcore.onResize(ew,eh);
54281         
54282     },
54283
54284     /**
54285      * Toggles the editor between standard and source edit mode.
54286      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
54287      */
54288     toggleSourceEdit : function(sourceEditMode)
54289     {
54290         this.editorcore.toggleSourceEdit(sourceEditMode);
54291         
54292         if(this.editorcore.sourceEditMode){
54293             Roo.log('editor - showing textarea');
54294             
54295 //            Roo.log('in');
54296 //            Roo.log(this.syncValue());
54297             this.editorcore.syncValue();
54298             this.el.removeClass('x-hidden');
54299             this.el.dom.removeAttribute('tabIndex');
54300             this.el.focus();
54301             this.el.dom.scrollTop = 0;
54302             
54303             
54304             for (var i = 0; i < this.toolbars.length; i++) {
54305                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
54306                     this.toolbars[i].tb.hide();
54307                     this.toolbars[i].footer.hide();
54308                 }
54309             }
54310             
54311         }else{
54312             Roo.log('editor - hiding textarea');
54313 //            Roo.log('out')
54314 //            Roo.log(this.pushValue()); 
54315             this.editorcore.pushValue();
54316             
54317             this.el.addClass('x-hidden');
54318             this.el.dom.setAttribute('tabIndex', -1);
54319             
54320             for (var i = 0; i < this.toolbars.length; i++) {
54321                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
54322                     this.toolbars[i].tb.show();
54323                     this.toolbars[i].footer.show();
54324                 }
54325             }
54326             
54327             //this.deferFocus();
54328         }
54329         
54330         this.setSize(this.wrap.getSize());
54331         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
54332         
54333         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
54334     },
54335  
54336     // private (for BoxComponent)
54337     adjustSize : Roo.BoxComponent.prototype.adjustSize,
54338
54339     // private (for BoxComponent)
54340     getResizeEl : function(){
54341         return this.wrap;
54342     },
54343
54344     // private (for BoxComponent)
54345     getPositionEl : function(){
54346         return this.wrap;
54347     },
54348
54349     // private
54350     initEvents : function(){
54351         this.originalValue = this.getValue();
54352     },
54353
54354     /**
54355      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
54356      * @method
54357      */
54358     markInvalid : Roo.emptyFn,
54359     /**
54360      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
54361      * @method
54362      */
54363     clearInvalid : Roo.emptyFn,
54364
54365     setValue : function(v){
54366         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
54367         this.editorcore.pushValue();
54368     },
54369
54370     /**
54371      * update the language in the body - really done by core
54372      * @param {String} language - eg. en / ar / zh-CN etc..
54373      */
54374     updateLanguage : function(lang)
54375     {
54376         this.language = lang;
54377         this.editorcore.language = lang;
54378         this.editorcore.updateLanguage();
54379      
54380     },
54381     // private
54382     deferFocus : function(){
54383         this.focus.defer(10, this);
54384     },
54385
54386     // doc'ed in Field
54387     focus : function(){
54388         this.editorcore.focus();
54389         
54390     },
54391       
54392
54393     // private
54394     onDestroy : function(){
54395         
54396         
54397         
54398         if(this.rendered){
54399             
54400             for (var i =0; i < this.toolbars.length;i++) {
54401                 // fixme - ask toolbars for heights?
54402                 this.toolbars[i].onDestroy();
54403             }
54404             
54405             this.wrap.dom.innerHTML = '';
54406             this.wrap.remove();
54407         }
54408     },
54409
54410     // private
54411     onFirstFocus : function(){
54412         //Roo.log("onFirstFocus");
54413         this.editorcore.onFirstFocus();
54414          for (var i =0; i < this.toolbars.length;i++) {
54415             this.toolbars[i].onFirstFocus();
54416         }
54417         
54418     },
54419     
54420     // private
54421     syncValue : function()
54422     {
54423         this.editorcore.syncValue();
54424     },
54425     
54426     pushValue : function()
54427     {
54428         this.editorcore.pushValue();
54429     },
54430     
54431     setStylesheets : function(stylesheets)
54432     {
54433         this.editorcore.setStylesheets(stylesheets);
54434     },
54435     
54436     removeStylesheets : function()
54437     {
54438         this.editorcore.removeStylesheets();
54439     }
54440      
54441     
54442     // hide stuff that is not compatible
54443     /**
54444      * @event blur
54445      * @hide
54446      */
54447     /**
54448      * @event change
54449      * @hide
54450      */
54451     /**
54452      * @event focus
54453      * @hide
54454      */
54455     /**
54456      * @event specialkey
54457      * @hide
54458      */
54459     /**
54460      * @cfg {String} fieldClass @hide
54461      */
54462     /**
54463      * @cfg {String} focusClass @hide
54464      */
54465     /**
54466      * @cfg {String} autoCreate @hide
54467      */
54468     /**
54469      * @cfg {String} inputType @hide
54470      */
54471     /**
54472      * @cfg {String} invalidClass @hide
54473      */
54474     /**
54475      * @cfg {String} invalidText @hide
54476      */
54477     /**
54478      * @cfg {String} msgFx @hide
54479      */
54480     /**
54481      * @cfg {String} validateOnBlur @hide
54482      */
54483 });
54484  
54485     /*
54486  * Based on
54487  * Ext JS Library 1.1.1
54488  * Copyright(c) 2006-2007, Ext JS, LLC.
54489  *  
54490  
54491  */
54492
54493 /**
54494  * @class Roo.form.HtmlEditor.ToolbarStandard
54495  * Basic Toolbar
54496
54497  * Usage:
54498  *
54499  new Roo.form.HtmlEditor({
54500     ....
54501     toolbars : [
54502         new Roo.form.HtmlEditorToolbar1({
54503             disable : { fonts: 1 , format: 1, ..., ... , ...],
54504             btns : [ .... ]
54505         })
54506     }
54507      
54508  * 
54509  * @cfg {Object} disable List of elements to disable..
54510  * @cfg {Roo.Toolbar.Item|Roo.Toolbar.Button|Roo.Toolbar.SplitButton|Roo.form.Field} btns[] List of additional buttons.
54511  * 
54512  * 
54513  * NEEDS Extra CSS? 
54514  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
54515  */
54516  
54517 Roo.form.HtmlEditor.ToolbarStandard = function(config)
54518 {
54519     
54520     Roo.apply(this, config);
54521     
54522     // default disabled, based on 'good practice'..
54523     this.disable = this.disable || {};
54524     Roo.applyIf(this.disable, {
54525         fontSize : true,
54526         colors : true,
54527         specialElements : true
54528     });
54529     
54530     
54531     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
54532     // dont call parent... till later.
54533 }
54534
54535 Roo.form.HtmlEditor.ToolbarStandard.prototype = {
54536     
54537     tb: false,
54538     
54539     rendered: false,
54540     
54541     editor : false,
54542     editorcore : false,
54543     /**
54544      * @cfg {Object} disable  List of toolbar elements to disable
54545          
54546      */
54547     disable : false,
54548     
54549     
54550      /**
54551      * @cfg {String} createLinkText The default text for the create link prompt
54552      */
54553     createLinkText : 'Please enter the URL for the link:',
54554     /**
54555      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
54556      */
54557     defaultLinkValue : 'http:/'+'/',
54558    
54559     
54560       /**
54561      * @cfg {Array} fontFamilies An array of available font families
54562      */
54563     fontFamilies : [
54564         'Arial',
54565         'Courier New',
54566         'Tahoma',
54567         'Times New Roman',
54568         'Verdana'
54569     ],
54570     
54571     specialChars : [
54572            "&#169;",
54573           "&#174;",     
54574           "&#8482;",    
54575           "&#163;" ,    
54576          // "&#8212;",    
54577           "&#8230;",    
54578           "&#247;" ,    
54579         //  "&#225;" ,     ?? a acute?
54580            "&#8364;"    , //Euro
54581        //   "&#8220;"    ,
54582         //  "&#8221;"    ,
54583         //  "&#8226;"    ,
54584           "&#176;"  //   , // degrees
54585
54586          // "&#233;"     , // e ecute
54587          // "&#250;"     , // u ecute?
54588     ],
54589     
54590     specialElements : [
54591         {
54592             text: "Insert Table",
54593             xtype: 'MenuItem',
54594             xns : Roo.Menu,
54595             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
54596                 
54597         },
54598         {    
54599             text: "Insert Image",
54600             xtype: 'MenuItem',
54601             xns : Roo.Menu,
54602             ihtml : '<img src="about:blank"/>'
54603             
54604         }
54605         
54606          
54607     ],
54608     
54609     
54610     inputElements : [ 
54611             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
54612             "input:submit", "input:button", "select", "textarea", "label" ],
54613     formats : [
54614         ["p"] ,  
54615         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
54616         ["pre"],[ "code"], 
54617         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
54618         ['div'],['span'],
54619         ['sup'],['sub']
54620     ],
54621     
54622     cleanStyles : [
54623         "font-size"
54624     ],
54625      /**
54626      * @cfg {String} defaultFont default font to use.
54627      */
54628     defaultFont: 'tahoma',
54629    
54630     fontSelect : false,
54631     
54632     
54633     formatCombo : false,
54634     
54635     init : function(editor)
54636     {
54637         this.editor = editor;
54638         this.editorcore = editor.editorcore ? editor.editorcore : editor;
54639         var editorcore = this.editorcore;
54640         
54641         var _t = this;
54642         
54643         var fid = editorcore.frameId;
54644         var etb = this;
54645         function btn(id, toggle, handler){
54646             var xid = fid + '-'+ id ;
54647             return {
54648                 id : xid,
54649                 cmd : id,
54650                 cls : 'x-btn-icon x-edit-'+id,
54651                 enableToggle:toggle !== false,
54652                 scope: _t, // was editor...
54653                 handler:handler||_t.relayBtnCmd,
54654                 clickEvent:'mousedown',
54655                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
54656                 tabIndex:-1
54657             };
54658         }
54659         
54660         
54661         
54662         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
54663         this.tb = tb;
54664          // stop form submits
54665         tb.el.on('click', function(e){
54666             e.preventDefault(); // what does this do?
54667         });
54668
54669         if(!this.disable.font) { // && !Roo.isSafari){
54670             /* why no safari for fonts 
54671             editor.fontSelect = tb.el.createChild({
54672                 tag:'select',
54673                 tabIndex: -1,
54674                 cls:'x-font-select',
54675                 html: this.createFontOptions()
54676             });
54677             
54678             editor.fontSelect.on('change', function(){
54679                 var font = editor.fontSelect.dom.value;
54680                 editor.relayCmd('fontname', font);
54681                 editor.deferFocus();
54682             }, editor);
54683             
54684             tb.add(
54685                 editor.fontSelect.dom,
54686                 '-'
54687             );
54688             */
54689             
54690         };
54691         if(!this.disable.formats){
54692             this.formatCombo = new Roo.form.ComboBox({
54693                 store: new Roo.data.SimpleStore({
54694                     id : 'tag',
54695                     fields: ['tag'],
54696                     data : this.formats // from states.js
54697                 }),
54698                 blockFocus : true,
54699                 name : '',
54700                 //autoCreate : {tag: "div",  size: "20"},
54701                 displayField:'tag',
54702                 typeAhead: false,
54703                 mode: 'local',
54704                 editable : false,
54705                 triggerAction: 'all',
54706                 emptyText:'Add tag',
54707                 selectOnFocus:true,
54708                 width:135,
54709                 listeners : {
54710                     'select': function(c, r, i) {
54711                         editorcore.insertTag(r.get('tag'));
54712                         editor.focus();
54713                     }
54714                 }
54715
54716             });
54717             tb.addField(this.formatCombo);
54718             
54719         }
54720         
54721         if(!this.disable.format){
54722             tb.add(
54723                 btn('bold'),
54724                 btn('italic'),
54725                 btn('underline'),
54726                 btn('strikethrough')
54727             );
54728         };
54729         if(!this.disable.fontSize){
54730             tb.add(
54731                 '-',
54732                 
54733                 
54734                 btn('increasefontsize', false, editorcore.adjustFont),
54735                 btn('decreasefontsize', false, editorcore.adjustFont)
54736             );
54737         };
54738         
54739         
54740         if(!this.disable.colors){
54741             tb.add(
54742                 '-', {
54743                     id:editorcore.frameId +'-forecolor',
54744                     cls:'x-btn-icon x-edit-forecolor',
54745                     clickEvent:'mousedown',
54746                     tooltip: this.buttonTips['forecolor'] || undefined,
54747                     tabIndex:-1,
54748                     menu : new Roo.menu.ColorMenu({
54749                         allowReselect: true,
54750                         focus: Roo.emptyFn,
54751                         value:'000000',
54752                         plain:true,
54753                         selectHandler: function(cp, color){
54754                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
54755                             editor.deferFocus();
54756                         },
54757                         scope: editorcore,
54758                         clickEvent:'mousedown'
54759                     })
54760                 }, {
54761                     id:editorcore.frameId +'backcolor',
54762                     cls:'x-btn-icon x-edit-backcolor',
54763                     clickEvent:'mousedown',
54764                     tooltip: this.buttonTips['backcolor'] || undefined,
54765                     tabIndex:-1,
54766                     menu : new Roo.menu.ColorMenu({
54767                         focus: Roo.emptyFn,
54768                         value:'FFFFFF',
54769                         plain:true,
54770                         allowReselect: true,
54771                         selectHandler: function(cp, color){
54772                             if(Roo.isGecko){
54773                                 editorcore.execCmd('useCSS', false);
54774                                 editorcore.execCmd('hilitecolor', color);
54775                                 editorcore.execCmd('useCSS', true);
54776                                 editor.deferFocus();
54777                             }else{
54778                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
54779                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
54780                                 editor.deferFocus();
54781                             }
54782                         },
54783                         scope:editorcore,
54784                         clickEvent:'mousedown'
54785                     })
54786                 }
54787             );
54788         };
54789         // now add all the items...
54790         
54791
54792         if(!this.disable.alignments){
54793             tb.add(
54794                 '-',
54795                 btn('justifyleft'),
54796                 btn('justifycenter'),
54797                 btn('justifyright')
54798             );
54799         };
54800
54801         //if(!Roo.isSafari){
54802             if(!this.disable.links){
54803                 tb.add(
54804                     '-',
54805                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
54806                 );
54807             };
54808
54809             if(!this.disable.lists){
54810                 tb.add(
54811                     '-',
54812                     btn('insertorderedlist'),
54813                     btn('insertunorderedlist')
54814                 );
54815             }
54816             if(!this.disable.sourceEdit){
54817                 tb.add(
54818                     '-',
54819                     btn('sourceedit', true, function(btn){
54820                         this.toggleSourceEdit(btn.pressed);
54821                     })
54822                 );
54823             }
54824         //}
54825         
54826         var smenu = { };
54827         // special menu.. - needs to be tidied up..
54828         if (!this.disable.special) {
54829             smenu = {
54830                 text: "&#169;",
54831                 cls: 'x-edit-none',
54832                 
54833                 menu : {
54834                     items : []
54835                 }
54836             };
54837             for (var i =0; i < this.specialChars.length; i++) {
54838                 smenu.menu.items.push({
54839                     
54840                     html: this.specialChars[i],
54841                     handler: function(a,b) {
54842                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
54843                         //editor.insertAtCursor(a.html);
54844                         
54845                     },
54846                     tabIndex:-1
54847                 });
54848             }
54849             
54850             
54851             tb.add(smenu);
54852             
54853             
54854         }
54855         
54856         var cmenu = { };
54857         if (!this.disable.cleanStyles) {
54858             cmenu = {
54859                 cls: 'x-btn-icon x-btn-clear',
54860                 
54861                 menu : {
54862                     items : []
54863                 }
54864             };
54865             for (var i =0; i < this.cleanStyles.length; i++) {
54866                 cmenu.menu.items.push({
54867                     actiontype : this.cleanStyles[i],
54868                     html: 'Remove ' + this.cleanStyles[i],
54869                     handler: function(a,b) {
54870 //                        Roo.log(a);
54871 //                        Roo.log(b);
54872                         var c = Roo.get(editorcore.doc.body);
54873                         c.select('[style]').each(function(s) {
54874                             s.dom.style.removeProperty(a.actiontype);
54875                         });
54876                         editorcore.syncValue();
54877                     },
54878                     tabIndex:-1
54879                 });
54880             }
54881             cmenu.menu.items.push({
54882                 actiontype : 'tablewidths',
54883                 html: 'Remove Table Widths',
54884                 handler: function(a,b) {
54885                     editorcore.cleanTableWidths();
54886                     editorcore.syncValue();
54887                 },
54888                 tabIndex:-1
54889             });
54890             cmenu.menu.items.push({
54891                 actiontype : 'word',
54892                 html: 'Remove MS Word Formating',
54893                 handler: function(a,b) {
54894                     editorcore.cleanWord();
54895                     editorcore.syncValue();
54896                 },
54897                 tabIndex:-1
54898             });
54899             
54900             cmenu.menu.items.push({
54901                 actiontype : 'all',
54902                 html: 'Remove All Styles',
54903                 handler: function(a,b) {
54904                     
54905                     var c = Roo.get(editorcore.doc.body);
54906                     c.select('[style]').each(function(s) {
54907                         s.dom.removeAttribute('style');
54908                     });
54909                     editorcore.syncValue();
54910                 },
54911                 tabIndex:-1
54912             });
54913             
54914             cmenu.menu.items.push({
54915                 actiontype : 'all',
54916                 html: 'Remove All CSS Classes',
54917                 handler: function(a,b) {
54918                     
54919                     var c = Roo.get(editorcore.doc.body);
54920                     c.select('[class]').each(function(s) {
54921                         s.dom.removeAttribute('class');
54922                     });
54923                     editorcore.cleanWord();
54924                     editorcore.syncValue();
54925                 },
54926                 tabIndex:-1
54927             });
54928             
54929              cmenu.menu.items.push({
54930                 actiontype : 'tidy',
54931                 html: 'Tidy HTML Source',
54932                 handler: function(a,b) {
54933                     new Roo.htmleditor.Tidy(editorcore.doc.body);
54934                     editorcore.syncValue();
54935                 },
54936                 tabIndex:-1
54937             });
54938             
54939             
54940             tb.add(cmenu);
54941         }
54942          
54943         if (!this.disable.specialElements) {
54944             var semenu = {
54945                 text: "Other;",
54946                 cls: 'x-edit-none',
54947                 menu : {
54948                     items : []
54949                 }
54950             };
54951             for (var i =0; i < this.specialElements.length; i++) {
54952                 semenu.menu.items.push(
54953                     Roo.apply({ 
54954                         handler: function(a,b) {
54955                             editor.insertAtCursor(this.ihtml);
54956                         }
54957                     }, this.specialElements[i])
54958                 );
54959                     
54960             }
54961             
54962             tb.add(semenu);
54963             
54964             
54965         }
54966          
54967         
54968         if (this.btns) {
54969             for(var i =0; i< this.btns.length;i++) {
54970                 var b = Roo.factory(this.btns[i],this.btns[i].xns || Roo.form);
54971                 b.cls =  'x-edit-none';
54972                 
54973                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
54974                     b.cls += ' x-init-enable';
54975                 }
54976                 
54977                 b.scope = editorcore;
54978                 tb.add(b);
54979             }
54980         
54981         }
54982         
54983         
54984         
54985         // disable everything...
54986         
54987         this.tb.items.each(function(item){
54988             
54989            if(
54990                 item.id != editorcore.frameId+ '-sourceedit' && 
54991                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
54992             ){
54993                 
54994                 item.disable();
54995             }
54996         });
54997         this.rendered = true;
54998         
54999         // the all the btns;
55000         editor.on('editorevent', this.updateToolbar, this);
55001         // other toolbars need to implement this..
55002         //editor.on('editmodechange', this.updateToolbar, this);
55003     },
55004     
55005     
55006     relayBtnCmd : function(btn) {
55007         this.editorcore.relayCmd(btn.cmd);
55008     },
55009     // private used internally
55010     createLink : function(){
55011         //Roo.log("create link?");
55012         var ec = this.editorcore;
55013         var ar = ec.getAllAncestors();
55014         var n = false;
55015         for(var i = 0;i< ar.length;i++) {
55016             if (ar[i] && ar[i].nodeName == 'A') {
55017                 n = ar[i];
55018                 break;
55019             }
55020         }
55021         
55022         (function() {
55023             
55024             Roo.MessageBox.show({
55025                 title : "Add / Edit Link URL",
55026                 msg : "Enter the url for the link",
55027                 buttons: Roo.MessageBox.OKCANCEL,
55028                 fn: function(btn, url){
55029                     if (btn != 'ok') {
55030                         return;
55031                     }
55032                     if(url && url != 'http:/'+'/'){
55033                         if (n) {
55034                             n.setAttribute('href', url);
55035                         } else {
55036                             ec.relayCmd('createlink', url);
55037                         }
55038                     }
55039                 },
55040                 minWidth:250,
55041                 prompt:true,
55042                 //multiline: multiline,
55043                 modal : true,
55044                 value :  n  ? n.getAttribute('href') : '' 
55045             });
55046             
55047              
55048         }).defer(100, this); // we have to defer this , otherwise the mouse click gives focus to the main window.
55049         
55050     },
55051
55052     
55053     /**
55054      * Protected method that will not generally be called directly. It triggers
55055      * a toolbar update by reading the markup state of the current selection in the editor.
55056      */
55057     updateToolbar: function(){
55058
55059         if(!this.editorcore.activated){
55060             this.editor.onFirstFocus();
55061             return;
55062         }
55063
55064         var btns = this.tb.items.map, 
55065             doc = this.editorcore.doc,
55066             frameId = this.editorcore.frameId;
55067
55068         if(!this.disable.font && !Roo.isSafari){
55069             /*
55070             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
55071             if(name != this.fontSelect.dom.value){
55072                 this.fontSelect.dom.value = name;
55073             }
55074             */
55075         }
55076         if(!this.disable.format){
55077             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
55078             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
55079             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
55080             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
55081         }
55082         if(!this.disable.alignments){
55083             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
55084             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
55085             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
55086         }
55087         if(!Roo.isSafari && !this.disable.lists){
55088             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
55089             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
55090         }
55091         
55092         var ans = this.editorcore.getAllAncestors();
55093         if (this.formatCombo) {
55094             
55095             
55096             var store = this.formatCombo.store;
55097             this.formatCombo.setValue("");
55098             for (var i =0; i < ans.length;i++) {
55099                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
55100                     // select it..
55101                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
55102                     break;
55103                 }
55104             }
55105         }
55106         
55107         
55108         
55109         // hides menus... - so this cant be on a menu...
55110         Roo.menu.MenuMgr.hideAll();
55111
55112         //this.editorsyncValue();
55113     },
55114    
55115     
55116     createFontOptions : function(){
55117         var buf = [], fs = this.fontFamilies, ff, lc;
55118         
55119         
55120         
55121         for(var i = 0, len = fs.length; i< len; i++){
55122             ff = fs[i];
55123             lc = ff.toLowerCase();
55124             buf.push(
55125                 '<option value="',lc,'" style="font-family:',ff,';"',
55126                     (this.defaultFont == lc ? ' selected="true">' : '>'),
55127                     ff,
55128                 '</option>'
55129             );
55130         }
55131         return buf.join('');
55132     },
55133     
55134     toggleSourceEdit : function(sourceEditMode){
55135         
55136         Roo.log("toolbar toogle");
55137         if(sourceEditMode === undefined){
55138             sourceEditMode = !this.sourceEditMode;
55139         }
55140         this.sourceEditMode = sourceEditMode === true;
55141         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
55142         // just toggle the button?
55143         if(btn.pressed !== this.sourceEditMode){
55144             btn.toggle(this.sourceEditMode);
55145             return;
55146         }
55147         
55148         if(sourceEditMode){
55149             Roo.log("disabling buttons");
55150             this.tb.items.each(function(item){
55151                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
55152                     item.disable();
55153                 }
55154             });
55155           
55156         }else{
55157             Roo.log("enabling buttons");
55158             if(this.editorcore.initialized){
55159                 this.tb.items.each(function(item){
55160                     item.enable();
55161                 });
55162                 // initialize 'blocks'
55163                 Roo.each(Roo.get(this.editorcore.doc.body).query('*[data-block]'), function(e) {
55164                     Roo.htmleditor.Block.factory(e).updateElement(e);
55165                 },this);
55166             
55167             }
55168             
55169         }
55170         Roo.log("calling toggole on editor");
55171         // tell the editor that it's been pressed..
55172         this.editor.toggleSourceEdit(sourceEditMode);
55173        
55174     },
55175      /**
55176      * Object collection of toolbar tooltips for the buttons in the editor. The key
55177      * is the command id associated with that button and the value is a valid QuickTips object.
55178      * For example:
55179 <pre><code>
55180 {
55181     bold : {
55182         title: 'Bold (Ctrl+B)',
55183         text: 'Make the selected text bold.',
55184         cls: 'x-html-editor-tip'
55185     },
55186     italic : {
55187         title: 'Italic (Ctrl+I)',
55188         text: 'Make the selected text italic.',
55189         cls: 'x-html-editor-tip'
55190     },
55191     ...
55192 </code></pre>
55193     * @type Object
55194      */
55195     buttonTips : {
55196         bold : {
55197             title: 'Bold (Ctrl+B)',
55198             text: 'Make the selected text bold.',
55199             cls: 'x-html-editor-tip'
55200         },
55201         italic : {
55202             title: 'Italic (Ctrl+I)',
55203             text: 'Make the selected text italic.',
55204             cls: 'x-html-editor-tip'
55205         },
55206         underline : {
55207             title: 'Underline (Ctrl+U)',
55208             text: 'Underline the selected text.',
55209             cls: 'x-html-editor-tip'
55210         },
55211         strikethrough : {
55212             title: 'Strikethrough',
55213             text: 'Strikethrough the selected text.',
55214             cls: 'x-html-editor-tip'
55215         },
55216         increasefontsize : {
55217             title: 'Grow Text',
55218             text: 'Increase the font size.',
55219             cls: 'x-html-editor-tip'
55220         },
55221         decreasefontsize : {
55222             title: 'Shrink Text',
55223             text: 'Decrease the font size.',
55224             cls: 'x-html-editor-tip'
55225         },
55226         backcolor : {
55227             title: 'Text Highlight Color',
55228             text: 'Change the background color of the selected text.',
55229             cls: 'x-html-editor-tip'
55230         },
55231         forecolor : {
55232             title: 'Font Color',
55233             text: 'Change the color of the selected text.',
55234             cls: 'x-html-editor-tip'
55235         },
55236         justifyleft : {
55237             title: 'Align Text Left',
55238             text: 'Align text to the left.',
55239             cls: 'x-html-editor-tip'
55240         },
55241         justifycenter : {
55242             title: 'Center Text',
55243             text: 'Center text in the editor.',
55244             cls: 'x-html-editor-tip'
55245         },
55246         justifyright : {
55247             title: 'Align Text Right',
55248             text: 'Align text to the right.',
55249             cls: 'x-html-editor-tip'
55250         },
55251         insertunorderedlist : {
55252             title: 'Bullet List',
55253             text: 'Start a bulleted list.',
55254             cls: 'x-html-editor-tip'
55255         },
55256         insertorderedlist : {
55257             title: 'Numbered List',
55258             text: 'Start a numbered list.',
55259             cls: 'x-html-editor-tip'
55260         },
55261         createlink : {
55262             title: 'Hyperlink',
55263             text: 'Make the selected text a hyperlink.',
55264             cls: 'x-html-editor-tip'
55265         },
55266         sourceedit : {
55267             title: 'Source Edit',
55268             text: 'Switch to source editing mode.',
55269             cls: 'x-html-editor-tip'
55270         }
55271     },
55272     // private
55273     onDestroy : function(){
55274         if(this.rendered){
55275             
55276             this.tb.items.each(function(item){
55277                 if(item.menu){
55278                     item.menu.removeAll();
55279                     if(item.menu.el){
55280                         item.menu.el.destroy();
55281                     }
55282                 }
55283                 item.destroy();
55284             });
55285              
55286         }
55287     },
55288     onFirstFocus: function() {
55289         this.tb.items.each(function(item){
55290            item.enable();
55291         });
55292     }
55293 };
55294
55295
55296
55297
55298 // <script type="text/javascript">
55299 /*
55300  * Based on
55301  * Ext JS Library 1.1.1
55302  * Copyright(c) 2006-2007, Ext JS, LLC.
55303  *  
55304  
55305  */
55306
55307  
55308 /**
55309  * @class Roo.form.HtmlEditor.ToolbarContext
55310  * Context Toolbar
55311  * 
55312  * Usage:
55313  *
55314  new Roo.form.HtmlEditor({
55315     ....
55316     toolbars : [
55317         { xtype: 'ToolbarStandard', styles : {} }
55318         { xtype: 'ToolbarContext', disable : {} }
55319     ]
55320 })
55321
55322      
55323  * 
55324  * @config : {Object} disable List of elements to disable.. (not done yet.)
55325  * @config : {Object} styles  Map of styles available.
55326  * 
55327  */
55328
55329 Roo.form.HtmlEditor.ToolbarContext = function(config)
55330 {
55331     
55332     Roo.apply(this, config);
55333     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
55334     // dont call parent... till later.
55335     this.styles = this.styles || {};
55336 }
55337
55338  
55339
55340 Roo.form.HtmlEditor.ToolbarContext.types = {
55341     'IMG' : [
55342         {
55343             name : 'width',
55344             title: "Width",
55345             width: 40
55346         },
55347         {
55348             name : 'height',
55349             title: "Height",
55350             width: 40
55351         },
55352         {
55353             name : 'align',
55354             title: "Align",
55355             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
55356             width : 80
55357             
55358         },
55359         {
55360             name : 'border',
55361             title: "Border",
55362             width: 40
55363         },
55364         {
55365             name : 'alt',
55366             title: "Alt",
55367             width: 120
55368         },
55369         {
55370             name : 'src',
55371             title: "Src",
55372             width: 220
55373         }
55374         
55375     ],
55376     
55377     'FIGURE' : [
55378         {
55379             name : 'align',
55380             title: "Align",
55381             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
55382             width : 80  
55383         }
55384     ],
55385     'A' : [
55386         {
55387             name : 'name',
55388             title: "Name",
55389             width: 50
55390         },
55391         {
55392             name : 'target',
55393             title: "Target",
55394             width: 120
55395         },
55396         {
55397             name : 'href',
55398             title: "Href",
55399             width: 220
55400         } // border?
55401         
55402     ],
55403     
55404     'INPUT' : [
55405         {
55406             name : 'name',
55407             title: "name",
55408             width: 120
55409         },
55410         {
55411             name : 'value',
55412             title: "Value",
55413             width: 120
55414         },
55415         {
55416             name : 'width',
55417             title: "Width",
55418             width: 40
55419         }
55420     ],
55421     'LABEL' : [
55422          {
55423             name : 'for',
55424             title: "For",
55425             width: 120
55426         }
55427     ],
55428     'TEXTAREA' : [
55429         {
55430             name : 'name',
55431             title: "name",
55432             width: 120
55433         },
55434         {
55435             name : 'rows',
55436             title: "Rows",
55437             width: 20
55438         },
55439         {
55440             name : 'cols',
55441             title: "Cols",
55442             width: 20
55443         }
55444     ],
55445     'SELECT' : [
55446         {
55447             name : 'name',
55448             title: "name",
55449             width: 120
55450         },
55451         {
55452             name : 'selectoptions',
55453             title: "Options",
55454             width: 200
55455         }
55456     ],
55457     
55458     // should we really allow this??
55459     // should this just be 
55460     'BODY' : [
55461         
55462         {
55463             name : 'title',
55464             title: "Title",
55465             width: 200,
55466             disabled : true
55467         }
55468     ],
55469  
55470     '*' : [
55471         // empty.
55472     ]
55473
55474 };
55475
55476 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
55477 Roo.form.HtmlEditor.ToolbarContext.stores = false;
55478
55479 Roo.form.HtmlEditor.ToolbarContext.options = {
55480         'font-family'  : [ 
55481                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
55482                 [ 'Courier New', 'Courier New'],
55483                 [ 'Tahoma', 'Tahoma'],
55484                 [ 'Times New Roman,serif', 'Times'],
55485                 [ 'Verdana','Verdana' ]
55486         ]
55487 };
55488
55489 // fixme - these need to be configurable..
55490  
55491
55492 //Roo.form.HtmlEditor.ToolbarContext.types
55493
55494
55495 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
55496     
55497     tb: false,
55498     
55499     rendered: false,
55500     
55501     editor : false,
55502     editorcore : false,
55503     /**
55504      * @cfg {Object} disable  List of toolbar elements to disable
55505          
55506      */
55507     disable : false,
55508     /**
55509      * @cfg {Object} styles List of styles 
55510      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
55511      *
55512      * These must be defined in the page, so they get rendered correctly..
55513      * .headline { }
55514      * TD.underline { }
55515      * 
55516      */
55517     styles : false,
55518     
55519     options: false,
55520     
55521     toolbars : false,
55522     
55523     init : function(editor)
55524     {
55525         this.editor = editor;
55526         this.editorcore = editor.editorcore ? editor.editorcore : editor;
55527         var editorcore = this.editorcore;
55528         
55529         var fid = editorcore.frameId;
55530         var etb = this;
55531         function btn(id, toggle, handler){
55532             var xid = fid + '-'+ id ;
55533             return {
55534                 id : xid,
55535                 cmd : id,
55536                 cls : 'x-btn-icon x-edit-'+id,
55537                 enableToggle:toggle !== false,
55538                 scope: editorcore, // was editor...
55539                 handler:handler||editorcore.relayBtnCmd,
55540                 clickEvent:'mousedown',
55541                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
55542                 tabIndex:-1
55543             };
55544         }
55545         // create a new element.
55546         var wdiv = editor.wrap.createChild({
55547                 tag: 'div'
55548             }, editor.wrap.dom.firstChild.nextSibling, true);
55549         
55550         // can we do this more than once??
55551         
55552          // stop form submits
55553       
55554  
55555         // disable everything...
55556         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
55557         this.toolbars = {};
55558         // block toolbars are built in updateToolbar when needed.
55559         for (var i in  ty) {
55560             
55561             this.toolbars[i] = this.buildToolbar(ty[i],i);
55562         }
55563         this.tb = this.toolbars.BODY;
55564         this.tb.el.show();
55565         this.buildFooter();
55566         this.footer.show();
55567         editor.on('hide', function( ) { this.footer.hide() }, this);
55568         editor.on('show', function( ) { this.footer.show() }, this);
55569         
55570          
55571         this.rendered = true;
55572         
55573         // the all the btns;
55574         editor.on('editorevent', this.updateToolbar, this);
55575         // other toolbars need to implement this..
55576         //editor.on('editmodechange', this.updateToolbar, this);
55577     },
55578     
55579     
55580     
55581     /**
55582      * Protected method that will not generally be called directly. It triggers
55583      * a toolbar update by reading the markup state of the current selection in the editor.
55584      *
55585      * Note you can force an update by calling on('editorevent', scope, false)
55586      */
55587     updateToolbar: function(editor ,ev, sel)
55588     {
55589         
55590         if (ev) {
55591             ev.stopEvent(); // se if we can stop this looping with mutiple events.
55592         }
55593         
55594         //Roo.log(ev);
55595         // capture mouse up - this is handy for selecting images..
55596         // perhaps should go somewhere else...
55597         if(!this.editorcore.activated){
55598              this.editor.onFirstFocus();
55599             return;
55600         }
55601         //Roo.log(ev ? ev.target : 'NOTARGET');
55602         
55603         
55604         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
55605         // selectNode - might want to handle IE?
55606         
55607         
55608         
55609         if (ev &&
55610             (ev.type == 'mouseup' || ev.type == 'click' ) &&
55611             ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
55612             // they have click on an image...
55613             // let's see if we can change the selection...
55614             sel = ev.target;
55615             
55616             // this triggers looping?
55617             //this.editorcore.selectNode(sel);
55618              
55619         }
55620         
55621         // this forces an id..
55622         Array.from(this.editorcore.doc.body.querySelectorAll('.roo-ed-selection')).forEach(function(e) {
55623              e.classList.remove('roo-ed-selection');
55624         });
55625         //Roo.select('.roo-ed-selection', false, this.editorcore.doc).removeClass('roo-ed-selection');
55626         //Roo.get(node).addClass('roo-ed-selection');
55627       
55628         //var updateFooter = sel ? false : true; 
55629         
55630         
55631         var ans = this.editorcore.getAllAncestors();
55632         
55633         // pick
55634         var ty = Roo.form.HtmlEditor.ToolbarContext.types;
55635         
55636         if (!sel) { 
55637             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
55638             sel = sel ? sel : this.editorcore.doc.body;
55639             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
55640             
55641         }
55642         
55643         var tn = sel.tagName.toUpperCase();
55644         var lastSel = this.tb.selectedNode;
55645         this.tb.selectedNode = sel;
55646         var left_label = tn;
55647         
55648         // ok see if we are editing a block?
55649         
55650         var db = false;
55651         // you are not actually selecting the block.
55652         if (sel && sel.hasAttribute('data-block')) {
55653             db = sel;
55654         } else if (sel && sel.closest('[data-block]')) {
55655             
55656             db = sel.closest('[data-block]');
55657             //var cepar = sel.closest('[contenteditable=true]');
55658             //if (db && cepar && cepar.tagName != 'BODY') {
55659             //   db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
55660             //}   
55661         }
55662         
55663         
55664         var block = false;
55665         //if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
55666         if (db && this.editorcore.enableBlocks) {
55667             block = Roo.htmleditor.Block.factory(db);
55668             
55669             
55670             if (block) {
55671                  db.className = (
55672                         db.classList.length > 0  ? db.className + ' ' : ''
55673                     )  + 'roo-ed-selection';
55674                  
55675                  // since we removed it earlier... its not there..
55676                 tn = 'BLOCK.' + db.getAttribute('data-block');
55677                 
55678                 //this.editorcore.selectNode(db);
55679                 if (typeof(this.toolbars[tn]) == 'undefined') {
55680                    this.toolbars[tn] = this.buildToolbar( false  ,tn ,block.friendly_name, block);
55681                 }
55682                 this.toolbars[tn].selectedNode = db;
55683                 left_label = block.friendly_name;
55684                 ans = this.editorcore.getAllAncestors();
55685             }
55686             
55687                 
55688             
55689         }
55690         
55691         
55692         if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
55693             return; // no change?
55694         }
55695         
55696         
55697           
55698         this.tb.el.hide();
55699         ///console.log("show: " + tn);
55700         this.tb =  typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
55701         
55702         this.tb.el.show();
55703         // update name
55704         this.tb.items.first().el.innerHTML = left_label + ':&nbsp;';
55705         
55706         
55707         // update attributes
55708         if (block && this.tb.fields) {
55709              
55710             this.tb.fields.each(function(e) {
55711                 e.setValue(block[e.name]);
55712             });
55713             
55714             
55715         } else  if (this.tb.fields && this.tb.selectedNode) {
55716             this.tb.fields.each( function(e) {
55717                 if (e.stylename) {
55718                     e.setValue(this.tb.selectedNode.style[e.stylename]);
55719                     return;
55720                 } 
55721                 e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
55722             }, this);
55723             this.updateToolbarStyles(this.tb.selectedNode);  
55724         }
55725         
55726         
55727        
55728         Roo.menu.MenuMgr.hideAll();
55729
55730         
55731         
55732     
55733         // update the footer
55734         //
55735         this.updateFooter(ans);
55736              
55737     },
55738     
55739     updateToolbarStyles : function(sel)
55740     {
55741         var hasStyles = false;
55742         for(var i in this.styles) {
55743             hasStyles = true;
55744             break;
55745         }
55746         
55747         // update styles
55748         if (hasStyles && this.tb.hasStyles) { 
55749             var st = this.tb.fields.item(0);
55750             
55751             st.store.removeAll();
55752             var cn = sel.className.split(/\s+/);
55753             
55754             var avs = [];
55755             if (this.styles['*']) {
55756                 
55757                 Roo.each(this.styles['*'], function(v) {
55758                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
55759                 });
55760             }
55761             if (this.styles[tn]) { 
55762                 Roo.each(this.styles[tn], function(v) {
55763                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
55764                 });
55765             }
55766             
55767             st.store.loadData(avs);
55768             st.collapse();
55769             st.setValue(cn);
55770         }
55771     },
55772     
55773      
55774     updateFooter : function(ans)
55775     {
55776         var html = '';
55777         if (ans === false) {
55778             this.footDisp.dom.innerHTML = '';
55779             return;
55780         }
55781         
55782         this.footerEls = ans.reverse();
55783         Roo.each(this.footerEls, function(a,i) {
55784             if (!a) { return; }
55785             html += html.length ? ' &gt; '  :  '';
55786             
55787             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
55788             
55789         });
55790        
55791         // 
55792         var sz = this.footDisp.up('td').getSize();
55793         this.footDisp.dom.style.width = (sz.width -10) + 'px';
55794         this.footDisp.dom.style.marginLeft = '5px';
55795         
55796         this.footDisp.dom.style.overflow = 'hidden';
55797         
55798         this.footDisp.dom.innerHTML = html;
55799             
55800         
55801     },
55802    
55803        
55804     // private
55805     onDestroy : function(){
55806         if(this.rendered){
55807             
55808             this.tb.items.each(function(item){
55809                 if(item.menu){
55810                     item.menu.removeAll();
55811                     if(item.menu.el){
55812                         item.menu.el.destroy();
55813                     }
55814                 }
55815                 item.destroy();
55816             });
55817              
55818         }
55819     },
55820     onFirstFocus: function() {
55821         // need to do this for all the toolbars..
55822         this.tb.items.each(function(item){
55823            item.enable();
55824         });
55825     },
55826     buildToolbar: function(tlist, nm, friendly_name, block)
55827     {
55828         var editor = this.editor;
55829         var editorcore = this.editorcore;
55830          // create a new element.
55831         var wdiv = editor.wrap.createChild({
55832                 tag: 'div'
55833             }, editor.wrap.dom.firstChild.nextSibling, true);
55834         
55835        
55836         var tb = new Roo.Toolbar(wdiv);
55837         ///this.tb = tb; // << this sets the active toolbar..
55838         if (tlist === false && block) {
55839             tlist = block.contextMenu(this);
55840         }
55841         
55842         tb.hasStyles = false;
55843         tb.name = nm;
55844         
55845         tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ":&nbsp;");
55846         
55847         var styles = Array.from(this.styles);
55848         
55849         
55850         // styles...
55851         if (styles && styles.length) {
55852             tb.hasStyles = true;
55853             // this needs a multi-select checkbox...
55854             tb.addField( new Roo.form.ComboBox({
55855                 store: new Roo.data.SimpleStore({
55856                     id : 'val',
55857                     fields: ['val', 'selected'],
55858                     data : [] 
55859                 }),
55860                 name : '-roo-edit-className',
55861                 attrname : 'className',
55862                 displayField: 'val',
55863                 typeAhead: false,
55864                 mode: 'local',
55865                 editable : false,
55866                 triggerAction: 'all',
55867                 emptyText:'Select Style',
55868                 selectOnFocus:true,
55869                 width: 130,
55870                 listeners : {
55871                     'select': function(c, r, i) {
55872                         // initial support only for on class per el..
55873                         tb.selectedNode.className =  r ? r.get('val') : '';
55874                         editorcore.syncValue();
55875                     }
55876                 }
55877     
55878             }));
55879         }
55880         
55881         var tbc = Roo.form.HtmlEditor.ToolbarContext;
55882         
55883         
55884         for (var i = 0; i < tlist.length; i++) {
55885             
55886             // newer versions will use xtype cfg to create menus.
55887             if (typeof(tlist[i].xtype) != 'undefined') {
55888                 
55889                 tb[typeof(tlist[i].name)== 'undefined' ? 'add' : 'addField'](Roo.factory(tlist[i]));
55890                 
55891                 
55892                 continue;
55893             }
55894             
55895             var item = tlist[i];
55896             tb.add(item.title + ":&nbsp;");
55897             
55898             
55899             //optname == used so you can configure the options available..
55900             var opts = item.opts ? item.opts : false;
55901             if (item.optname) { // use the b
55902                 opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
55903            
55904             }
55905             
55906             if (opts) {
55907                 // opts == pulldown..
55908                 tb.addField( new Roo.form.ComboBox({
55909                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
55910                         id : 'val',
55911                         fields: ['val', 'display'],
55912                         data : opts  
55913                     }),
55914                     name : '-roo-edit-' + tlist[i].name,
55915                     
55916                     attrname : tlist[i].name,
55917                     stylename : item.style ? item.style : false,
55918                     
55919                     displayField: item.displayField ? item.displayField : 'val',
55920                     valueField :  'val',
55921                     typeAhead: false,
55922                     mode: typeof(tbc.stores[tlist[i].name]) != 'undefined'  ? 'remote' : 'local',
55923                     editable : false,
55924                     triggerAction: 'all',
55925                     emptyText:'Select',
55926                     selectOnFocus:true,
55927                     width: item.width ? item.width  : 130,
55928                     listeners : {
55929                         'select': function(c, r, i) {
55930                              
55931                             
55932                             if (c.stylename) {
55933                                 tb.selectedNode.style[c.stylename] =  r.get('val');
55934                                 editorcore.syncValue();
55935                                 return;
55936                             }
55937                             if (r === false) {
55938                                 tb.selectedNode.removeAttribute(c.attrname);
55939                                 editorcore.syncValue();
55940                                 return;
55941                             }
55942                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
55943                             editorcore.syncValue();
55944                         }
55945                     }
55946
55947                 }));
55948                 continue;
55949                     
55950                  
55951                 /*
55952                 tb.addField( new Roo.form.TextField({
55953                     name: i,
55954                     width: 100,
55955                     //allowBlank:false,
55956                     value: ''
55957                 }));
55958                 continue;
55959                 */
55960             }
55961             tb.addField( new Roo.form.TextField({
55962                 name: '-roo-edit-' + tlist[i].name,
55963                 attrname : tlist[i].name,
55964                 
55965                 width: item.width,
55966                 //allowBlank:true,
55967                 value: '',
55968                 listeners: {
55969                     'change' : function(f, nv, ov) {
55970                         
55971                          
55972                         tb.selectedNode.setAttribute(f.attrname, nv);
55973                         editorcore.syncValue();
55974                     }
55975                 }
55976             }));
55977              
55978         }
55979         
55980         var _this = this;
55981         var show_delete = !block || block.deleteTitle !== false;
55982         if(nm == 'BODY'){
55983             show_delete = false;
55984             tb.addSeparator();
55985         
55986             tb.addButton( {
55987                 text: 'Stylesheets',
55988
55989                 listeners : {
55990                     click : function ()
55991                     {
55992                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
55993                     }
55994                 }
55995             });
55996         }
55997         
55998         tb.addFill();
55999         if (show_delete) {
56000             tb.addButton({
56001                 text: block && block.deleteTitle ? block.deleteTitle  : 'Remove Block or Formating', // remove the tag, and puts the children outside...
56002         
56003                 listeners : {
56004                     click : function ()
56005                     {
56006                         var sn = tb.selectedNode;
56007                         if (block) {
56008                             sn = Roo.htmleditor.Block.factory(tb.selectedNode).removeNode();
56009                             
56010                         }
56011                         if (!sn) {
56012                             return;
56013                         }
56014                         var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
56015                         if (sn.hasAttribute('data-block')) {
56016                             stn =  sn.nextSibling || sn.previousSibling || sn.parentNode;
56017                             sn.parentNode.removeChild(sn);
56018                             
56019                         } else if (sn && sn.tagName != 'BODY') {
56020                             // remove and keep parents.
56021                             a = new Roo.htmleditor.FilterKeepChildren({tag : false});
56022                             a.replaceTag(sn);
56023                         }
56024                         
56025                         
56026                         var range = editorcore.createRange();
56027             
56028                         range.setStart(stn,0);
56029                         range.setEnd(stn,0); 
56030                         var selection = editorcore.getSelection();
56031                         selection.removeAllRanges();
56032                         selection.addRange(range);
56033                         
56034                         
56035                         //_this.updateToolbar(null, null, pn);
56036                         _this.updateToolbar(null, null, null);
56037                         _this.updateFooter(false);
56038                         
56039                     }
56040                 }
56041                 
56042                         
56043                     
56044                 
56045             });
56046         }    
56047         
56048         tb.el.on('click', function(e){
56049             e.preventDefault(); // what does this do?
56050         });
56051         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
56052         tb.el.hide();
56053         
56054         // dont need to disable them... as they will get hidden
56055         return tb;
56056          
56057         
56058     },
56059     buildFooter : function()
56060     {
56061         
56062         var fel = this.editor.wrap.createChild();
56063         this.footer = new Roo.Toolbar(fel);
56064         // toolbar has scrolly on left / right?
56065         var footDisp= new Roo.Toolbar.Fill();
56066         var _t = this;
56067         this.footer.add(
56068             {
56069                 text : '&lt;',
56070                 xtype: 'Button',
56071                 handler : function() {
56072                     _t.footDisp.scrollTo('left',0,true)
56073                 }
56074             }
56075         );
56076         this.footer.add( footDisp );
56077         this.footer.add( 
56078             {
56079                 text : '&gt;',
56080                 xtype: 'Button',
56081                 handler : function() {
56082                     // no animation..
56083                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
56084                 }
56085             }
56086         );
56087         var fel = Roo.get(footDisp.el);
56088         fel.addClass('x-editor-context');
56089         this.footDispWrap = fel; 
56090         this.footDispWrap.overflow  = 'hidden';
56091         
56092         this.footDisp = fel.createChild();
56093         this.footDispWrap.on('click', this.onContextClick, this)
56094         
56095         
56096     },
56097     // when the footer contect changes
56098     onContextClick : function (ev,dom)
56099     {
56100         ev.preventDefault();
56101         var  cn = dom.className;
56102         //Roo.log(cn);
56103         if (!cn.match(/x-ed-loc-/)) {
56104             return;
56105         }
56106         var n = cn.split('-').pop();
56107         var ans = this.footerEls;
56108         var sel = ans[n];
56109         
56110         this.editorcore.selectNode(sel);
56111         
56112         
56113         this.updateToolbar(null, null, sel);
56114         
56115         
56116     }
56117     
56118     
56119     
56120     
56121     
56122 });
56123
56124
56125
56126
56127
56128 /*
56129  * Based on:
56130  * Ext JS Library 1.1.1
56131  * Copyright(c) 2006-2007, Ext JS, LLC.
56132  *
56133  * Originally Released Under LGPL - original licence link has changed is not relivant.
56134  *
56135  * Fork - LGPL
56136  * <script type="text/javascript">
56137  */
56138  
56139 /**
56140  * @class Roo.form.BasicForm
56141  * @extends Roo.util.Observable
56142  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
56143  * @constructor
56144  * @param {String/HTMLElement/Roo.Element} el The form element or its id
56145  * @param {Object} config Configuration options
56146  */
56147 Roo.form.BasicForm = function(el, config){
56148     this.allItems = [];
56149     this.childForms = [];
56150     Roo.apply(this, config);
56151     /*
56152      * The Roo.form.Field items in this form.
56153      * @type MixedCollection
56154      */
56155      
56156      
56157     this.items = new Roo.util.MixedCollection(false, function(o){
56158         return o.id || (o.id = Roo.id());
56159     });
56160     this.addEvents({
56161         /**
56162          * @event beforeaction
56163          * Fires before any action is performed. Return false to cancel the action.
56164          * @param {Form} this
56165          * @param {Action} action The action to be performed
56166          */
56167         beforeaction: true,
56168         /**
56169          * @event actionfailed
56170          * Fires when an action fails.
56171          * @param {Form} this
56172          * @param {Action} action The action that failed
56173          */
56174         actionfailed : true,
56175         /**
56176          * @event actioncomplete
56177          * Fires when an action is completed.
56178          * @param {Form} this
56179          * @param {Action} action The action that completed
56180          */
56181         actioncomplete : true
56182     });
56183     if(el){
56184         this.initEl(el);
56185     }
56186     Roo.form.BasicForm.superclass.constructor.call(this);
56187     
56188     Roo.form.BasicForm.popover.apply();
56189 };
56190
56191 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
56192     /**
56193      * @cfg {String} method
56194      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
56195      */
56196     /**
56197      * @cfg {DataReader} reader
56198      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
56199      * This is optional as there is built-in support for processing JSON.
56200      */
56201     /**
56202      * @cfg {DataReader} errorReader
56203      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
56204      * This is completely optional as there is built-in support for processing JSON.
56205      */
56206     /**
56207      * @cfg {String} url
56208      * The URL to use for form actions if one isn't supplied in the action options.
56209      */
56210     /**
56211      * @cfg {Boolean} fileUpload
56212      * Set to true if this form is a file upload.
56213      */
56214      
56215     /**
56216      * @cfg {Object} baseParams
56217      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
56218      */
56219      /**
56220      
56221     /**
56222      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
56223      */
56224     timeout: 30,
56225
56226     // private
56227     activeAction : null,
56228
56229     /**
56230      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
56231      * or setValues() data instead of when the form was first created.
56232      */
56233     trackResetOnLoad : false,
56234     
56235     
56236     /**
56237      * childForms - used for multi-tab forms
56238      * @type {Array}
56239      */
56240     childForms : false,
56241     
56242     /**
56243      * allItems - full list of fields.
56244      * @type {Array}
56245      */
56246     allItems : false,
56247     
56248     /**
56249      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
56250      * element by passing it or its id or mask the form itself by passing in true.
56251      * @type Mixed
56252      */
56253     waitMsgTarget : false,
56254     
56255     /**
56256      * @type Boolean
56257      */
56258     disableMask : false,
56259     
56260     /**
56261      * @cfg {Boolean} errorMask Should the form be masked (and the active element highlighted on error - default false
56262      */
56263     errorMask : false,
56264     
56265     /**
56266      * @cfg {Number} maskOffset space around form element to mask if there is an error Default 100
56267      */
56268     maskOffset : 100,
56269
56270     // private
56271     initEl : function(el){
56272         this.el = Roo.get(el);
56273         this.id = this.el.id || Roo.id();
56274         this.el.on('submit', this.onSubmit, this);
56275         this.el.addClass('x-form');
56276     },
56277
56278     // private
56279     onSubmit : function(e){
56280         e.stopEvent();
56281     },
56282
56283     /**
56284      * Returns true if client-side validation on the form is successful.
56285      * @return Boolean
56286      */
56287     isValid : function(){
56288         var valid = true;
56289         var target = false;
56290         this.items.each(function(f){
56291             if(f.validate()){
56292                 return;
56293             }
56294             
56295             valid = false;
56296                 
56297             if(!target && f.el.isVisible(true)){
56298                 target = f;
56299             }
56300         });
56301         
56302         if(this.errorMask && !valid){
56303             Roo.form.BasicForm.popover.mask(this, target);
56304         }
56305         
56306         return valid;
56307     },
56308     /**
56309      * Returns array of invalid form fields.
56310      * @return Array
56311      */
56312     
56313     invalidFields : function()
56314     {
56315         var ret = [];
56316         this.items.each(function(f){
56317             if(f.validate()){
56318                 return;
56319             }
56320             ret.push(f);
56321             
56322         });
56323         
56324         return ret;
56325     },
56326     
56327     
56328     /**
56329      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
56330      * @return Boolean
56331      */
56332     isDirty : function(){
56333         var dirty = false;
56334         this.items.each(function(f){
56335            if(f.isDirty()){
56336                dirty = true;
56337                return false;
56338            }
56339         });
56340         return dirty;
56341     },
56342     
56343     /**
56344      * Returns true if any fields in this form have changed since their original load. (New version)
56345      * @return Boolean
56346      */
56347     
56348     hasChanged : function()
56349     {
56350         var dirty = false;
56351         this.items.each(function(f){
56352            if(f.hasChanged()){
56353                dirty = true;
56354                return false;
56355            }
56356         });
56357         return dirty;
56358         
56359     },
56360     /**
56361      * Resets all hasChanged to 'false' -
56362      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
56363      * So hasChanged storage is only to be used for this purpose
56364      * @return Boolean
56365      */
56366     resetHasChanged : function()
56367     {
56368         this.items.each(function(f){
56369            f.resetHasChanged();
56370         });
56371         
56372     },
56373     
56374     
56375     /**
56376      * Performs a predefined action (submit or load) or custom actions you define on this form.
56377      * @param {String} actionName The name of the action type
56378      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
56379      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
56380      * accept other config options):
56381      * <pre>
56382 Property          Type             Description
56383 ----------------  ---------------  ----------------------------------------------------------------------------------
56384 url               String           The url for the action (defaults to the form's url)
56385 method            String           The form method to use (defaults to the form's method, or POST if not defined)
56386 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
56387 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
56388                                    validate the form on the client (defaults to false)
56389      * </pre>
56390      * @return {BasicForm} this
56391      */
56392     doAction : function(action, options){
56393         if(typeof action == 'string'){
56394             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
56395         }
56396         if(this.fireEvent('beforeaction', this, action) !== false){
56397             this.beforeAction(action);
56398             action.run.defer(100, action);
56399         }
56400         return this;
56401     },
56402
56403     /**
56404      * Shortcut to do a submit action.
56405      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
56406      * @return {BasicForm} this
56407      */
56408     submit : function(options){
56409         this.doAction('submit', options);
56410         return this;
56411     },
56412
56413     /**
56414      * Shortcut to do a load action.
56415      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
56416      * @return {BasicForm} this
56417      */
56418     load : function(options){
56419         this.doAction('load', options);
56420         return this;
56421     },
56422
56423     /**
56424      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
56425      * @param {Record} record The record to edit
56426      * @return {BasicForm} this
56427      */
56428     updateRecord : function(record){
56429         record.beginEdit();
56430         var fs = record.fields;
56431         fs.each(function(f){
56432             var field = this.findField(f.name);
56433             if(field){
56434                 record.set(f.name, field.getValue());
56435             }
56436         }, this);
56437         record.endEdit();
56438         return this;
56439     },
56440
56441     /**
56442      * Loads an Roo.data.Record into this form.
56443      * @param {Record} record The record to load
56444      * @return {BasicForm} this
56445      */
56446     loadRecord : function(record){
56447         this.setValues(record.data);
56448         return this;
56449     },
56450
56451     // private
56452     beforeAction : function(action){
56453         var o = action.options;
56454         
56455         if(!this.disableMask) {
56456             if(this.waitMsgTarget === true){
56457                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
56458             }else if(this.waitMsgTarget){
56459                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
56460                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
56461             }else {
56462                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
56463             }
56464         }
56465         
56466          
56467     },
56468
56469     // private
56470     afterAction : function(action, success){
56471         this.activeAction = null;
56472         var o = action.options;
56473         
56474         if(!this.disableMask) {
56475             if(this.waitMsgTarget === true){
56476                 this.el.unmask();
56477             }else if(this.waitMsgTarget){
56478                 this.waitMsgTarget.unmask();
56479             }else{
56480                 Roo.MessageBox.updateProgress(1);
56481                 Roo.MessageBox.hide();
56482             }
56483         }
56484         
56485         if(success){
56486             if(o.reset){
56487                 this.reset();
56488             }
56489             Roo.callback(o.success, o.scope, [this, action]);
56490             this.fireEvent('actioncomplete', this, action);
56491             
56492         }else{
56493             
56494             // failure condition..
56495             // we have a scenario where updates need confirming.
56496             // eg. if a locking scenario exists..
56497             // we look for { errors : { needs_confirm : true }} in the response.
56498             if (
56499                 (typeof(action.result) != 'undefined')  &&
56500                 (typeof(action.result.errors) != 'undefined')  &&
56501                 (typeof(action.result.errors.needs_confirm) != 'undefined')
56502            ){
56503                 var _t = this;
56504                 Roo.MessageBox.confirm(
56505                     "Change requires confirmation",
56506                     action.result.errorMsg,
56507                     function(r) {
56508                         if (r != 'yes') {
56509                             return;
56510                         }
56511                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
56512                     }
56513                     
56514                 );
56515                 
56516                 
56517                 
56518                 return;
56519             }
56520             
56521             Roo.callback(o.failure, o.scope, [this, action]);
56522             // show an error message if no failed handler is set..
56523             if (!this.hasListener('actionfailed')) {
56524                 Roo.MessageBox.alert("Error",
56525                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
56526                         action.result.errorMsg :
56527                         "Saving Failed, please check your entries or try again"
56528                 );
56529             }
56530             
56531             this.fireEvent('actionfailed', this, action);
56532         }
56533         
56534     },
56535
56536     /**
56537      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
56538      * @param {String} id The value to search for
56539      * @return Field
56540      */
56541     findField : function(id){
56542         var field = this.items.get(id);
56543         if(!field){
56544             this.items.each(function(f){
56545                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
56546                     field = f;
56547                     return false;
56548                 }
56549             });
56550         }
56551         return field || null;
56552     },
56553
56554     /**
56555      * Add a secondary form to this one, 
56556      * Used to provide tabbed forms. One form is primary, with hidden values 
56557      * which mirror the elements from the other forms.
56558      * 
56559      * @param {Roo.form.Form} form to add.
56560      * 
56561      */
56562     addForm : function(form)
56563     {
56564        
56565         if (this.childForms.indexOf(form) > -1) {
56566             // already added..
56567             return;
56568         }
56569         this.childForms.push(form);
56570         var n = '';
56571         Roo.each(form.allItems, function (fe) {
56572             
56573             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
56574             if (this.findField(n)) { // already added..
56575                 return;
56576             }
56577             var add = new Roo.form.Hidden({
56578                 name : n
56579             });
56580             add.render(this.el);
56581             
56582             this.add( add );
56583         }, this);
56584         
56585     },
56586     /**
56587      * Mark fields in this form invalid in bulk.
56588      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
56589      * @return {BasicForm} this
56590      */
56591     markInvalid : function(errors){
56592         if(errors instanceof Array){
56593             for(var i = 0, len = errors.length; i < len; i++){
56594                 var fieldError = errors[i];
56595                 var f = this.findField(fieldError.id);
56596                 if(f){
56597                     f.markInvalid(fieldError.msg);
56598                 }
56599             }
56600         }else{
56601             var field, id;
56602             for(id in errors){
56603                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
56604                     field.markInvalid(errors[id]);
56605                 }
56606             }
56607         }
56608         Roo.each(this.childForms || [], function (f) {
56609             f.markInvalid(errors);
56610         });
56611         
56612         return this;
56613     },
56614
56615     /**
56616      * Set values for fields in this form in bulk.
56617      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
56618      * @return {BasicForm} this
56619      */
56620     setValues : function(values){
56621         if(values instanceof Array){ // array of objects
56622             for(var i = 0, len = values.length; i < len; i++){
56623                 var v = values[i];
56624                 var f = this.findField(v.id);
56625                 if(f){
56626                     f.setValue(v.value);
56627                     if(this.trackResetOnLoad){
56628                         f.originalValue = f.getValue();
56629                     }
56630                 }
56631             }
56632         }else{ // object hash
56633             var field, id;
56634             for(id in values){
56635                 if(typeof values[id] != 'function' && (field = this.findField(id))){
56636                     
56637                     
56638                     
56639                     
56640                     if (field.setFromData && 
56641                         field.valueField && 
56642                         field.displayField &&
56643                         // combos' with local stores can 
56644                         // be queried via setValue()
56645                         // to set their value..
56646                         (field.store && !field.store.isLocal)
56647                         ) {
56648                         // it's a combo
56649                         var sd = { };
56650                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
56651                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
56652                         field.setFromData(sd);
56653                         
56654                     } else if (field.inputType && field.inputType == 'radio') {
56655                         
56656                         field.setValue(values[id]);
56657                     } else {
56658                         field.setValue(values[id]);
56659                     }
56660                     
56661                     
56662                     if(this.trackResetOnLoad){
56663                         field.originalValue = field.getValue();
56664                     }
56665                 }
56666             }
56667         }
56668         this.resetHasChanged();
56669         
56670         
56671         Roo.each(this.childForms || [], function (f) {
56672             f.setValues(values);
56673             f.resetHasChanged();
56674         });
56675                 
56676         return this;
56677     },
56678  
56679     /**
56680      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
56681      * they are returned as an array.
56682      * @param {Boolean} asString (def)
56683      * @return {Object}
56684      */
56685     getValues : function(asString)
56686     {
56687         if (this.childForms) {
56688             // copy values from the child forms
56689             Roo.each(this.childForms, function (f) {
56690                 this.setValues(f.getFieldValues()); // get the full set of data, as we might be copying comboboxes from external into this one.
56691             }, this);
56692         }
56693         
56694         // use formdata
56695         if (typeof(FormData) != 'undefined' && asString !== true) {
56696             // this relies on a 'recent' version of chrome apparently...
56697             try {
56698                 var fd = (new FormData(this.el.dom)).entries();
56699                 var ret = {};
56700                 var ent = fd.next();
56701                 while (!ent.done) {
56702                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
56703                     ent = fd.next();
56704                 };
56705                 return ret;
56706             } catch(e) {
56707                 
56708             }
56709             
56710         }
56711         
56712         
56713         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
56714         if(asString === true){
56715             return fs;
56716         }
56717         return Roo.urlDecode(fs);
56718     },
56719     
56720     /**
56721      * Returns the fields in this form as an object with key/value pairs. 
56722      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
56723      * Normally this will not return readOnly data 
56724      * @param {Boolean} with_readonly return readonly field data.
56725      * @return {Object}
56726      */
56727     getFieldValues : function(with_readonly)
56728     {
56729         if (this.childForms) {
56730             // copy values from the child forms
56731             // should this call getFieldValues - probably not as we do not currently copy
56732             // hidden fields when we generate..
56733             Roo.each(this.childForms, function (f) {
56734                 this.setValues(f.getFieldValues());
56735             }, this);
56736         }
56737         
56738         var ret = {};
56739         this.items.each(function(f){
56740             
56741             if (f.readOnly && with_readonly !== true) {
56742                 return; // skip read only values. - this is in theory to stop 'old' values being copied over new ones
56743                         // if a subform contains a copy of them.
56744                         // if you have subforms with the same editable data, you will need to copy the data back
56745                         // and forth.
56746             }
56747             
56748             if (!f.getName()) {
56749                 return;
56750             }
56751             var v = f.getValue();
56752             if (f.inputType =='radio') {
56753                 if (typeof(ret[f.getName()]) == 'undefined') {
56754                     ret[f.getName()] = ''; // empty..
56755                 }
56756                 
56757                 if (!f.el.dom.checked) {
56758                     return;
56759                     
56760                 }
56761                 v = f.el.dom.value;
56762                 
56763             }
56764             
56765             // not sure if this supported any more..
56766             if ((typeof(v) == 'object') && f.getRawValue) {
56767                 v = f.getRawValue() ; // dates..
56768             }
56769             // combo boxes where name != hiddenName...
56770             if (f.name != f.getName()) {
56771                 ret[f.name] = f.getRawValue();
56772             }
56773             ret[f.getName()] = v;
56774         });
56775         
56776         return ret;
56777     },
56778
56779     /**
56780      * Clears all invalid messages in this form.
56781      * @return {BasicForm} this
56782      */
56783     clearInvalid : function(){
56784         this.items.each(function(f){
56785            f.clearInvalid();
56786         });
56787         
56788         Roo.each(this.childForms || [], function (f) {
56789             f.clearInvalid();
56790         });
56791         
56792         
56793         return this;
56794     },
56795
56796     /**
56797      * Resets this form.
56798      * @return {BasicForm} this
56799      */
56800     reset : function(){
56801         this.items.each(function(f){
56802             f.reset();
56803         });
56804         
56805         Roo.each(this.childForms || [], function (f) {
56806             f.reset();
56807         });
56808         this.resetHasChanged();
56809         
56810         return this;
56811     },
56812
56813     /**
56814      * Add Roo.form components to this form.
56815      * @param {Field} field1
56816      * @param {Field} field2 (optional)
56817      * @param {Field} etc (optional)
56818      * @return {BasicForm} this
56819      */
56820     add : function(){
56821         this.items.addAll(Array.prototype.slice.call(arguments, 0));
56822         return this;
56823     },
56824
56825
56826     /**
56827      * Removes a field from the items collection (does NOT remove its markup).
56828      * @param {Field} field
56829      * @return {BasicForm} this
56830      */
56831     remove : function(field){
56832         this.items.remove(field);
56833         return this;
56834     },
56835
56836     /**
56837      * Looks at the fields in this form, checks them for an id attribute,
56838      * and calls applyTo on the existing dom element with that id.
56839      * @return {BasicForm} this
56840      */
56841     render : function(){
56842         this.items.each(function(f){
56843             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
56844                 f.applyTo(f.id);
56845             }
56846         });
56847         return this;
56848     },
56849
56850     /**
56851      * Calls {@link Ext#apply} for all fields in this form with the passed object.
56852      * @param {Object} values
56853      * @return {BasicForm} this
56854      */
56855     applyToFields : function(o){
56856         this.items.each(function(f){
56857            Roo.apply(f, o);
56858         });
56859         return this;
56860     },
56861
56862     /**
56863      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
56864      * @param {Object} values
56865      * @return {BasicForm} this
56866      */
56867     applyIfToFields : function(o){
56868         this.items.each(function(f){
56869            Roo.applyIf(f, o);
56870         });
56871         return this;
56872     }
56873 });
56874
56875 // back compat
56876 Roo.BasicForm = Roo.form.BasicForm;
56877
56878 Roo.apply(Roo.form.BasicForm, {
56879     
56880     popover : {
56881         
56882         padding : 5,
56883         
56884         isApplied : false,
56885         
56886         isMasked : false,
56887         
56888         form : false,
56889         
56890         target : false,
56891         
56892         intervalID : false,
56893         
56894         maskEl : false,
56895         
56896         apply : function()
56897         {
56898             if(this.isApplied){
56899                 return;
56900             }
56901             
56902             this.maskEl = {
56903                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
56904                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
56905                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
56906                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
56907             };
56908             
56909             this.maskEl.top.enableDisplayMode("block");
56910             this.maskEl.left.enableDisplayMode("block");
56911             this.maskEl.bottom.enableDisplayMode("block");
56912             this.maskEl.right.enableDisplayMode("block");
56913             
56914             Roo.get(document.body).on('click', function(){
56915                 this.unmask();
56916             }, this);
56917             
56918             Roo.get(document.body).on('touchstart', function(){
56919                 this.unmask();
56920             }, this);
56921             
56922             this.isApplied = true
56923         },
56924         
56925         mask : function(form, target)
56926         {
56927             this.form = form;
56928             
56929             this.target = target;
56930             
56931             if(!this.form.errorMask || !target.el){
56932                 return;
56933             }
56934             
56935             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
56936             
56937             var ot = this.target.el.calcOffsetsTo(scrollable);
56938             
56939             var scrollTo = ot[1] - this.form.maskOffset;
56940             
56941             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
56942             
56943             scrollable.scrollTo('top', scrollTo);
56944             
56945             var el = this.target.wrap || this.target.el;
56946             
56947             var box = el.getBox();
56948             
56949             this.maskEl.top.setStyle('position', 'absolute');
56950             this.maskEl.top.setStyle('z-index', 10000);
56951             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
56952             this.maskEl.top.setLeft(0);
56953             this.maskEl.top.setTop(0);
56954             this.maskEl.top.show();
56955             
56956             this.maskEl.left.setStyle('position', 'absolute');
56957             this.maskEl.left.setStyle('z-index', 10000);
56958             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
56959             this.maskEl.left.setLeft(0);
56960             this.maskEl.left.setTop(box.y - this.padding);
56961             this.maskEl.left.show();
56962
56963             this.maskEl.bottom.setStyle('position', 'absolute');
56964             this.maskEl.bottom.setStyle('z-index', 10000);
56965             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
56966             this.maskEl.bottom.setLeft(0);
56967             this.maskEl.bottom.setTop(box.bottom + this.padding);
56968             this.maskEl.bottom.show();
56969
56970             this.maskEl.right.setStyle('position', 'absolute');
56971             this.maskEl.right.setStyle('z-index', 10000);
56972             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
56973             this.maskEl.right.setLeft(box.right + this.padding);
56974             this.maskEl.right.setTop(box.y - this.padding);
56975             this.maskEl.right.show();
56976
56977             this.intervalID = window.setInterval(function() {
56978                 Roo.form.BasicForm.popover.unmask();
56979             }, 10000);
56980
56981             window.onwheel = function(){ return false;};
56982             
56983             (function(){ this.isMasked = true; }).defer(500, this);
56984             
56985         },
56986         
56987         unmask : function()
56988         {
56989             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
56990                 return;
56991             }
56992             
56993             this.maskEl.top.setStyle('position', 'absolute');
56994             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
56995             this.maskEl.top.hide();
56996
56997             this.maskEl.left.setStyle('position', 'absolute');
56998             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
56999             this.maskEl.left.hide();
57000
57001             this.maskEl.bottom.setStyle('position', 'absolute');
57002             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
57003             this.maskEl.bottom.hide();
57004
57005             this.maskEl.right.setStyle('position', 'absolute');
57006             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
57007             this.maskEl.right.hide();
57008             
57009             window.onwheel = function(){ return true;};
57010             
57011             if(this.intervalID){
57012                 window.clearInterval(this.intervalID);
57013                 this.intervalID = false;
57014             }
57015             
57016             this.isMasked = false;
57017             
57018         }
57019         
57020     }
57021     
57022 });/*
57023  * Based on:
57024  * Ext JS Library 1.1.1
57025  * Copyright(c) 2006-2007, Ext JS, LLC.
57026  *
57027  * Originally Released Under LGPL - original licence link has changed is not relivant.
57028  *
57029  * Fork - LGPL
57030  * <script type="text/javascript">
57031  */
57032
57033 /**
57034  * @class Roo.form.Form
57035  * @extends Roo.form.BasicForm
57036  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
57037  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
57038  * @constructor
57039  * @param {Object} config Configuration options
57040  */
57041 Roo.form.Form = function(config){
57042     var xitems =  [];
57043     if (config.items) {
57044         xitems = config.items;
57045         delete config.items;
57046     }
57047    
57048     
57049     Roo.form.Form.superclass.constructor.call(this, null, config);
57050     this.url = this.url || this.action;
57051     if(!this.root){
57052         this.root = new Roo.form.Layout(Roo.applyIf({
57053             id: Roo.id()
57054         }, config));
57055     }
57056     this.active = this.root;
57057     /**
57058      * Array of all the buttons that have been added to this form via {@link addButton}
57059      * @type Array
57060      */
57061     this.buttons = [];
57062     this.allItems = [];
57063     this.addEvents({
57064         /**
57065          * @event clientvalidation
57066          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
57067          * @param {Form} this
57068          * @param {Boolean} valid true if the form has passed client-side validation
57069          */
57070         clientvalidation: true,
57071         /**
57072          * @event rendered
57073          * Fires when the form is rendered
57074          * @param {Roo.form.Form} form
57075          */
57076         rendered : true
57077     });
57078     
57079     if (this.progressUrl) {
57080             // push a hidden field onto the list of fields..
57081             this.addxtype( {
57082                     xns: Roo.form, 
57083                     xtype : 'Hidden', 
57084                     name : 'UPLOAD_IDENTIFIER' 
57085             });
57086         }
57087         
57088     
57089     Roo.each(xitems, this.addxtype, this);
57090     
57091 };
57092
57093 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
57094      /**
57095      * @cfg {Roo.Button} buttons[] buttons at bottom of form
57096      */
57097     
57098     /**
57099      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
57100      */
57101     /**
57102      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
57103      */
57104     /**
57105      * @cfg {String} buttonAlign (left|center|right)  Valid values are "left," "center" and "right" (defaults to "center")
57106      */
57107     buttonAlign:'center',
57108
57109     /**
57110      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
57111      */
57112     minButtonWidth:75,
57113
57114     /**
57115      * @cfg {String} labelAlign (left|top|right) Valid values are "left," "top" and "right" (defaults to "left").
57116      * This property cascades to child containers if not set.
57117      */
57118     labelAlign:'left',
57119
57120     /**
57121      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
57122      * fires a looping event with that state. This is required to bind buttons to the valid
57123      * state using the config value formBind:true on the button.
57124      */
57125     monitorValid : false,
57126
57127     /**
57128      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
57129      */
57130     monitorPoll : 200,
57131     
57132     /**
57133      * @cfg {String} progressUrl - Url to return progress data 
57134      */
57135     
57136     progressUrl : false,
57137     /**
57138      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
57139      * sending a formdata with extra parameters - eg uploaded elements.
57140      */
57141     
57142     formData : false,
57143     
57144     /**
57145      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
57146      * fields are added and the column is closed. If no fields are passed the column remains open
57147      * until end() is called.
57148      * @param {Object} config The config to pass to the column
57149      * @param {Field} field1 (optional)
57150      * @param {Field} field2 (optional)
57151      * @param {Field} etc (optional)
57152      * @return Column The column container object
57153      */
57154     column : function(c){
57155         var col = new Roo.form.Column(c);
57156         this.start(col);
57157         if(arguments.length > 1){ // duplicate code required because of Opera
57158             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
57159             this.end();
57160         }
57161         return col;
57162     },
57163
57164     /**
57165      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
57166      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
57167      * until end() is called.
57168      * @param {Object} config The config to pass to the fieldset
57169      * @param {Field} field1 (optional)
57170      * @param {Field} field2 (optional)
57171      * @param {Field} etc (optional)
57172      * @return FieldSet The fieldset container object
57173      */
57174     fieldset : function(c){
57175         var fs = new Roo.form.FieldSet(c);
57176         this.start(fs);
57177         if(arguments.length > 1){ // duplicate code required because of Opera
57178             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
57179             this.end();
57180         }
57181         return fs;
57182     },
57183
57184     /**
57185      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
57186      * fields are added and the container is closed. If no fields are passed the container remains open
57187      * until end() is called.
57188      * @param {Object} config The config to pass to the Layout
57189      * @param {Field} field1 (optional)
57190      * @param {Field} field2 (optional)
57191      * @param {Field} etc (optional)
57192      * @return Layout The container object
57193      */
57194     container : function(c){
57195         var l = new Roo.form.Layout(c);
57196         this.start(l);
57197         if(arguments.length > 1){ // duplicate code required because of Opera
57198             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
57199             this.end();
57200         }
57201         return l;
57202     },
57203
57204     /**
57205      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
57206      * @param {Object} container A Roo.form.Layout or subclass of Layout
57207      * @return {Form} this
57208      */
57209     start : function(c){
57210         // cascade label info
57211         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
57212         this.active.stack.push(c);
57213         c.ownerCt = this.active;
57214         this.active = c;
57215         return this;
57216     },
57217
57218     /**
57219      * Closes the current open container
57220      * @return {Form} this
57221      */
57222     end : function(){
57223         if(this.active == this.root){
57224             return this;
57225         }
57226         this.active = this.active.ownerCt;
57227         return this;
57228     },
57229
57230     /**
57231      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
57232      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
57233      * as the label of the field.
57234      * @param {Field} field1
57235      * @param {Field} field2 (optional)
57236      * @param {Field} etc. (optional)
57237      * @return {Form} this
57238      */
57239     add : function(){
57240         this.active.stack.push.apply(this.active.stack, arguments);
57241         this.allItems.push.apply(this.allItems,arguments);
57242         var r = [];
57243         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
57244             if(a[i].isFormField){
57245                 r.push(a[i]);
57246             }
57247         }
57248         if(r.length > 0){
57249             Roo.form.Form.superclass.add.apply(this, r);
57250         }
57251         return this;
57252     },
57253     
57254
57255     
57256     
57257     
57258      /**
57259      * Find any element that has been added to a form, using it's ID or name
57260      * This can include framesets, columns etc. along with regular fields..
57261      * @param {String} id - id or name to find.
57262      
57263      * @return {Element} e - or false if nothing found.
57264      */
57265     findbyId : function(id)
57266     {
57267         var ret = false;
57268         if (!id) {
57269             return ret;
57270         }
57271         Roo.each(this.allItems, function(f){
57272             if (f.id == id || f.name == id ){
57273                 ret = f;
57274                 return false;
57275             }
57276         });
57277         return ret;
57278     },
57279
57280     
57281     
57282     /**
57283      * Render this form into the passed container. This should only be called once!
57284      * @param {String/HTMLElement/Element} container The element this component should be rendered into
57285      * @return {Form} this
57286      */
57287     render : function(ct)
57288     {
57289         
57290         
57291         
57292         ct = Roo.get(ct);
57293         var o = this.autoCreate || {
57294             tag: 'form',
57295             method : this.method || 'POST',
57296             id : this.id || Roo.id()
57297         };
57298         this.initEl(ct.createChild(o));
57299
57300         this.root.render(this.el);
57301         
57302        
57303              
57304         this.items.each(function(f){
57305             f.render('x-form-el-'+f.id);
57306         });
57307
57308         if(this.buttons.length > 0){
57309             // tables are required to maintain order and for correct IE layout
57310             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
57311                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
57312                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
57313             }}, null, true);
57314             var tr = tb.getElementsByTagName('tr')[0];
57315             for(var i = 0, len = this.buttons.length; i < len; i++) {
57316                 var b = this.buttons[i];
57317                 var td = document.createElement('td');
57318                 td.className = 'x-form-btn-td';
57319                 b.render(tr.appendChild(td));
57320             }
57321         }
57322         if(this.monitorValid){ // initialize after render
57323             this.startMonitoring();
57324         }
57325         this.fireEvent('rendered', this);
57326         return this;
57327     },
57328
57329     /**
57330      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
57331      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
57332      * object or a valid Roo.DomHelper element config
57333      * @param {Function} handler The function called when the button is clicked
57334      * @param {Object} scope (optional) The scope of the handler function
57335      * @return {Roo.Button}
57336      */
57337     addButton : function(config, handler, scope){
57338         var bc = {
57339             handler: handler,
57340             scope: scope,
57341             minWidth: this.minButtonWidth,
57342             hideParent:true
57343         };
57344         if(typeof config == "string"){
57345             bc.text = config;
57346         }else{
57347             Roo.apply(bc, config);
57348         }
57349         var btn = new Roo.Button(null, bc);
57350         this.buttons.push(btn);
57351         return btn;
57352     },
57353
57354      /**
57355      * Adds a series of form elements (using the xtype property as the factory method.
57356      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
57357      * @param {Object} config 
57358      */
57359     
57360     addxtype : function()
57361     {
57362         var ar = Array.prototype.slice.call(arguments, 0);
57363         var ret = false;
57364         for(var i = 0; i < ar.length; i++) {
57365             if (!ar[i]) {
57366                 continue; // skip -- if this happends something invalid got sent, we 
57367                 // should ignore it, as basically that interface element will not show up
57368                 // and that should be pretty obvious!!
57369             }
57370             
57371             if (Roo.form[ar[i].xtype]) {
57372                 ar[i].form = this;
57373                 var fe = Roo.factory(ar[i], Roo.form);
57374                 if (!ret) {
57375                     ret = fe;
57376                 }
57377                 fe.form = this;
57378                 if (fe.store) {
57379                     fe.store.form = this;
57380                 }
57381                 if (fe.isLayout) {  
57382                          
57383                     this.start(fe);
57384                     this.allItems.push(fe);
57385                     if (fe.items && fe.addxtype) {
57386                         fe.addxtype.apply(fe, fe.items);
57387                         delete fe.items;
57388                     }
57389                      this.end();
57390                     continue;
57391                 }
57392                 
57393                 
57394                  
57395                 this.add(fe);
57396               //  console.log('adding ' + ar[i].xtype);
57397             }
57398             if (ar[i].xtype == 'Button') {  
57399                 //console.log('adding button');
57400                 //console.log(ar[i]);
57401                 this.addButton(ar[i]);
57402                 this.allItems.push(fe);
57403                 continue;
57404             }
57405             
57406             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
57407                 alert('end is not supported on xtype any more, use items');
57408             //    this.end();
57409             //    //console.log('adding end');
57410             }
57411             
57412         }
57413         return ret;
57414     },
57415     
57416     /**
57417      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
57418      * option "monitorValid"
57419      */
57420     startMonitoring : function(){
57421         if(!this.bound){
57422             this.bound = true;
57423             Roo.TaskMgr.start({
57424                 run : this.bindHandler,
57425                 interval : this.monitorPoll || 200,
57426                 scope: this
57427             });
57428         }
57429     },
57430
57431     /**
57432      * Stops monitoring of the valid state of this form
57433      */
57434     stopMonitoring : function(){
57435         this.bound = false;
57436     },
57437
57438     // private
57439     bindHandler : function(){
57440         if(!this.bound){
57441             return false; // stops binding
57442         }
57443         var valid = true;
57444         this.items.each(function(f){
57445             if(!f.isValid(true)){
57446                 valid = false;
57447                 return false;
57448             }
57449         });
57450         for(var i = 0, len = this.buttons.length; i < len; i++){
57451             var btn = this.buttons[i];
57452             if(btn.formBind === true && btn.disabled === valid){
57453                 btn.setDisabled(!valid);
57454             }
57455         }
57456         this.fireEvent('clientvalidation', this, valid);
57457     }
57458     
57459     
57460     
57461     
57462     
57463     
57464     
57465     
57466 });
57467
57468
57469 // back compat
57470 Roo.Form = Roo.form.Form;
57471 /*
57472  * Based on:
57473  * Ext JS Library 1.1.1
57474  * Copyright(c) 2006-2007, Ext JS, LLC.
57475  *
57476  * Originally Released Under LGPL - original licence link has changed is not relivant.
57477  *
57478  * Fork - LGPL
57479  * <script type="text/javascript">
57480  */
57481
57482 // as we use this in bootstrap.
57483 Roo.namespace('Roo.form');
57484  /**
57485  * @class Roo.form.Action
57486  * Internal Class used to handle form actions
57487  * @constructor
57488  * @param {Roo.form.BasicForm} el The form element or its id
57489  * @param {Object} config Configuration options
57490  */
57491
57492  
57493  
57494 // define the action interface
57495 Roo.form.Action = function(form, options){
57496     this.form = form;
57497     this.options = options || {};
57498 };
57499 /**
57500  * Client Validation Failed
57501  * @const 
57502  */
57503 Roo.form.Action.CLIENT_INVALID = 'client';
57504 /**
57505  * Server Validation Failed
57506  * @const 
57507  */
57508 Roo.form.Action.SERVER_INVALID = 'server';
57509  /**
57510  * Connect to Server Failed
57511  * @const 
57512  */
57513 Roo.form.Action.CONNECT_FAILURE = 'connect';
57514 /**
57515  * Reading Data from Server Failed
57516  * @const 
57517  */
57518 Roo.form.Action.LOAD_FAILURE = 'load';
57519
57520 Roo.form.Action.prototype = {
57521     type : 'default',
57522     failureType : undefined,
57523     response : undefined,
57524     result : undefined,
57525
57526     // interface method
57527     run : function(options){
57528
57529     },
57530
57531     // interface method
57532     success : function(response){
57533
57534     },
57535
57536     // interface method
57537     handleResponse : function(response){
57538
57539     },
57540
57541     // default connection failure
57542     failure : function(response){
57543         
57544         this.response = response;
57545         this.failureType = Roo.form.Action.CONNECT_FAILURE;
57546         this.form.afterAction(this, false);
57547     },
57548
57549     processResponse : function(response){
57550         this.response = response;
57551         if(!response.responseText){
57552             return true;
57553         }
57554         this.result = this.handleResponse(response);
57555         return this.result;
57556     },
57557
57558     // utility functions used internally
57559     getUrl : function(appendParams){
57560         var url = this.options.url || this.form.url || this.form.el.dom.action;
57561         if(appendParams){
57562             var p = this.getParams();
57563             if(p){
57564                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
57565             }
57566         }
57567         return url;
57568     },
57569
57570     getMethod : function(){
57571         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
57572     },
57573
57574     getParams : function(){
57575         var bp = this.form.baseParams;
57576         var p = this.options.params;
57577         if(p){
57578             if(typeof p == "object"){
57579                 p = Roo.urlEncode(Roo.applyIf(p, bp));
57580             }else if(typeof p == 'string' && bp){
57581                 p += '&' + Roo.urlEncode(bp);
57582             }
57583         }else if(bp){
57584             p = Roo.urlEncode(bp);
57585         }
57586         return p;
57587     },
57588
57589     createCallback : function(){
57590         return {
57591             success: this.success,
57592             failure: this.failure,
57593             scope: this,
57594             timeout: (this.form.timeout*1000),
57595             upload: this.form.fileUpload ? this.success : undefined
57596         };
57597     }
57598 };
57599
57600 Roo.form.Action.Submit = function(form, options){
57601     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
57602 };
57603
57604 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
57605     type : 'submit',
57606
57607     haveProgress : false,
57608     uploadComplete : false,
57609     
57610     // uploadProgress indicator.
57611     uploadProgress : function()
57612     {
57613         if (!this.form.progressUrl) {
57614             return;
57615         }
57616         
57617         if (!this.haveProgress) {
57618             Roo.MessageBox.progress("Uploading", "Uploading");
57619         }
57620         if (this.uploadComplete) {
57621            Roo.MessageBox.hide();
57622            return;
57623         }
57624         
57625         this.haveProgress = true;
57626    
57627         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
57628         
57629         var c = new Roo.data.Connection();
57630         c.request({
57631             url : this.form.progressUrl,
57632             params: {
57633                 id : uid
57634             },
57635             method: 'GET',
57636             success : function(req){
57637                //console.log(data);
57638                 var rdata = false;
57639                 var edata;
57640                 try  {
57641                    rdata = Roo.decode(req.responseText)
57642                 } catch (e) {
57643                     Roo.log("Invalid data from server..");
57644                     Roo.log(edata);
57645                     return;
57646                 }
57647                 if (!rdata || !rdata.success) {
57648                     Roo.log(rdata);
57649                     Roo.MessageBox.alert(Roo.encode(rdata));
57650                     return;
57651                 }
57652                 var data = rdata.data;
57653                 
57654                 if (this.uploadComplete) {
57655                    Roo.MessageBox.hide();
57656                    return;
57657                 }
57658                    
57659                 if (data){
57660                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
57661                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
57662                     );
57663                 }
57664                 this.uploadProgress.defer(2000,this);
57665             },
57666        
57667             failure: function(data) {
57668                 Roo.log('progress url failed ');
57669                 Roo.log(data);
57670             },
57671             scope : this
57672         });
57673            
57674     },
57675     
57676     
57677     run : function()
57678     {
57679         // run get Values on the form, so it syncs any secondary forms.
57680         this.form.getValues();
57681         
57682         var o = this.options;
57683         var method = this.getMethod();
57684         var isPost = method == 'POST';
57685         if(o.clientValidation === false || this.form.isValid()){
57686             
57687             if (this.form.progressUrl) {
57688                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
57689                     (new Date() * 1) + '' + Math.random());
57690                     
57691             } 
57692             
57693             
57694             Roo.Ajax.request(Roo.apply(this.createCallback(), {
57695                 form:this.form.el.dom,
57696                 url:this.getUrl(!isPost),
57697                 method: method,
57698                 params:isPost ? this.getParams() : null,
57699                 isUpload: this.form.fileUpload,
57700                 formData : this.form.formData
57701             }));
57702             
57703             this.uploadProgress();
57704
57705         }else if (o.clientValidation !== false){ // client validation failed
57706             this.failureType = Roo.form.Action.CLIENT_INVALID;
57707             this.form.afterAction(this, false);
57708         }
57709     },
57710
57711     success : function(response)
57712     {
57713         this.uploadComplete= true;
57714         if (this.haveProgress) {
57715             Roo.MessageBox.hide();
57716         }
57717         
57718         
57719         var result = this.processResponse(response);
57720         if(result === true || result.success){
57721             this.form.afterAction(this, true);
57722             return;
57723         }
57724         if(result.errors){
57725             this.form.markInvalid(result.errors);
57726             this.failureType = Roo.form.Action.SERVER_INVALID;
57727         }
57728         this.form.afterAction(this, false);
57729     },
57730     failure : function(response)
57731     {
57732         this.uploadComplete= true;
57733         if (this.haveProgress) {
57734             Roo.MessageBox.hide();
57735         }
57736         
57737         this.response = response;
57738         this.failureType = Roo.form.Action.CONNECT_FAILURE;
57739         this.form.afterAction(this, false);
57740     },
57741     
57742     handleResponse : function(response){
57743         if(this.form.errorReader){
57744             var rs = this.form.errorReader.read(response);
57745             var errors = [];
57746             if(rs.records){
57747                 for(var i = 0, len = rs.records.length; i < len; i++) {
57748                     var r = rs.records[i];
57749                     errors[i] = r.data;
57750                 }
57751             }
57752             if(errors.length < 1){
57753                 errors = null;
57754             }
57755             return {
57756                 success : rs.success,
57757                 errors : errors
57758             };
57759         }
57760         var ret = false;
57761         try {
57762             var rt = response.responseText;
57763             if (rt.match(/^\<!--\[CDATA\[/)) {
57764                 rt = rt.replace(/^\<!--\[CDATA\[/,'');
57765                 rt = rt.replace(/\]\]--\>$/,'');
57766             }
57767             
57768             ret = Roo.decode(rt);
57769         } catch (e) {
57770             ret = {
57771                 success: false,
57772                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
57773                 errors : []
57774             };
57775         }
57776         return ret;
57777         
57778     }
57779 });
57780
57781
57782 Roo.form.Action.Load = function(form, options){
57783     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
57784     this.reader = this.form.reader;
57785 };
57786
57787 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
57788     type : 'load',
57789
57790     run : function(){
57791         
57792         Roo.Ajax.request(Roo.apply(
57793                 this.createCallback(), {
57794                     method:this.getMethod(),
57795                     url:this.getUrl(false),
57796                     params:this.getParams()
57797         }));
57798     },
57799
57800     success : function(response){
57801         
57802         var result = this.processResponse(response);
57803         if(result === true || !result.success || !result.data){
57804             this.failureType = Roo.form.Action.LOAD_FAILURE;
57805             this.form.afterAction(this, false);
57806             return;
57807         }
57808         this.form.clearInvalid();
57809         this.form.setValues(result.data);
57810         this.form.afterAction(this, true);
57811     },
57812
57813     handleResponse : function(response){
57814         if(this.form.reader){
57815             var rs = this.form.reader.read(response);
57816             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
57817             return {
57818                 success : rs.success,
57819                 data : data
57820             };
57821         }
57822         return Roo.decode(response.responseText);
57823     }
57824 });
57825
57826 Roo.form.Action.ACTION_TYPES = {
57827     'load' : Roo.form.Action.Load,
57828     'submit' : Roo.form.Action.Submit
57829 };/*
57830  * Based on:
57831  * Ext JS Library 1.1.1
57832  * Copyright(c) 2006-2007, Ext JS, LLC.
57833  *
57834  * Originally Released Under LGPL - original licence link has changed is not relivant.
57835  *
57836  * Fork - LGPL
57837  * <script type="text/javascript">
57838  */
57839  
57840 /**
57841  * @class Roo.form.Layout
57842  * @extends Roo.Component
57843  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
57844  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
57845  * @constructor
57846  * @param {Object} config Configuration options
57847  */
57848 Roo.form.Layout = function(config){
57849     var xitems = [];
57850     if (config.items) {
57851         xitems = config.items;
57852         delete config.items;
57853     }
57854     Roo.form.Layout.superclass.constructor.call(this, config);
57855     this.stack = [];
57856     Roo.each(xitems, this.addxtype, this);
57857      
57858 };
57859
57860 Roo.extend(Roo.form.Layout, Roo.Component, {
57861     /**
57862      * @cfg {String/Object} autoCreate
57863      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
57864      */
57865     /**
57866      * @cfg {String/Object/Function} style
57867      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
57868      * a function which returns such a specification.
57869      */
57870     /**
57871      * @cfg {String} labelAlign (left|top|right)
57872      * Valid values are "left," "top" and "right" (defaults to "left")
57873      */
57874     /**
57875      * @cfg {Number} labelWidth
57876      * Fixed width in pixels of all field labels (defaults to undefined)
57877      */
57878     /**
57879      * @cfg {Boolean} clear
57880      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
57881      */
57882     clear : true,
57883     /**
57884      * @cfg {String} labelSeparator
57885      * The separator to use after field labels (defaults to ':')
57886      */
57887     labelSeparator : ':',
57888     /**
57889      * @cfg {Boolean} hideLabels
57890      * True to suppress the display of field labels in this layout (defaults to false)
57891      */
57892     hideLabels : false,
57893
57894     // private
57895     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
57896     
57897     isLayout : true,
57898     
57899     // private
57900     onRender : function(ct, position){
57901         if(this.el){ // from markup
57902             this.el = Roo.get(this.el);
57903         }else {  // generate
57904             var cfg = this.getAutoCreate();
57905             this.el = ct.createChild(cfg, position);
57906         }
57907         if(this.style){
57908             this.el.applyStyles(this.style);
57909         }
57910         if(this.labelAlign){
57911             this.el.addClass('x-form-label-'+this.labelAlign);
57912         }
57913         if(this.hideLabels){
57914             this.labelStyle = "display:none";
57915             this.elementStyle = "padding-left:0;";
57916         }else{
57917             if(typeof this.labelWidth == 'number'){
57918                 this.labelStyle = "width:"+this.labelWidth+"px;";
57919                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
57920             }
57921             if(this.labelAlign == 'top'){
57922                 this.labelStyle = "width:auto;";
57923                 this.elementStyle = "padding-left:0;";
57924             }
57925         }
57926         var stack = this.stack;
57927         var slen = stack.length;
57928         if(slen > 0){
57929             if(!this.fieldTpl){
57930                 var t = new Roo.Template(
57931                     '<div class="x-form-item {5}">',
57932                         '<label for="{0}" style="{2}">{1}{4}</label>',
57933                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
57934                         '</div>',
57935                     '</div><div class="x-form-clear-left"></div>'
57936                 );
57937                 t.disableFormats = true;
57938                 t.compile();
57939                 Roo.form.Layout.prototype.fieldTpl = t;
57940             }
57941             for(var i = 0; i < slen; i++) {
57942                 if(stack[i].isFormField){
57943                     this.renderField(stack[i]);
57944                 }else{
57945                     this.renderComponent(stack[i]);
57946                 }
57947             }
57948         }
57949         if(this.clear){
57950             this.el.createChild({cls:'x-form-clear'});
57951         }
57952     },
57953
57954     // private
57955     renderField : function(f){
57956         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
57957                f.id, //0
57958                f.fieldLabel, //1
57959                f.labelStyle||this.labelStyle||'', //2
57960                this.elementStyle||'', //3
57961                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
57962                f.itemCls||this.itemCls||''  //5
57963        ], true).getPrevSibling());
57964     },
57965
57966     // private
57967     renderComponent : function(c){
57968         c.render(c.isLayout ? this.el : this.el.createChild());    
57969     },
57970     /**
57971      * Adds a object form elements (using the xtype property as the factory method.)
57972      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
57973      * @param {Object} config 
57974      */
57975     addxtype : function(o)
57976     {
57977         // create the lement.
57978         o.form = this.form;
57979         var fe = Roo.factory(o, Roo.form);
57980         this.form.allItems.push(fe);
57981         this.stack.push(fe);
57982         
57983         if (fe.isFormField) {
57984             this.form.items.add(fe);
57985         }
57986          
57987         return fe;
57988     }
57989 });
57990
57991
57992 /**
57993  * @class Roo.form.Column
57994  * @extends Roo.form.Layout
57995  * @children Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
57996  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
57997  * @constructor
57998  * @param {Object} config Configuration options
57999  */
58000 Roo.form.Column = function(config){
58001     Roo.form.Column.superclass.constructor.call(this, config);
58002 };
58003
58004 Roo.extend(Roo.form.Column, Roo.form.Layout, {
58005     /**
58006      * @cfg {Number/String} width
58007      * The fixed width of the column in pixels or CSS value (defaults to "auto")
58008      */
58009     /**
58010      * @cfg {String/Object} autoCreate
58011      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
58012      */
58013
58014     // private
58015     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
58016
58017     // private
58018     onRender : function(ct, position){
58019         Roo.form.Column.superclass.onRender.call(this, ct, position);
58020         if(this.width){
58021             this.el.setWidth(this.width);
58022         }
58023     }
58024 });
58025
58026 /**
58027  * @class Roo.form.Row
58028  * @extends Roo.form.Layout
58029  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
58030  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
58031  * @constructor
58032  * @param {Object} config Configuration options
58033  */
58034
58035  
58036 Roo.form.Row = function(config){
58037     Roo.form.Row.superclass.constructor.call(this, config);
58038 };
58039  
58040 Roo.extend(Roo.form.Row, Roo.form.Layout, {
58041       /**
58042      * @cfg {Number/String} width
58043      * The fixed width of the column in pixels or CSS value (defaults to "auto")
58044      */
58045     /**
58046      * @cfg {Number/String} height
58047      * The fixed height of the column in pixels or CSS value (defaults to "auto")
58048      */
58049     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
58050     
58051     padWidth : 20,
58052     // private
58053     onRender : function(ct, position){
58054         //console.log('row render');
58055         if(!this.rowTpl){
58056             var t = new Roo.Template(
58057                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
58058                     '<label for="{0}" style="{2}">{1}{4}</label>',
58059                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
58060                     '</div>',
58061                 '</div>'
58062             );
58063             t.disableFormats = true;
58064             t.compile();
58065             Roo.form.Layout.prototype.rowTpl = t;
58066         }
58067         this.fieldTpl = this.rowTpl;
58068         
58069         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
58070         var labelWidth = 100;
58071         
58072         if ((this.labelAlign != 'top')) {
58073             if (typeof this.labelWidth == 'number') {
58074                 labelWidth = this.labelWidth
58075             }
58076             this.padWidth =  20 + labelWidth;
58077             
58078         }
58079         
58080         Roo.form.Column.superclass.onRender.call(this, ct, position);
58081         if(this.width){
58082             this.el.setWidth(this.width);
58083         }
58084         if(this.height){
58085             this.el.setHeight(this.height);
58086         }
58087     },
58088     
58089     // private
58090     renderField : function(f){
58091         f.fieldEl = this.fieldTpl.append(this.el, [
58092                f.id, f.fieldLabel,
58093                f.labelStyle||this.labelStyle||'',
58094                this.elementStyle||'',
58095                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
58096                f.itemCls||this.itemCls||'',
58097                f.width ? f.width + this.padWidth : 160 + this.padWidth
58098        ],true);
58099     }
58100 });
58101  
58102
58103 /**
58104  * @class Roo.form.FieldSet
58105  * @extends Roo.form.Layout
58106  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
58107  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
58108  * @constructor
58109  * @param {Object} config Configuration options
58110  */
58111 Roo.form.FieldSet = function(config){
58112     Roo.form.FieldSet.superclass.constructor.call(this, config);
58113 };
58114
58115 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
58116     /**
58117      * @cfg {String} legend
58118      * The text to display as the legend for the FieldSet (defaults to '')
58119      */
58120     /**
58121      * @cfg {String/Object} autoCreate
58122      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
58123      */
58124
58125     // private
58126     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
58127
58128     // private
58129     onRender : function(ct, position){
58130         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
58131         if(this.legend){
58132             this.setLegend(this.legend);
58133         }
58134     },
58135
58136     // private
58137     setLegend : function(text){
58138         if(this.rendered){
58139             this.el.child('legend').update(text);
58140         }
58141     }
58142 });/*
58143  * Based on:
58144  * Ext JS Library 1.1.1
58145  * Copyright(c) 2006-2007, Ext JS, LLC.
58146  *
58147  * Originally Released Under LGPL - original licence link has changed is not relivant.
58148  *
58149  * Fork - LGPL
58150  * <script type="text/javascript">
58151  */
58152 /**
58153  * @class Roo.form.VTypes
58154  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
58155  * @static
58156  */
58157 Roo.form.VTypes = function(){
58158     // closure these in so they are only created once.
58159     var alpha = /^[a-zA-Z_]+$/;
58160     var alphanum = /^[a-zA-Z0-9_]+$/;
58161     var email = /^([\w'-]+)(\.[\w'-]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
58162     var url = /^(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
58163     var urlWeb = /^((https?):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
58164
58165     // All these messages and functions are configurable
58166     return {
58167         /**
58168          * The function used to validate email addresses
58169          * @param {String} value The email address
58170          */
58171         email : function(v){
58172             return email.test(v);
58173         },
58174         /**
58175          * The error text to display when the email validation function returns false
58176          * @type String
58177          */
58178         emailText : 'This field should be an e-mail address in the format "user@domain.com"',
58179         /**
58180          * The keystroke filter mask to be applied on email input
58181          * @type RegExp
58182          */
58183         emailMask : /[a-z0-9_\.\-@]/i,
58184
58185         /**
58186          * The function used to validate URLs
58187          * @param {String} value The URL
58188          */
58189         url : function(v){
58190             return url.test(v);
58191         },
58192         /**
58193          * The funciton used to validate URLs (only allow schemes 'https' and 'http')
58194          * @param {String} v The URL
58195          */
58196         urlWeb : function(v) {
58197             return urlWeb.test(v);
58198         },
58199         /**
58200          * The error text to display when the url validation function returns false
58201          * @type String
58202          */
58203         urlText : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
58204         
58205         /**
58206          * The function used to validate alpha values
58207          * @param {String} value The value
58208          */
58209         alpha : function(v){
58210             return alpha.test(v);
58211         },
58212         /**
58213          * The error text to display when the alpha validation function returns false
58214          * @type String
58215          */
58216         alphaText : 'This field should only contain letters and _',
58217         /**
58218          * The keystroke filter mask to be applied on alpha input
58219          * @type RegExp
58220          */
58221         alphaMask : /[a-z_]/i,
58222
58223         /**
58224          * The function used to validate alphanumeric values
58225          * @param {String} value The value
58226          */
58227         alphanum : function(v){
58228             return alphanum.test(v);
58229         },
58230         /**
58231          * The error text to display when the alphanumeric validation function returns false
58232          * @type String
58233          */
58234         alphanumText : 'This field should only contain letters, numbers and _',
58235         /**
58236          * The keystroke filter mask to be applied on alphanumeric input
58237          * @type RegExp
58238          */
58239         alphanumMask : /[a-z0-9_]/i
58240     };
58241 }();//<script type="text/javascript">
58242
58243 /**
58244  * @class Roo.form.FCKeditor
58245  * @extends Roo.form.TextArea
58246  * Wrapper around the FCKEditor http://www.fckeditor.net
58247  * @constructor
58248  * Creates a new FCKeditor
58249  * @param {Object} config Configuration options
58250  */
58251 Roo.form.FCKeditor = function(config){
58252     Roo.form.FCKeditor.superclass.constructor.call(this, config);
58253     this.addEvents({
58254          /**
58255          * @event editorinit
58256          * Fired when the editor is initialized - you can add extra handlers here..
58257          * @param {FCKeditor} this
58258          * @param {Object} the FCK object.
58259          */
58260         editorinit : true
58261     });
58262     
58263     
58264 };
58265 Roo.form.FCKeditor.editors = { };
58266 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
58267 {
58268     //defaultAutoCreate : {
58269     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
58270     //},
58271     // private
58272     /**
58273      * @cfg {Object} fck options - see fck manual for details.
58274      */
58275     fckconfig : false,
58276     
58277     /**
58278      * @cfg {Object} fck toolbar set (Basic or Default)
58279      */
58280     toolbarSet : 'Basic',
58281     /**
58282      * @cfg {Object} fck BasePath
58283      */ 
58284     basePath : '/fckeditor/',
58285     
58286     
58287     frame : false,
58288     
58289     value : '',
58290     
58291    
58292     onRender : function(ct, position)
58293     {
58294         if(!this.el){
58295             this.defaultAutoCreate = {
58296                 tag: "textarea",
58297                 style:"width:300px;height:60px;",
58298                 autocomplete: "new-password"
58299             };
58300         }
58301         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
58302         /*
58303         if(this.grow){
58304             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
58305             if(this.preventScrollbars){
58306                 this.el.setStyle("overflow", "hidden");
58307             }
58308             this.el.setHeight(this.growMin);
58309         }
58310         */
58311         //console.log('onrender' + this.getId() );
58312         Roo.form.FCKeditor.editors[this.getId()] = this;
58313          
58314
58315         this.replaceTextarea() ;
58316         
58317     },
58318     
58319     getEditor : function() {
58320         return this.fckEditor;
58321     },
58322     /**
58323      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
58324      * @param {Mixed} value The value to set
58325      */
58326     
58327     
58328     setValue : function(value)
58329     {
58330         //console.log('setValue: ' + value);
58331         
58332         if(typeof(value) == 'undefined') { // not sure why this is happending...
58333             return;
58334         }
58335         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
58336         
58337         //if(!this.el || !this.getEditor()) {
58338         //    this.value = value;
58339             //this.setValue.defer(100,this,[value]);    
58340         //    return;
58341         //} 
58342         
58343         if(!this.getEditor()) {
58344             return;
58345         }
58346         
58347         this.getEditor().SetData(value);
58348         
58349         //
58350
58351     },
58352
58353     /**
58354      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
58355      * @return {Mixed} value The field value
58356      */
58357     getValue : function()
58358     {
58359         
58360         if (this.frame && this.frame.dom.style.display == 'none') {
58361             return Roo.form.FCKeditor.superclass.getValue.call(this);
58362         }
58363         
58364         if(!this.el || !this.getEditor()) {
58365            
58366            // this.getValue.defer(100,this); 
58367             return this.value;
58368         }
58369        
58370         
58371         var value=this.getEditor().GetData();
58372         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
58373         return Roo.form.FCKeditor.superclass.getValue.call(this);
58374         
58375
58376     },
58377
58378     /**
58379      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
58380      * @return {Mixed} value The field value
58381      */
58382     getRawValue : function()
58383     {
58384         if (this.frame && this.frame.dom.style.display == 'none') {
58385             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
58386         }
58387         
58388         if(!this.el || !this.getEditor()) {
58389             //this.getRawValue.defer(100,this); 
58390             return this.value;
58391             return;
58392         }
58393         
58394         
58395         
58396         var value=this.getEditor().GetData();
58397         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
58398         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
58399          
58400     },
58401     
58402     setSize : function(w,h) {
58403         
58404         
58405         
58406         //if (this.frame && this.frame.dom.style.display == 'none') {
58407         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
58408         //    return;
58409         //}
58410         //if(!this.el || !this.getEditor()) {
58411         //    this.setSize.defer(100,this, [w,h]); 
58412         //    return;
58413         //}
58414         
58415         
58416         
58417         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
58418         
58419         this.frame.dom.setAttribute('width', w);
58420         this.frame.dom.setAttribute('height', h);
58421         this.frame.setSize(w,h);
58422         
58423     },
58424     
58425     toggleSourceEdit : function(value) {
58426         
58427       
58428          
58429         this.el.dom.style.display = value ? '' : 'none';
58430         this.frame.dom.style.display = value ?  'none' : '';
58431         
58432     },
58433     
58434     
58435     focus: function(tag)
58436     {
58437         if (this.frame.dom.style.display == 'none') {
58438             return Roo.form.FCKeditor.superclass.focus.call(this);
58439         }
58440         if(!this.el || !this.getEditor()) {
58441             this.focus.defer(100,this, [tag]); 
58442             return;
58443         }
58444         
58445         
58446         
58447         
58448         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
58449         this.getEditor().Focus();
58450         if (tgs.length) {
58451             if (!this.getEditor().Selection.GetSelection()) {
58452                 this.focus.defer(100,this, [tag]); 
58453                 return;
58454             }
58455             
58456             
58457             var r = this.getEditor().EditorDocument.createRange();
58458             r.setStart(tgs[0],0);
58459             r.setEnd(tgs[0],0);
58460             this.getEditor().Selection.GetSelection().removeAllRanges();
58461             this.getEditor().Selection.GetSelection().addRange(r);
58462             this.getEditor().Focus();
58463         }
58464         
58465     },
58466     
58467     
58468     
58469     replaceTextarea : function()
58470     {
58471         if ( document.getElementById( this.getId() + '___Frame' ) ) {
58472             return ;
58473         }
58474         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
58475         //{
58476             // We must check the elements firstly using the Id and then the name.
58477         var oTextarea = document.getElementById( this.getId() );
58478         
58479         var colElementsByName = document.getElementsByName( this.getId() ) ;
58480          
58481         oTextarea.style.display = 'none' ;
58482
58483         if ( oTextarea.tabIndex ) {            
58484             this.TabIndex = oTextarea.tabIndex ;
58485         }
58486         
58487         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
58488         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
58489         this.frame = Roo.get(this.getId() + '___Frame')
58490     },
58491     
58492     _getConfigHtml : function()
58493     {
58494         var sConfig = '' ;
58495
58496         for ( var o in this.fckconfig ) {
58497             sConfig += sConfig.length > 0  ? '&amp;' : '';
58498             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
58499         }
58500
58501         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
58502     },
58503     
58504     
58505     _getIFrameHtml : function()
58506     {
58507         var sFile = 'fckeditor.html' ;
58508         /* no idea what this is about..
58509         try
58510         {
58511             if ( (/fcksource=true/i).test( window.top.location.search ) )
58512                 sFile = 'fckeditor.original.html' ;
58513         }
58514         catch (e) { 
58515         */
58516
58517         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
58518         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
58519         
58520         
58521         var html = '<iframe id="' + this.getId() +
58522             '___Frame" src="' + sLink +
58523             '" width="' + this.width +
58524             '" height="' + this.height + '"' +
58525             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
58526             ' frameborder="0" scrolling="no"></iframe>' ;
58527
58528         return html ;
58529     },
58530     
58531     _insertHtmlBefore : function( html, element )
58532     {
58533         if ( element.insertAdjacentHTML )       {
58534             // IE
58535             element.insertAdjacentHTML( 'beforeBegin', html ) ;
58536         } else { // Gecko
58537             var oRange = document.createRange() ;
58538             oRange.setStartBefore( element ) ;
58539             var oFragment = oRange.createContextualFragment( html );
58540             element.parentNode.insertBefore( oFragment, element ) ;
58541         }
58542     }
58543     
58544     
58545   
58546     
58547     
58548     
58549     
58550
58551 });
58552
58553 //Roo.reg('fckeditor', Roo.form.FCKeditor);
58554
58555 function FCKeditor_OnComplete(editorInstance){
58556     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
58557     f.fckEditor = editorInstance;
58558     //console.log("loaded");
58559     f.fireEvent('editorinit', f, editorInstance);
58560
58561   
58562
58563  
58564
58565
58566
58567
58568
58569
58570
58571
58572
58573
58574
58575
58576
58577
58578
58579 //<script type="text/javascript">
58580 /**
58581  * @class Roo.form.GridField
58582  * @extends Roo.form.Field
58583  * Embed a grid (or editable grid into a form)
58584  * STATUS ALPHA
58585  * 
58586  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
58587  * it needs 
58588  * xgrid.store = Roo.data.Store
58589  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
58590  * xgrid.store.reader = Roo.data.JsonReader 
58591  * 
58592  * 
58593  * @constructor
58594  * Creates a new GridField
58595  * @param {Object} config Configuration options
58596  */
58597 Roo.form.GridField = function(config){
58598     Roo.form.GridField.superclass.constructor.call(this, config);
58599      
58600 };
58601
58602 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
58603     /**
58604      * @cfg {Number} width  - used to restrict width of grid..
58605      */
58606     width : 100,
58607     /**
58608      * @cfg {Number} height - used to restrict height of grid..
58609      */
58610     height : 50,
58611      /**
58612      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
58613          * 
58614          *}
58615      */
58616     xgrid : false, 
58617     /**
58618      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
58619      * {tag: "input", type: "checkbox", autocomplete: "off"})
58620      */
58621    // defaultAutoCreate : { tag: 'div' },
58622     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
58623     /**
58624      * @cfg {String} addTitle Text to include for adding a title.
58625      */
58626     addTitle : false,
58627     //
58628     onResize : function(){
58629         Roo.form.Field.superclass.onResize.apply(this, arguments);
58630     },
58631
58632     initEvents : function(){
58633         // Roo.form.Checkbox.superclass.initEvents.call(this);
58634         // has no events...
58635        
58636     },
58637
58638
58639     getResizeEl : function(){
58640         return this.wrap;
58641     },
58642
58643     getPositionEl : function(){
58644         return this.wrap;
58645     },
58646
58647     // private
58648     onRender : function(ct, position){
58649         
58650         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
58651         var style = this.style;
58652         delete this.style;
58653         
58654         Roo.form.GridField.superclass.onRender.call(this, ct, position);
58655         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
58656         this.viewEl = this.wrap.createChild({ tag: 'div' });
58657         if (style) {
58658             this.viewEl.applyStyles(style);
58659         }
58660         if (this.width) {
58661             this.viewEl.setWidth(this.width);
58662         }
58663         if (this.height) {
58664             this.viewEl.setHeight(this.height);
58665         }
58666         //if(this.inputValue !== undefined){
58667         //this.setValue(this.value);
58668         
58669         
58670         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
58671         
58672         
58673         this.grid.render();
58674         this.grid.getDataSource().on('remove', this.refreshValue, this);
58675         this.grid.getDataSource().on('update', this.refreshValue, this);
58676         this.grid.on('afteredit', this.refreshValue, this);
58677  
58678     },
58679      
58680     
58681     /**
58682      * Sets the value of the item. 
58683      * @param {String} either an object  or a string..
58684      */
58685     setValue : function(v){
58686         //this.value = v;
58687         v = v || []; // empty set..
58688         // this does not seem smart - it really only affects memoryproxy grids..
58689         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
58690             var ds = this.grid.getDataSource();
58691             // assumes a json reader..
58692             var data = {}
58693             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
58694             ds.loadData( data);
58695         }
58696         // clear selection so it does not get stale.
58697         if (this.grid.sm) { 
58698             this.grid.sm.clearSelections();
58699         }
58700         
58701         Roo.form.GridField.superclass.setValue.call(this, v);
58702         this.refreshValue();
58703         // should load data in the grid really....
58704     },
58705     
58706     // private
58707     refreshValue: function() {
58708          var val = [];
58709         this.grid.getDataSource().each(function(r) {
58710             val.push(r.data);
58711         });
58712         this.el.dom.value = Roo.encode(val);
58713     }
58714     
58715      
58716     
58717     
58718 });/*
58719  * Based on:
58720  * Ext JS Library 1.1.1
58721  * Copyright(c) 2006-2007, Ext JS, LLC.
58722  *
58723  * Originally Released Under LGPL - original licence link has changed is not relivant.
58724  *
58725  * Fork - LGPL
58726  * <script type="text/javascript">
58727  */
58728 /**
58729  * @class Roo.form.DisplayField
58730  * @extends Roo.form.Field
58731  * A generic Field to display non-editable data.
58732  * @cfg {Boolean} closable (true|false) default false
58733  * @constructor
58734  * Creates a new Display Field item.
58735  * @param {Object} config Configuration options
58736  */
58737 Roo.form.DisplayField = function(config){
58738     Roo.form.DisplayField.superclass.constructor.call(this, config);
58739     
58740     this.addEvents({
58741         /**
58742          * @event close
58743          * Fires after the click the close btn
58744              * @param {Roo.form.DisplayField} this
58745              */
58746         close : true
58747     });
58748 };
58749
58750 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
58751     inputType:      'hidden',
58752     allowBlank:     true,
58753     readOnly:         true,
58754     
58755  
58756     /**
58757      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
58758      */
58759     focusClass : undefined,
58760     /**
58761      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
58762      */
58763     fieldClass: 'x-form-field',
58764     
58765      /**
58766      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
58767      */
58768     valueRenderer: undefined,
58769     
58770     width: 100,
58771     /**
58772      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
58773      * {tag: "input", type: "checkbox", autocomplete: "off"})
58774      */
58775      
58776  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
58777  
58778     closable : false,
58779     
58780     onResize : function(){
58781         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
58782         
58783     },
58784
58785     initEvents : function(){
58786         // Roo.form.Checkbox.superclass.initEvents.call(this);
58787         // has no events...
58788         
58789         if(this.closable){
58790             this.closeEl.on('click', this.onClose, this);
58791         }
58792        
58793     },
58794
58795
58796     getResizeEl : function(){
58797         return this.wrap;
58798     },
58799
58800     getPositionEl : function(){
58801         return this.wrap;
58802     },
58803
58804     // private
58805     onRender : function(ct, position){
58806         
58807         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
58808         //if(this.inputValue !== undefined){
58809         this.wrap = this.el.wrap();
58810         
58811         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
58812         
58813         if(this.closable){
58814             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
58815         }
58816         
58817         if (this.bodyStyle) {
58818             this.viewEl.applyStyles(this.bodyStyle);
58819         }
58820         //this.viewEl.setStyle('padding', '2px');
58821         
58822         this.setValue(this.value);
58823         
58824     },
58825 /*
58826     // private
58827     initValue : Roo.emptyFn,
58828
58829   */
58830
58831         // private
58832     onClick : function(){
58833         
58834     },
58835
58836     /**
58837      * Sets the checked state of the checkbox.
58838      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
58839      */
58840     setValue : function(v){
58841         this.value = v;
58842         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
58843         // this might be called before we have a dom element..
58844         if (!this.viewEl) {
58845             return;
58846         }
58847         this.viewEl.dom.innerHTML = html;
58848         Roo.form.DisplayField.superclass.setValue.call(this, v);
58849
58850     },
58851     
58852     onClose : function(e)
58853     {
58854         e.preventDefault();
58855         
58856         this.fireEvent('close', this);
58857     }
58858 });/*
58859  * 
58860  * Licence- LGPL
58861  * 
58862  */
58863
58864 /**
58865  * @class Roo.form.DayPicker
58866  * @extends Roo.form.Field
58867  * A Day picker show [M] [T] [W] ....
58868  * @constructor
58869  * Creates a new Day Picker
58870  * @param {Object} config Configuration options
58871  */
58872 Roo.form.DayPicker= function(config){
58873     Roo.form.DayPicker.superclass.constructor.call(this, config);
58874      
58875 };
58876
58877 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
58878     /**
58879      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
58880      */
58881     focusClass : undefined,
58882     /**
58883      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
58884      */
58885     fieldClass: "x-form-field",
58886    
58887     /**
58888      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
58889      * {tag: "input", type: "checkbox", autocomplete: "off"})
58890      */
58891     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
58892     
58893    
58894     actionMode : 'viewEl', 
58895     //
58896     // private
58897  
58898     inputType : 'hidden',
58899     
58900      
58901     inputElement: false, // real input element?
58902     basedOn: false, // ????
58903     
58904     isFormField: true, // not sure where this is needed!!!!
58905
58906     onResize : function(){
58907         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
58908         if(!this.boxLabel){
58909             this.el.alignTo(this.wrap, 'c-c');
58910         }
58911     },
58912
58913     initEvents : function(){
58914         Roo.form.Checkbox.superclass.initEvents.call(this);
58915         this.el.on("click", this.onClick,  this);
58916         this.el.on("change", this.onClick,  this);
58917     },
58918
58919
58920     getResizeEl : function(){
58921         return this.wrap;
58922     },
58923
58924     getPositionEl : function(){
58925         return this.wrap;
58926     },
58927
58928     
58929     // private
58930     onRender : function(ct, position){
58931         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
58932        
58933         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
58934         
58935         var r1 = '<table><tr>';
58936         var r2 = '<tr class="x-form-daypick-icons">';
58937         for (var i=0; i < 7; i++) {
58938             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
58939             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
58940         }
58941         
58942         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
58943         viewEl.select('img').on('click', this.onClick, this);
58944         this.viewEl = viewEl;   
58945         
58946         
58947         // this will not work on Chrome!!!
58948         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
58949         this.el.on('propertychange', this.setFromHidden,  this);  //ie
58950         
58951         
58952           
58953
58954     },
58955
58956     // private
58957     initValue : Roo.emptyFn,
58958
58959     /**
58960      * Returns the checked state of the checkbox.
58961      * @return {Boolean} True if checked, else false
58962      */
58963     getValue : function(){
58964         return this.el.dom.value;
58965         
58966     },
58967
58968         // private
58969     onClick : function(e){ 
58970         //this.setChecked(!this.checked);
58971         Roo.get(e.target).toggleClass('x-menu-item-checked');
58972         this.refreshValue();
58973         //if(this.el.dom.checked != this.checked){
58974         //    this.setValue(this.el.dom.checked);
58975        // }
58976     },
58977     
58978     // private
58979     refreshValue : function()
58980     {
58981         var val = '';
58982         this.viewEl.select('img',true).each(function(e,i,n)  {
58983             val += e.is(".x-menu-item-checked") ? String(n) : '';
58984         });
58985         this.setValue(val, true);
58986     },
58987
58988     /**
58989      * Sets the checked state of the checkbox.
58990      * On is always based on a string comparison between inputValue and the param.
58991      * @param {Boolean/String} value - the value to set 
58992      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
58993      */
58994     setValue : function(v,suppressEvent){
58995         if (!this.el.dom) {
58996             return;
58997         }
58998         var old = this.el.dom.value ;
58999         this.el.dom.value = v;
59000         if (suppressEvent) {
59001             return ;
59002         }
59003          
59004         // update display..
59005         this.viewEl.select('img',true).each(function(e,i,n)  {
59006             
59007             var on = e.is(".x-menu-item-checked");
59008             var newv = v.indexOf(String(n)) > -1;
59009             if (on != newv) {
59010                 e.toggleClass('x-menu-item-checked');
59011             }
59012             
59013         });
59014         
59015         
59016         this.fireEvent('change', this, v, old);
59017         
59018         
59019     },
59020    
59021     // handle setting of hidden value by some other method!!?!?
59022     setFromHidden: function()
59023     {
59024         if(!this.el){
59025             return;
59026         }
59027         //console.log("SET FROM HIDDEN");
59028         //alert('setFrom hidden');
59029         this.setValue(this.el.dom.value);
59030     },
59031     
59032     onDestroy : function()
59033     {
59034         if(this.viewEl){
59035             Roo.get(this.viewEl).remove();
59036         }
59037          
59038         Roo.form.DayPicker.superclass.onDestroy.call(this);
59039     }
59040
59041 });/*
59042  * RooJS Library 1.1.1
59043  * Copyright(c) 2008-2011  Alan Knowles
59044  *
59045  * License - LGPL
59046  */
59047  
59048
59049 /**
59050  * @class Roo.form.ComboCheck
59051  * @extends Roo.form.ComboBox
59052  * A combobox for multiple select items.
59053  *
59054  * FIXME - could do with a reset button..
59055  * 
59056  * @constructor
59057  * Create a new ComboCheck
59058  * @param {Object} config Configuration options
59059  */
59060 Roo.form.ComboCheck = function(config){
59061     Roo.form.ComboCheck.superclass.constructor.call(this, config);
59062     // should verify some data...
59063     // like
59064     // hiddenName = required..
59065     // displayField = required
59066     // valudField == required
59067     var req= [ 'hiddenName', 'displayField', 'valueField' ];
59068     var _t = this;
59069     Roo.each(req, function(e) {
59070         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
59071             throw "Roo.form.ComboCheck : missing value for: " + e;
59072         }
59073     });
59074     
59075     
59076 };
59077
59078 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
59079      
59080      
59081     editable : false,
59082      
59083     selectedClass: 'x-menu-item-checked', 
59084     
59085     // private
59086     onRender : function(ct, position){
59087         var _t = this;
59088         
59089         
59090         
59091         if(!this.tpl){
59092             var cls = 'x-combo-list';
59093
59094             
59095             this.tpl =  new Roo.Template({
59096                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
59097                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
59098                    '<span>{' + this.displayField + '}</span>' +
59099                     '</div>' 
59100                 
59101             });
59102         }
59103  
59104         
59105         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
59106         this.view.singleSelect = false;
59107         this.view.multiSelect = true;
59108         this.view.toggleSelect = true;
59109         this.pageTb.add(new Roo.Toolbar.Fill(),{
59110             
59111             text: 'Select All',
59112             handler: function() {
59113                 _t.selectAll();
59114             }
59115         },
59116         {
59117             text: 'Done',
59118             handler: function() {
59119                 _t.collapse();
59120             }
59121         });
59122     },
59123     
59124     cleanLeadingSpace : function(e)
59125     {
59126         // this is disabled, as it retriggers setvalue on blur
59127         return;
59128     },
59129     doForce : function() {
59130         // no idea what this did, but it blanks out our values.
59131         return;
59132     },
59133     onViewOver : function(e, t){
59134         // do nothing...
59135         return;
59136         
59137     },
59138     
59139     onViewClick : function(doFocus,index){
59140         return;
59141         
59142     },
59143     select: function () {
59144         //Roo.log("SELECT CALLED");
59145     },
59146      
59147     selectByValue : function(xv, scrollIntoView){
59148         var ar = this.getValueArray();
59149         var sels = [];
59150         
59151         Roo.each(ar, function(v) {
59152             if(v === undefined || v === null){
59153                 return;
59154             }
59155             var r = this.findRecord(this.valueField, v);
59156             if(r){
59157                 sels.push(this.store.indexOf(r))
59158                 
59159             }
59160         },this);
59161         this.view.select(sels);
59162         return false;
59163     },
59164     
59165     selectAll : function()
59166     {
59167         var sels = [];
59168         this.store.each(function(r,i) {
59169             sels.push(i);
59170         });
59171         this.view.select(sels);
59172         this.collapse();
59173         return false;
59174
59175     },
59176     
59177     onSelect : function(record, index){
59178        // Roo.log("onselect Called");
59179        // this is only called by the clear button now..
59180         this.view.clearSelections();
59181         this.setValue('[]');
59182         if (this.value != this.valueBefore) {
59183             this.fireEvent('change', this, this.value, this.valueBefore);
59184             this.valueBefore = this.value;
59185         }
59186     },
59187     getValueArray : function()
59188     {
59189         var ar = [] ;
59190         
59191         try {
59192             //Roo.log(this.value);
59193             if (typeof(this.value) == 'undefined') {
59194                 return [];
59195             }
59196             var ar = Roo.decode(this.value);
59197             return  ar instanceof Array ? ar : []; //?? valid?
59198             
59199         } catch(e) {
59200             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
59201             return [];
59202         }
59203          
59204     },
59205     expand : function ()
59206     {
59207         
59208         Roo.form.ComboCheck.superclass.expand.call(this);
59209         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
59210         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
59211         
59212
59213     },
59214     
59215     collapse : function(){
59216         Roo.form.ComboCheck.superclass.collapse.call(this);
59217         var sl = this.view.getSelectedIndexes();
59218         var st = this.store;
59219         var nv = [];
59220         var tv = [];
59221         var r;
59222         Roo.each(sl, function(i) {
59223             r = st.getAt(i);
59224             nv.push(r.get(this.valueField));
59225         },this);
59226         this.setValue(Roo.encode(nv));
59227         if (this.value != this.valueBefore) {
59228
59229             this.fireEvent('change', this, this.value, this.valueBefore);
59230             this.valueBefore = this.value;
59231         }
59232         
59233     },
59234     
59235     setValue : function(v){
59236         // Roo.log(v);
59237         this.value = v;
59238         
59239         var vals = this.getValueArray();
59240         var tv = [];
59241         Roo.each(vals, function(k) {
59242             var r = this.findRecord(this.valueField, k);
59243             if(r){
59244                 tv.push(r.data[this.displayField]);
59245             }else if(this.valueNotFoundText !== undefined){
59246                 tv.push( this.valueNotFoundText );
59247             }
59248         },this);
59249        // Roo.log(tv);
59250         
59251         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
59252         this.hiddenField.value = v;
59253         this.value = v;
59254     }
59255     
59256 });/*
59257  * Based on:
59258  * Ext JS Library 1.1.1
59259  * Copyright(c) 2006-2007, Ext JS, LLC.
59260  *
59261  * Originally Released Under LGPL - original licence link has changed is not relivant.
59262  *
59263  * Fork - LGPL
59264  * <script type="text/javascript">
59265  */
59266  
59267 /**
59268  * @class Roo.form.Signature
59269  * @extends Roo.form.Field
59270  * Signature field.  
59271  * @constructor
59272  * 
59273  * @param {Object} config Configuration options
59274  */
59275
59276 Roo.form.Signature = function(config){
59277     Roo.form.Signature.superclass.constructor.call(this, config);
59278     
59279     this.addEvents({// not in used??
59280          /**
59281          * @event confirm
59282          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
59283              * @param {Roo.form.Signature} combo This combo box
59284              */
59285         'confirm' : true,
59286         /**
59287          * @event reset
59288          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
59289              * @param {Roo.form.ComboBox} combo This combo box
59290              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
59291              */
59292         'reset' : true
59293     });
59294 };
59295
59296 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
59297     /**
59298      * @cfg {Object} labels Label to use when rendering a form.
59299      * defaults to 
59300      * labels : { 
59301      *      clear : "Clear",
59302      *      confirm : "Confirm"
59303      *  }
59304      */
59305     labels : { 
59306         clear : "Clear",
59307         confirm : "Confirm"
59308     },
59309     /**
59310      * @cfg {Number} width The signature panel width (defaults to 300)
59311      */
59312     width: 300,
59313     /**
59314      * @cfg {Number} height The signature panel height (defaults to 100)
59315      */
59316     height : 100,
59317     /**
59318      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
59319      */
59320     allowBlank : false,
59321     
59322     //private
59323     // {Object} signPanel The signature SVG panel element (defaults to {})
59324     signPanel : {},
59325     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
59326     isMouseDown : false,
59327     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
59328     isConfirmed : false,
59329     // {String} signatureTmp SVG mapping string (defaults to empty string)
59330     signatureTmp : '',
59331     
59332     
59333     defaultAutoCreate : { // modified by initCompnoent..
59334         tag: "input",
59335         type:"hidden"
59336     },
59337
59338     // private
59339     onRender : function(ct, position){
59340         
59341         Roo.form.Signature.superclass.onRender.call(this, ct, position);
59342         
59343         this.wrap = this.el.wrap({
59344             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
59345         });
59346         
59347         this.createToolbar(this);
59348         this.signPanel = this.wrap.createChild({
59349                 tag: 'div',
59350                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
59351             }, this.el
59352         );
59353             
59354         this.svgID = Roo.id();
59355         this.svgEl = this.signPanel.createChild({
59356               xmlns : 'http://www.w3.org/2000/svg',
59357               tag : 'svg',
59358               id : this.svgID + "-svg",
59359               width: this.width,
59360               height: this.height,
59361               viewBox: '0 0 '+this.width+' '+this.height,
59362               cn : [
59363                 {
59364                     tag: "rect",
59365                     id: this.svgID + "-svg-r",
59366                     width: this.width,
59367                     height: this.height,
59368                     fill: "#ffa"
59369                 },
59370                 {
59371                     tag: "line",
59372                     id: this.svgID + "-svg-l",
59373                     x1: "0", // start
59374                     y1: (this.height*0.8), // start set the line in 80% of height
59375                     x2: this.width, // end
59376                     y2: (this.height*0.8), // end set the line in 80% of height
59377                     'stroke': "#666",
59378                     'stroke-width': "1",
59379                     'stroke-dasharray': "3",
59380                     'shape-rendering': "crispEdges",
59381                     'pointer-events': "none"
59382                 },
59383                 {
59384                     tag: "path",
59385                     id: this.svgID + "-svg-p",
59386                     'stroke': "navy",
59387                     'stroke-width': "3",
59388                     'fill': "none",
59389                     'pointer-events': 'none'
59390                 }
59391               ]
59392         });
59393         this.createSVG();
59394         this.svgBox = this.svgEl.dom.getScreenCTM();
59395     },
59396     createSVG : function(){ 
59397         var svg = this.signPanel;
59398         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
59399         var t = this;
59400
59401         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
59402         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
59403         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
59404         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
59405         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
59406         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
59407         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
59408         
59409     },
59410     isTouchEvent : function(e){
59411         return e.type.match(/^touch/);
59412     },
59413     getCoords : function (e) {
59414         var pt    = this.svgEl.dom.createSVGPoint();
59415         pt.x = e.clientX; 
59416         pt.y = e.clientY;
59417         if (this.isTouchEvent(e)) {
59418             pt.x =  e.targetTouches[0].clientX;
59419             pt.y = e.targetTouches[0].clientY;
59420         }
59421         var a = this.svgEl.dom.getScreenCTM();
59422         var b = a.inverse();
59423         var mx = pt.matrixTransform(b);
59424         return mx.x + ',' + mx.y;
59425     },
59426     //mouse event headler 
59427     down : function (e) {
59428         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
59429         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
59430         
59431         this.isMouseDown = true;
59432         
59433         e.preventDefault();
59434     },
59435     move : function (e) {
59436         if (this.isMouseDown) {
59437             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
59438             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
59439         }
59440         
59441         e.preventDefault();
59442     },
59443     up : function (e) {
59444         this.isMouseDown = false;
59445         var sp = this.signatureTmp.split(' ');
59446         
59447         if(sp.length > 1){
59448             if(!sp[sp.length-2].match(/^L/)){
59449                 sp.pop();
59450                 sp.pop();
59451                 sp.push("");
59452                 this.signatureTmp = sp.join(" ");
59453             }
59454         }
59455         if(this.getValue() != this.signatureTmp){
59456             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
59457             this.isConfirmed = false;
59458         }
59459         e.preventDefault();
59460     },
59461     
59462     /**
59463      * Protected method that will not generally be called directly. It
59464      * is called when the editor creates its toolbar. Override this method if you need to
59465      * add custom toolbar buttons.
59466      * @param {HtmlEditor} editor
59467      */
59468     createToolbar : function(editor){
59469          function btn(id, toggle, handler){
59470             var xid = fid + '-'+ id ;
59471             return {
59472                 id : xid,
59473                 cmd : id,
59474                 cls : 'x-btn-icon x-edit-'+id,
59475                 enableToggle:toggle !== false,
59476                 scope: editor, // was editor...
59477                 handler:handler||editor.relayBtnCmd,
59478                 clickEvent:'mousedown',
59479                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
59480                 tabIndex:-1
59481             };
59482         }
59483         
59484         
59485         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
59486         this.tb = tb;
59487         this.tb.add(
59488            {
59489                 cls : ' x-signature-btn x-signature-'+id,
59490                 scope: editor, // was editor...
59491                 handler: this.reset,
59492                 clickEvent:'mousedown',
59493                 text: this.labels.clear
59494             },
59495             {
59496                  xtype : 'Fill',
59497                  xns: Roo.Toolbar
59498             }, 
59499             {
59500                 cls : '  x-signature-btn x-signature-'+id,
59501                 scope: editor, // was editor...
59502                 handler: this.confirmHandler,
59503                 clickEvent:'mousedown',
59504                 text: this.labels.confirm
59505             }
59506         );
59507     
59508     },
59509     //public
59510     /**
59511      * when user is clicked confirm then show this image.....
59512      * 
59513      * @return {String} Image Data URI
59514      */
59515     getImageDataURI : function(){
59516         var svg = this.svgEl.dom.parentNode.innerHTML;
59517         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
59518         return src; 
59519     },
59520     /**
59521      * 
59522      * @return {Boolean} this.isConfirmed
59523      */
59524     getConfirmed : function(){
59525         return this.isConfirmed;
59526     },
59527     /**
59528      * 
59529      * @return {Number} this.width
59530      */
59531     getWidth : function(){
59532         return this.width;
59533     },
59534     /**
59535      * 
59536      * @return {Number} this.height
59537      */
59538     getHeight : function(){
59539         return this.height;
59540     },
59541     // private
59542     getSignature : function(){
59543         return this.signatureTmp;
59544     },
59545     // private
59546     reset : function(){
59547         this.signatureTmp = '';
59548         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
59549         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
59550         this.isConfirmed = false;
59551         Roo.form.Signature.superclass.reset.call(this);
59552     },
59553     setSignature : function(s){
59554         this.signatureTmp = s;
59555         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
59556         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
59557         this.setValue(s);
59558         this.isConfirmed = false;
59559         Roo.form.Signature.superclass.reset.call(this);
59560     }, 
59561     test : function(){
59562 //        Roo.log(this.signPanel.dom.contentWindow.up())
59563     },
59564     //private
59565     setConfirmed : function(){
59566         
59567         
59568         
59569 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
59570     },
59571     // private
59572     confirmHandler : function(){
59573         if(!this.getSignature()){
59574             return;
59575         }
59576         
59577         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
59578         this.setValue(this.getSignature());
59579         this.isConfirmed = true;
59580         
59581         this.fireEvent('confirm', this);
59582     },
59583     // private
59584     // Subclasses should provide the validation implementation by overriding this
59585     validateValue : function(value){
59586         if(this.allowBlank){
59587             return true;
59588         }
59589         
59590         if(this.isConfirmed){
59591             return true;
59592         }
59593         return false;
59594     }
59595 });/*
59596  * Based on:
59597  * Ext JS Library 1.1.1
59598  * Copyright(c) 2006-2007, Ext JS, LLC.
59599  *
59600  * Originally Released Under LGPL - original licence link has changed is not relivant.
59601  *
59602  * Fork - LGPL
59603  * <script type="text/javascript">
59604  */
59605  
59606
59607 /**
59608  * @class Roo.form.ComboBox
59609  * @extends Roo.form.TriggerField
59610  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
59611  * @constructor
59612  * Create a new ComboBox.
59613  * @param {Object} config Configuration options
59614  */
59615 Roo.form.Select = function(config){
59616     Roo.form.Select.superclass.constructor.call(this, config);
59617      
59618 };
59619
59620 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
59621     /**
59622      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
59623      */
59624     /**
59625      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
59626      * rendering into an Roo.Editor, defaults to false)
59627      */
59628     /**
59629      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
59630      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
59631      */
59632     /**
59633      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
59634      */
59635     /**
59636      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
59637      * the dropdown list (defaults to undefined, with no header element)
59638      */
59639
59640      /**
59641      * @cfg {String/Roo.Template} tpl The template to use to render the output
59642      */
59643      
59644     // private
59645     defaultAutoCreate : {tag: "select"  },
59646     /**
59647      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
59648      */
59649     listWidth: undefined,
59650     /**
59651      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
59652      * mode = 'remote' or 'text' if mode = 'local')
59653      */
59654     displayField: undefined,
59655     /**
59656      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
59657      * mode = 'remote' or 'value' if mode = 'local'). 
59658      * Note: use of a valueField requires the user make a selection
59659      * in order for a value to be mapped.
59660      */
59661     valueField: undefined,
59662     
59663     
59664     /**
59665      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
59666      * field's data value (defaults to the underlying DOM element's name)
59667      */
59668     hiddenName: undefined,
59669     /**
59670      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
59671      */
59672     listClass: '',
59673     /**
59674      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
59675      */
59676     selectedClass: 'x-combo-selected',
59677     /**
59678      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
59679      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
59680      * which displays a downward arrow icon).
59681      */
59682     triggerClass : 'x-form-arrow-trigger',
59683     /**
59684      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
59685      */
59686     shadow:'sides',
59687     /**
59688      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
59689      * anchor positions (defaults to 'tl-bl')
59690      */
59691     listAlign: 'tl-bl?',
59692     /**
59693      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
59694      */
59695     maxHeight: 300,
59696     /**
59697      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
59698      * query specified by the allQuery config option (defaults to 'query')
59699      */
59700     triggerAction: 'query',
59701     /**
59702      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
59703      * (defaults to 4, does not apply if editable = false)
59704      */
59705     minChars : 4,
59706     /**
59707      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
59708      * delay (typeAheadDelay) if it matches a known value (defaults to false)
59709      */
59710     typeAhead: false,
59711     /**
59712      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
59713      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
59714      */
59715     queryDelay: 500,
59716     /**
59717      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
59718      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
59719      */
59720     pageSize: 0,
59721     /**
59722      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
59723      * when editable = true (defaults to false)
59724      */
59725     selectOnFocus:false,
59726     /**
59727      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
59728      */
59729     queryParam: 'query',
59730     /**
59731      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
59732      * when mode = 'remote' (defaults to 'Loading...')
59733      */
59734     loadingText: 'Loading...',
59735     /**
59736      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
59737      */
59738     resizable: false,
59739     /**
59740      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
59741      */
59742     handleHeight : 8,
59743     /**
59744      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
59745      * traditional select (defaults to true)
59746      */
59747     editable: true,
59748     /**
59749      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
59750      */
59751     allQuery: '',
59752     /**
59753      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
59754      */
59755     mode: 'remote',
59756     /**
59757      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
59758      * listWidth has a higher value)
59759      */
59760     minListWidth : 70,
59761     /**
59762      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
59763      * allow the user to set arbitrary text into the field (defaults to false)
59764      */
59765     forceSelection:false,
59766     /**
59767      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
59768      * if typeAhead = true (defaults to 250)
59769      */
59770     typeAheadDelay : 250,
59771     /**
59772      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
59773      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
59774      */
59775     valueNotFoundText : undefined,
59776     
59777     /**
59778      * @cfg {String} defaultValue The value displayed after loading the store.
59779      */
59780     defaultValue: '',
59781     
59782     /**
59783      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
59784      */
59785     blockFocus : false,
59786     
59787     /**
59788      * @cfg {Boolean} disableClear Disable showing of clear button.
59789      */
59790     disableClear : false,
59791     /**
59792      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
59793      */
59794     alwaysQuery : false,
59795     
59796     //private
59797     addicon : false,
59798     editicon: false,
59799     
59800     // element that contains real text value.. (when hidden is used..)
59801      
59802     // private
59803     onRender : function(ct, position){
59804         Roo.form.Field.prototype.onRender.call(this, ct, position);
59805         
59806         if(this.store){
59807             this.store.on('beforeload', this.onBeforeLoad, this);
59808             this.store.on('load', this.onLoad, this);
59809             this.store.on('loadexception', this.onLoadException, this);
59810             this.store.load({});
59811         }
59812         
59813         
59814         
59815     },
59816
59817     // private
59818     initEvents : function(){
59819         //Roo.form.ComboBox.superclass.initEvents.call(this);
59820  
59821     },
59822
59823     onDestroy : function(){
59824        
59825         if(this.store){
59826             this.store.un('beforeload', this.onBeforeLoad, this);
59827             this.store.un('load', this.onLoad, this);
59828             this.store.un('loadexception', this.onLoadException, this);
59829         }
59830         //Roo.form.ComboBox.superclass.onDestroy.call(this);
59831     },
59832
59833     // private
59834     fireKey : function(e){
59835         if(e.isNavKeyPress() && !this.list.isVisible()){
59836             this.fireEvent("specialkey", this, e);
59837         }
59838     },
59839
59840     // private
59841     onResize: function(w, h){
59842         
59843         return; 
59844     
59845         
59846     },
59847
59848     /**
59849      * Allow or prevent the user from directly editing the field text.  If false is passed,
59850      * the user will only be able to select from the items defined in the dropdown list.  This method
59851      * is the runtime equivalent of setting the 'editable' config option at config time.
59852      * @param {Boolean} value True to allow the user to directly edit the field text
59853      */
59854     setEditable : function(value){
59855          
59856     },
59857
59858     // private
59859     onBeforeLoad : function(){
59860         
59861         Roo.log("Select before load");
59862         return;
59863     
59864         this.innerList.update(this.loadingText ?
59865                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
59866         //this.restrictHeight();
59867         this.selectedIndex = -1;
59868     },
59869
59870     // private
59871     onLoad : function(){
59872
59873     
59874         var dom = this.el.dom;
59875         dom.innerHTML = '';
59876          var od = dom.ownerDocument;
59877          
59878         if (this.emptyText) {
59879             var op = od.createElement('option');
59880             op.setAttribute('value', '');
59881             op.innerHTML = String.format('{0}', this.emptyText);
59882             dom.appendChild(op);
59883         }
59884         if(this.store.getCount() > 0){
59885            
59886             var vf = this.valueField;
59887             var df = this.displayField;
59888             this.store.data.each(function(r) {
59889                 // which colmsn to use... testing - cdoe / title..
59890                 var op = od.createElement('option');
59891                 op.setAttribute('value', r.data[vf]);
59892                 op.innerHTML = String.format('{0}', r.data[df]);
59893                 dom.appendChild(op);
59894             });
59895             if (typeof(this.defaultValue != 'undefined')) {
59896                 this.setValue(this.defaultValue);
59897             }
59898             
59899              
59900         }else{
59901             //this.onEmptyResults();
59902         }
59903         //this.el.focus();
59904     },
59905     // private
59906     onLoadException : function()
59907     {
59908         dom.innerHTML = '';
59909             
59910         Roo.log("Select on load exception");
59911         return;
59912     
59913         this.collapse();
59914         Roo.log(this.store.reader.jsonData);
59915         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
59916             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
59917         }
59918         
59919         
59920     },
59921     // private
59922     onTypeAhead : function(){
59923          
59924     },
59925
59926     // private
59927     onSelect : function(record, index){
59928         Roo.log('on select?');
59929         return;
59930         if(this.fireEvent('beforeselect', this, record, index) !== false){
59931             this.setFromData(index > -1 ? record.data : false);
59932             this.collapse();
59933             this.fireEvent('select', this, record, index);
59934         }
59935     },
59936
59937     /**
59938      * Returns the currently selected field value or empty string if no value is set.
59939      * @return {String} value The selected value
59940      */
59941     getValue : function(){
59942         var dom = this.el.dom;
59943         this.value = dom.options[dom.selectedIndex].value;
59944         return this.value;
59945         
59946     },
59947
59948     /**
59949      * Clears any text/value currently set in the field
59950      */
59951     clearValue : function(){
59952         this.value = '';
59953         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
59954         
59955     },
59956
59957     /**
59958      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
59959      * will be displayed in the field.  If the value does not match the data value of an existing item,
59960      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
59961      * Otherwise the field will be blank (although the value will still be set).
59962      * @param {String} value The value to match
59963      */
59964     setValue : function(v){
59965         var d = this.el.dom;
59966         for (var i =0; i < d.options.length;i++) {
59967             if (v == d.options[i].value) {
59968                 d.selectedIndex = i;
59969                 this.value = v;
59970                 return;
59971             }
59972         }
59973         this.clearValue();
59974     },
59975     /**
59976      * @property {Object} the last set data for the element
59977      */
59978     
59979     lastData : false,
59980     /**
59981      * Sets the value of the field based on a object which is related to the record format for the store.
59982      * @param {Object} value the value to set as. or false on reset?
59983      */
59984     setFromData : function(o){
59985         Roo.log('setfrom data?');
59986          
59987         
59988         
59989     },
59990     // private
59991     reset : function(){
59992         this.clearValue();
59993     },
59994     // private
59995     findRecord : function(prop, value){
59996         
59997         return false;
59998     
59999         var record;
60000         if(this.store.getCount() > 0){
60001             this.store.each(function(r){
60002                 if(r.data[prop] == value){
60003                     record = r;
60004                     return false;
60005                 }
60006                 return true;
60007             });
60008         }
60009         return record;
60010     },
60011     
60012     getName: function()
60013     {
60014         // returns hidden if it's set..
60015         if (!this.rendered) {return ''};
60016         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
60017         
60018     },
60019      
60020
60021     
60022
60023     // private
60024     onEmptyResults : function(){
60025         Roo.log('empty results');
60026         //this.collapse();
60027     },
60028
60029     /**
60030      * Returns true if the dropdown list is expanded, else false.
60031      */
60032     isExpanded : function(){
60033         return false;
60034     },
60035
60036     /**
60037      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
60038      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
60039      * @param {String} value The data value of the item to select
60040      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
60041      * selected item if it is not currently in view (defaults to true)
60042      * @return {Boolean} True if the value matched an item in the list, else false
60043      */
60044     selectByValue : function(v, scrollIntoView){
60045         Roo.log('select By Value');
60046         return false;
60047     
60048         if(v !== undefined && v !== null){
60049             var r = this.findRecord(this.valueField || this.displayField, v);
60050             if(r){
60051                 this.select(this.store.indexOf(r), scrollIntoView);
60052                 return true;
60053             }
60054         }
60055         return false;
60056     },
60057
60058     /**
60059      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
60060      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
60061      * @param {Number} index The zero-based index of the list item to select
60062      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
60063      * selected item if it is not currently in view (defaults to true)
60064      */
60065     select : function(index, scrollIntoView){
60066         Roo.log('select ');
60067         return  ;
60068         
60069         this.selectedIndex = index;
60070         this.view.select(index);
60071         if(scrollIntoView !== false){
60072             var el = this.view.getNode(index);
60073             if(el){
60074                 this.innerList.scrollChildIntoView(el, false);
60075             }
60076         }
60077     },
60078
60079       
60080
60081     // private
60082     validateBlur : function(){
60083         
60084         return;
60085         
60086     },
60087
60088     // private
60089     initQuery : function(){
60090         this.doQuery(this.getRawValue());
60091     },
60092
60093     // private
60094     doForce : function(){
60095         if(this.el.dom.value.length > 0){
60096             this.el.dom.value =
60097                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
60098              
60099         }
60100     },
60101
60102     /**
60103      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
60104      * query allowing the query action to be canceled if needed.
60105      * @param {String} query The SQL query to execute
60106      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
60107      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
60108      * saved in the current store (defaults to false)
60109      */
60110     doQuery : function(q, forceAll){
60111         
60112         Roo.log('doQuery?');
60113         if(q === undefined || q === null){
60114             q = '';
60115         }
60116         var qe = {
60117             query: q,
60118             forceAll: forceAll,
60119             combo: this,
60120             cancel:false
60121         };
60122         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
60123             return false;
60124         }
60125         q = qe.query;
60126         forceAll = qe.forceAll;
60127         if(forceAll === true || (q.length >= this.minChars)){
60128             if(this.lastQuery != q || this.alwaysQuery){
60129                 this.lastQuery = q;
60130                 if(this.mode == 'local'){
60131                     this.selectedIndex = -1;
60132                     if(forceAll){
60133                         this.store.clearFilter();
60134                     }else{
60135                         this.store.filter(this.displayField, q);
60136                     }
60137                     this.onLoad();
60138                 }else{
60139                     this.store.baseParams[this.queryParam] = q;
60140                     this.store.load({
60141                         params: this.getParams(q)
60142                     });
60143                     this.expand();
60144                 }
60145             }else{
60146                 this.selectedIndex = -1;
60147                 this.onLoad();   
60148             }
60149         }
60150     },
60151
60152     // private
60153     getParams : function(q){
60154         var p = {};
60155         //p[this.queryParam] = q;
60156         if(this.pageSize){
60157             p.start = 0;
60158             p.limit = this.pageSize;
60159         }
60160         return p;
60161     },
60162
60163     /**
60164      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
60165      */
60166     collapse : function(){
60167         
60168     },
60169
60170     // private
60171     collapseIf : function(e){
60172         
60173     },
60174
60175     /**
60176      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
60177      */
60178     expand : function(){
60179         
60180     } ,
60181
60182     // private
60183      
60184
60185     /** 
60186     * @cfg {Boolean} grow 
60187     * @hide 
60188     */
60189     /** 
60190     * @cfg {Number} growMin 
60191     * @hide 
60192     */
60193     /** 
60194     * @cfg {Number} growMax 
60195     * @hide 
60196     */
60197     /**
60198      * @hide
60199      * @method autoSize
60200      */
60201     
60202     setWidth : function()
60203     {
60204         
60205     },
60206     getResizeEl : function(){
60207         return this.el;
60208     }
60209 });//<script type="text/javasscript">
60210  
60211
60212 /**
60213  * @class Roo.DDView
60214  * A DnD enabled version of Roo.View.
60215  * @param {Element/String} container The Element in which to create the View.
60216  * @param {String} tpl The template string used to create the markup for each element of the View
60217  * @param {Object} config The configuration properties. These include all the config options of
60218  * {@link Roo.View} plus some specific to this class.<br>
60219  * <p>
60220  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
60221  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
60222  * <p>
60223  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
60224 .x-view-drag-insert-above {
60225         border-top:1px dotted #3366cc;
60226 }
60227 .x-view-drag-insert-below {
60228         border-bottom:1px dotted #3366cc;
60229 }
60230 </code></pre>
60231  * 
60232  */
60233  
60234 Roo.DDView = function(container, tpl, config) {
60235     Roo.DDView.superclass.constructor.apply(this, arguments);
60236     this.getEl().setStyle("outline", "0px none");
60237     this.getEl().unselectable();
60238     if (this.dragGroup) {
60239         this.setDraggable(this.dragGroup.split(","));
60240     }
60241     if (this.dropGroup) {
60242         this.setDroppable(this.dropGroup.split(","));
60243     }
60244     if (this.deletable) {
60245         this.setDeletable();
60246     }
60247     this.isDirtyFlag = false;
60248         this.addEvents({
60249                 "drop" : true
60250         });
60251 };
60252
60253 Roo.extend(Roo.DDView, Roo.View, {
60254 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
60255 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
60256 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
60257 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
60258
60259         isFormField: true,
60260
60261         reset: Roo.emptyFn,
60262         
60263         clearInvalid: Roo.form.Field.prototype.clearInvalid,
60264
60265         validate: function() {
60266                 return true;
60267         },
60268         
60269         destroy: function() {
60270                 this.purgeListeners();
60271                 this.getEl.removeAllListeners();
60272                 this.getEl().remove();
60273                 if (this.dragZone) {
60274                         if (this.dragZone.destroy) {
60275                                 this.dragZone.destroy();
60276                         }
60277                 }
60278                 if (this.dropZone) {
60279                         if (this.dropZone.destroy) {
60280                                 this.dropZone.destroy();
60281                         }
60282                 }
60283         },
60284
60285 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
60286         getName: function() {
60287                 return this.name;
60288         },
60289
60290 /**     Loads the View from a JSON string representing the Records to put into the Store. */
60291         setValue: function(v) {
60292                 if (!this.store) {
60293                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
60294                 }
60295                 var data = {};
60296                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
60297                 this.store.proxy = new Roo.data.MemoryProxy(data);
60298                 this.store.load();
60299         },
60300
60301 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
60302         getValue: function() {
60303                 var result = '(';
60304                 this.store.each(function(rec) {
60305                         result += rec.id + ',';
60306                 });
60307                 return result.substr(0, result.length - 1) + ')';
60308         },
60309         
60310         getIds: function() {
60311                 var i = 0, result = new Array(this.store.getCount());
60312                 this.store.each(function(rec) {
60313                         result[i++] = rec.id;
60314                 });
60315                 return result;
60316         },
60317         
60318         isDirty: function() {
60319                 return this.isDirtyFlag;
60320         },
60321
60322 /**
60323  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
60324  *      whole Element becomes the target, and this causes the drop gesture to append.
60325  */
60326     getTargetFromEvent : function(e) {
60327                 var target = e.getTarget();
60328                 while ((target !== null) && (target.parentNode != this.el.dom)) {
60329                 target = target.parentNode;
60330                 }
60331                 if (!target) {
60332                         target = this.el.dom.lastChild || this.el.dom;
60333                 }
60334                 return target;
60335     },
60336
60337 /**
60338  *      Create the drag data which consists of an object which has the property "ddel" as
60339  *      the drag proxy element. 
60340  */
60341     getDragData : function(e) {
60342         var target = this.findItemFromChild(e.getTarget());
60343                 if(target) {
60344                         this.handleSelection(e);
60345                         var selNodes = this.getSelectedNodes();
60346             var dragData = {
60347                 source: this,
60348                 copy: this.copy || (this.allowCopy && e.ctrlKey),
60349                 nodes: selNodes,
60350                 records: []
60351                         };
60352                         var selectedIndices = this.getSelectedIndexes();
60353                         for (var i = 0; i < selectedIndices.length; i++) {
60354                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
60355                         }
60356                         if (selNodes.length == 1) {
60357                                 dragData.ddel = target.cloneNode(true); // the div element
60358                         } else {
60359                                 var div = document.createElement('div'); // create the multi element drag "ghost"
60360                                 div.className = 'multi-proxy';
60361                                 for (var i = 0, len = selNodes.length; i < len; i++) {
60362                                         div.appendChild(selNodes[i].cloneNode(true));
60363                                 }
60364                                 dragData.ddel = div;
60365                         }
60366             //console.log(dragData)
60367             //console.log(dragData.ddel.innerHTML)
60368                         return dragData;
60369                 }
60370         //console.log('nodragData')
60371                 return false;
60372     },
60373     
60374 /**     Specify to which ddGroup items in this DDView may be dragged. */
60375     setDraggable: function(ddGroup) {
60376         if (ddGroup instanceof Array) {
60377                 Roo.each(ddGroup, this.setDraggable, this);
60378                 return;
60379         }
60380         if (this.dragZone) {
60381                 this.dragZone.addToGroup(ddGroup);
60382         } else {
60383                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
60384                                 containerScroll: true,
60385                                 ddGroup: ddGroup 
60386
60387                         });
60388 //                      Draggability implies selection. DragZone's mousedown selects the element.
60389                         if (!this.multiSelect) { this.singleSelect = true; }
60390
60391 //                      Wire the DragZone's handlers up to methods in *this*
60392                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
60393                 }
60394     },
60395
60396 /**     Specify from which ddGroup this DDView accepts drops. */
60397     setDroppable: function(ddGroup) {
60398         if (ddGroup instanceof Array) {
60399                 Roo.each(ddGroup, this.setDroppable, this);
60400                 return;
60401         }
60402         if (this.dropZone) {
60403                 this.dropZone.addToGroup(ddGroup);
60404         } else {
60405                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
60406                                 containerScroll: true,
60407                                 ddGroup: ddGroup
60408                         });
60409
60410 //                      Wire the DropZone's handlers up to methods in *this*
60411                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
60412                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
60413                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
60414                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
60415                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
60416                 }
60417     },
60418
60419 /**     Decide whether to drop above or below a View node. */
60420     getDropPoint : function(e, n, dd){
60421         if (n == this.el.dom) { return "above"; }
60422                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
60423                 var c = t + (b - t) / 2;
60424                 var y = Roo.lib.Event.getPageY(e);
60425                 if(y <= c) {
60426                         return "above";
60427                 }else{
60428                         return "below";
60429                 }
60430     },
60431
60432     onNodeEnter : function(n, dd, e, data){
60433                 return false;
60434     },
60435     
60436     onNodeOver : function(n, dd, e, data){
60437                 var pt = this.getDropPoint(e, n, dd);
60438                 // set the insert point style on the target node
60439                 var dragElClass = this.dropNotAllowed;
60440                 if (pt) {
60441                         var targetElClass;
60442                         if (pt == "above"){
60443                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
60444                                 targetElClass = "x-view-drag-insert-above";
60445                         } else {
60446                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
60447                                 targetElClass = "x-view-drag-insert-below";
60448                         }
60449                         if (this.lastInsertClass != targetElClass){
60450                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
60451                                 this.lastInsertClass = targetElClass;
60452                         }
60453                 }
60454                 return dragElClass;
60455         },
60456
60457     onNodeOut : function(n, dd, e, data){
60458                 this.removeDropIndicators(n);
60459     },
60460
60461     onNodeDrop : function(n, dd, e, data){
60462         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
60463                 return false;
60464         }
60465         var pt = this.getDropPoint(e, n, dd);
60466                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
60467                 if (pt == "below") { insertAt++; }
60468                 for (var i = 0; i < data.records.length; i++) {
60469                         var r = data.records[i];
60470                         var dup = this.store.getById(r.id);
60471                         if (dup && (dd != this.dragZone)) {
60472                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
60473                         } else {
60474                                 if (data.copy) {
60475                                         this.store.insert(insertAt++, r.copy());
60476                                 } else {
60477                                         data.source.isDirtyFlag = true;
60478                                         r.store.remove(r);
60479                                         this.store.insert(insertAt++, r);
60480                                 }
60481                                 this.isDirtyFlag = true;
60482                         }
60483                 }
60484                 this.dragZone.cachedTarget = null;
60485                 return true;
60486     },
60487
60488     removeDropIndicators : function(n){
60489                 if(n){
60490                         Roo.fly(n).removeClass([
60491                                 "x-view-drag-insert-above",
60492                                 "x-view-drag-insert-below"]);
60493                         this.lastInsertClass = "_noclass";
60494                 }
60495     },
60496
60497 /**
60498  *      Utility method. Add a delete option to the DDView's context menu.
60499  *      @param {String} imageUrl The URL of the "delete" icon image.
60500  */
60501         setDeletable: function(imageUrl) {
60502                 if (!this.singleSelect && !this.multiSelect) {
60503                         this.singleSelect = true;
60504                 }
60505                 var c = this.getContextMenu();
60506                 this.contextMenu.on("itemclick", function(item) {
60507                         switch (item.id) {
60508                                 case "delete":
60509                                         this.remove(this.getSelectedIndexes());
60510                                         break;
60511                         }
60512                 }, this);
60513                 this.contextMenu.add({
60514                         icon: imageUrl,
60515                         id: "delete",
60516                         text: 'Delete'
60517                 });
60518         },
60519         
60520 /**     Return the context menu for this DDView. */
60521         getContextMenu: function() {
60522                 if (!this.contextMenu) {
60523 //                      Create the View's context menu
60524                         this.contextMenu = new Roo.menu.Menu({
60525                                 id: this.id + "-contextmenu"
60526                         });
60527                         this.el.on("contextmenu", this.showContextMenu, this);
60528                 }
60529                 return this.contextMenu;
60530         },
60531         
60532         disableContextMenu: function() {
60533                 if (this.contextMenu) {
60534                         this.el.un("contextmenu", this.showContextMenu, this);
60535                 }
60536         },
60537
60538         showContextMenu: function(e, item) {
60539         item = this.findItemFromChild(e.getTarget());
60540                 if (item) {
60541                         e.stopEvent();
60542                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
60543                         this.contextMenu.showAt(e.getXY());
60544             }
60545     },
60546
60547 /**
60548  *      Remove {@link Roo.data.Record}s at the specified indices.
60549  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
60550  */
60551     remove: function(selectedIndices) {
60552                 selectedIndices = [].concat(selectedIndices);
60553                 for (var i = 0; i < selectedIndices.length; i++) {
60554                         var rec = this.store.getAt(selectedIndices[i]);
60555                         this.store.remove(rec);
60556                 }
60557     },
60558
60559 /**
60560  *      Double click fires the event, but also, if this is draggable, and there is only one other
60561  *      related DropZone, it transfers the selected node.
60562  */
60563     onDblClick : function(e){
60564         var item = this.findItemFromChild(e.getTarget());
60565         if(item){
60566             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
60567                 return false;
60568             }
60569             if (this.dragGroup) {
60570                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
60571                     while (targets.indexOf(this.dropZone) > -1) {
60572                             targets.remove(this.dropZone);
60573                                 }
60574                     if (targets.length == 1) {
60575                                         this.dragZone.cachedTarget = null;
60576                         var el = Roo.get(targets[0].getEl());
60577                         var box = el.getBox(true);
60578                         targets[0].onNodeDrop(el.dom, {
60579                                 target: el.dom,
60580                                 xy: [box.x, box.y + box.height - 1]
60581                         }, null, this.getDragData(e));
60582                     }
60583                 }
60584         }
60585     },
60586     
60587     handleSelection: function(e) {
60588                 this.dragZone.cachedTarget = null;
60589         var item = this.findItemFromChild(e.getTarget());
60590         if (!item) {
60591                 this.clearSelections(true);
60592                 return;
60593         }
60594                 if (item && (this.multiSelect || this.singleSelect)){
60595                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
60596                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
60597                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
60598                                 this.unselect(item);
60599                         } else {
60600                                 this.select(item, this.multiSelect && e.ctrlKey);
60601                                 this.lastSelection = item;
60602                         }
60603                 }
60604     },
60605
60606     onItemClick : function(item, index, e){
60607                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
60608                         return false;
60609                 }
60610                 return true;
60611     },
60612
60613     unselect : function(nodeInfo, suppressEvent){
60614                 var node = this.getNode(nodeInfo);
60615                 if(node && this.isSelected(node)){
60616                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
60617                                 Roo.fly(node).removeClass(this.selectedClass);
60618                                 this.selections.remove(node);
60619                                 if(!suppressEvent){
60620                                         this.fireEvent("selectionchange", this, this.selections);
60621                                 }
60622                         }
60623                 }
60624     }
60625 });
60626 Roo.layout = {};/*
60627  * Based on:
60628  * Ext JS Library 1.1.1
60629  * Copyright(c) 2006-2007, Ext JS, LLC.
60630  *
60631  * Originally Released Under LGPL - original licence link has changed is not relivant.
60632  *
60633  * Fork - LGPL
60634  * <script type="text/javascript">
60635  */
60636  
60637 /**
60638  * @class Roo.layout.Manager
60639  * @extends Roo.util.Observable
60640  * Base class for layout managers.
60641  */
60642 Roo.layout.Manager = function(container, config){
60643     Roo.layout.Manager.superclass.constructor.call(this);
60644     this.el = Roo.get(container);
60645     // ie scrollbar fix
60646     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
60647         document.body.scroll = "no";
60648     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
60649         this.el.position('relative');
60650     }
60651     this.id = this.el.id;
60652     this.el.addClass("x-layout-container");
60653     /** false to disable window resize monitoring @type Boolean */
60654     this.monitorWindowResize = true;
60655     this.regions = {};
60656     this.addEvents({
60657         /**
60658          * @event layout
60659          * Fires when a layout is performed. 
60660          * @param {Roo.layout.Manager} this
60661          */
60662         "layout" : true,
60663         /**
60664          * @event regionresized
60665          * Fires when the user resizes a region. 
60666          * @param {Roo.layout.Region} region The resized region
60667          * @param {Number} newSize The new size (width for east/west, height for north/south)
60668          */
60669         "regionresized" : true,
60670         /**
60671          * @event regioncollapsed
60672          * Fires when a region is collapsed. 
60673          * @param {Roo.layout.Region} region The collapsed region
60674          */
60675         "regioncollapsed" : true,
60676         /**
60677          * @event regionexpanded
60678          * Fires when a region is expanded.  
60679          * @param {Roo.layout.Region} region The expanded region
60680          */
60681         "regionexpanded" : true
60682     });
60683     this.updating = false;
60684     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
60685 };
60686
60687 Roo.extend(Roo.layout.Manager, Roo.util.Observable, {
60688     /**
60689      * Returns true if this layout is currently being updated
60690      * @return {Boolean}
60691      */
60692     isUpdating : function(){
60693         return this.updating; 
60694     },
60695     
60696     /**
60697      * Suspend the LayoutManager from doing auto-layouts while
60698      * making multiple add or remove calls
60699      */
60700     beginUpdate : function(){
60701         this.updating = true;    
60702     },
60703     
60704     /**
60705      * Restore auto-layouts and optionally disable the manager from performing a layout
60706      * @param {Boolean} noLayout true to disable a layout update 
60707      */
60708     endUpdate : function(noLayout){
60709         this.updating = false;
60710         if(!noLayout){
60711             this.layout();
60712         }    
60713     },
60714     
60715     layout: function(){
60716         
60717     },
60718     
60719     onRegionResized : function(region, newSize){
60720         this.fireEvent("regionresized", region, newSize);
60721         this.layout();
60722     },
60723     
60724     onRegionCollapsed : function(region){
60725         this.fireEvent("regioncollapsed", region);
60726     },
60727     
60728     onRegionExpanded : function(region){
60729         this.fireEvent("regionexpanded", region);
60730     },
60731         
60732     /**
60733      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
60734      * performs box-model adjustments.
60735      * @return {Object} The size as an object {width: (the width), height: (the height)}
60736      */
60737     getViewSize : function(){
60738         var size;
60739         if(this.el.dom != document.body){
60740             size = this.el.getSize();
60741         }else{
60742             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
60743         }
60744         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
60745         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
60746         return size;
60747     },
60748     
60749     /**
60750      * Returns the Element this layout is bound to.
60751      * @return {Roo.Element}
60752      */
60753     getEl : function(){
60754         return this.el;
60755     },
60756     
60757     /**
60758      * Returns the specified region.
60759      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
60760      * @return {Roo.layout.Region}
60761      */
60762     getRegion : function(target){
60763         return this.regions[target.toLowerCase()];
60764     },
60765     
60766     onWindowResize : function(){
60767         if(this.monitorWindowResize){
60768             this.layout();
60769         }
60770     }
60771 });/*
60772  * Based on:
60773  * Ext JS Library 1.1.1
60774  * Copyright(c) 2006-2007, Ext JS, LLC.
60775  *
60776  * Originally Released Under LGPL - original licence link has changed is not relivant.
60777  *
60778  * Fork - LGPL
60779  * <script type="text/javascript">
60780  */
60781 /**
60782  * @class Roo.layout.Border
60783  * @extends Roo.layout.Manager
60784  * @children Roo.panel.Content
60785  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
60786  * please see: <br><br>
60787  * <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>
60788  * <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>
60789  * Example:
60790  <pre><code>
60791  var layout = new Roo.layout.Border(document.body, {
60792     north: {
60793         initialSize: 25,
60794         titlebar: false
60795     },
60796     west: {
60797         split:true,
60798         initialSize: 200,
60799         minSize: 175,
60800         maxSize: 400,
60801         titlebar: true,
60802         collapsible: true
60803     },
60804     east: {
60805         split:true,
60806         initialSize: 202,
60807         minSize: 175,
60808         maxSize: 400,
60809         titlebar: true,
60810         collapsible: true
60811     },
60812     south: {
60813         split:true,
60814         initialSize: 100,
60815         minSize: 100,
60816         maxSize: 200,
60817         titlebar: true,
60818         collapsible: true
60819     },
60820     center: {
60821         titlebar: true,
60822         autoScroll:true,
60823         resizeTabs: true,
60824         minTabWidth: 50,
60825         preferredTabWidth: 150
60826     }
60827 });
60828
60829 // shorthand
60830 var CP = Roo.panel.Content;
60831
60832 layout.beginUpdate();
60833 layout.add("north", new CP("north", "North"));
60834 layout.add("south", new CP("south", {title: "South", closable: true}));
60835 layout.add("west", new CP("west", {title: "West"}));
60836 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
60837 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
60838 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
60839 layout.getRegion("center").showPanel("center1");
60840 layout.endUpdate();
60841 </code></pre>
60842
60843 <b>The container the layout is rendered into can be either the body element or any other element.
60844 If it is not the body element, the container needs to either be an absolute positioned element,
60845 or you will need to add "position:relative" to the css of the container.  You will also need to specify
60846 the container size if it is not the body element.</b>
60847
60848 * @constructor
60849 * Create a new BorderLayout
60850 * @param {String/HTMLElement/Element} container The container this layout is bound to
60851 * @param {Object} config Configuration options
60852  */
60853 Roo.layout.Border = function(container, config){
60854     config = config || {};
60855     Roo.layout.Border.superclass.constructor.call(this, container, config);
60856     this.factory = config.factory || Roo.layout.Border.RegionFactory;
60857     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
60858         var target = this.factory.validRegions[i];
60859         if(config[target]){
60860             this.addRegion(target, config[target]);
60861         }
60862     }
60863 };
60864
60865 Roo.extend(Roo.layout.Border, Roo.layout.Manager, {
60866         
60867         /**
60868          * @cfg {Roo.layout.Region} east
60869          */
60870         /**
60871          * @cfg {Roo.layout.Region} west
60872          */
60873         /**
60874          * @cfg {Roo.layout.Region} north
60875          */
60876         /**
60877          * @cfg {Roo.layout.Region} south
60878          */
60879         /**
60880          * @cfg {Roo.layout.Region} center
60881          */
60882     /**
60883      * Creates and adds a new region if it doesn't already exist.
60884      * @param {String} target The target region key (north, south, east, west or center).
60885      * @param {Object} config The regions config object
60886      * @return {BorderLayoutRegion} The new region
60887      */
60888     addRegion : function(target, config){
60889         if(!this.regions[target]){
60890             var r = this.factory.create(target, this, config);
60891             this.bindRegion(target, r);
60892         }
60893         return this.regions[target];
60894     },
60895
60896     // private (kinda)
60897     bindRegion : function(name, r){
60898         this.regions[name] = r;
60899         r.on("visibilitychange", this.layout, this);
60900         r.on("paneladded", this.layout, this);
60901         r.on("panelremoved", this.layout, this);
60902         r.on("invalidated", this.layout, this);
60903         r.on("resized", this.onRegionResized, this);
60904         r.on("collapsed", this.onRegionCollapsed, this);
60905         r.on("expanded", this.onRegionExpanded, this);
60906     },
60907
60908     /**
60909      * Performs a layout update.
60910      */
60911     layout : function(){
60912         if(this.updating) {
60913             return;
60914         }
60915         var size = this.getViewSize();
60916         var w = size.width;
60917         var h = size.height;
60918         var centerW = w;
60919         var centerH = h;
60920         var centerY = 0;
60921         var centerX = 0;
60922         //var x = 0, y = 0;
60923
60924         var rs = this.regions;
60925         var north = rs["north"];
60926         var south = rs["south"]; 
60927         var west = rs["west"];
60928         var east = rs["east"];
60929         var center = rs["center"];
60930         //if(this.hideOnLayout){ // not supported anymore
60931             //c.el.setStyle("display", "none");
60932         //}
60933         if(north && north.isVisible()){
60934             var b = north.getBox();
60935             var m = north.getMargins();
60936             b.width = w - (m.left+m.right);
60937             b.x = m.left;
60938             b.y = m.top;
60939             centerY = b.height + b.y + m.bottom;
60940             centerH -= centerY;
60941             north.updateBox(this.safeBox(b));
60942         }
60943         if(south && south.isVisible()){
60944             var b = south.getBox();
60945             var m = south.getMargins();
60946             b.width = w - (m.left+m.right);
60947             b.x = m.left;
60948             var totalHeight = (b.height + m.top + m.bottom);
60949             b.y = h - totalHeight + m.top;
60950             centerH -= totalHeight;
60951             south.updateBox(this.safeBox(b));
60952         }
60953         if(west && west.isVisible()){
60954             var b = west.getBox();
60955             var m = west.getMargins();
60956             b.height = centerH - (m.top+m.bottom);
60957             b.x = m.left;
60958             b.y = centerY + m.top;
60959             var totalWidth = (b.width + m.left + m.right);
60960             centerX += totalWidth;
60961             centerW -= totalWidth;
60962             west.updateBox(this.safeBox(b));
60963         }
60964         if(east && east.isVisible()){
60965             var b = east.getBox();
60966             var m = east.getMargins();
60967             b.height = centerH - (m.top+m.bottom);
60968             var totalWidth = (b.width + m.left + m.right);
60969             b.x = w - totalWidth + m.left;
60970             b.y = centerY + m.top;
60971             centerW -= totalWidth;
60972             east.updateBox(this.safeBox(b));
60973         }
60974         if(center){
60975             var m = center.getMargins();
60976             var centerBox = {
60977                 x: centerX + m.left,
60978                 y: centerY + m.top,
60979                 width: centerW - (m.left+m.right),
60980                 height: centerH - (m.top+m.bottom)
60981             };
60982             //if(this.hideOnLayout){
60983                 //center.el.setStyle("display", "block");
60984             //}
60985             center.updateBox(this.safeBox(centerBox));
60986         }
60987         this.el.repaint();
60988         this.fireEvent("layout", this);
60989     },
60990
60991     // private
60992     safeBox : function(box){
60993         box.width = Math.max(0, box.width);
60994         box.height = Math.max(0, box.height);
60995         return box;
60996     },
60997
60998     /**
60999      * Adds a ContentPanel (or subclass) to this layout.
61000      * @param {String} target The target region key (north, south, east, west or center).
61001      * @param {Roo.panel.Content} panel The panel to add
61002      * @return {Roo.panel.Content} The added panel
61003      */
61004     add : function(target, panel){
61005          
61006         target = target.toLowerCase();
61007         return this.regions[target].add(panel);
61008     },
61009
61010     /**
61011      * Remove a ContentPanel (or subclass) to this layout.
61012      * @param {String} target The target region key (north, south, east, west or center).
61013      * @param {Number/String/Roo.panel.Content} panel The index, id or panel to remove
61014      * @return {Roo.panel.Content} The removed panel
61015      */
61016     remove : function(target, panel){
61017         target = target.toLowerCase();
61018         return this.regions[target].remove(panel);
61019     },
61020
61021     /**
61022      * Searches all regions for a panel with the specified id
61023      * @param {String} panelId
61024      * @return {Roo.panel.Content} The panel or null if it wasn't found
61025      */
61026     findPanel : function(panelId){
61027         var rs = this.regions;
61028         for(var target in rs){
61029             if(typeof rs[target] != "function"){
61030                 var p = rs[target].getPanel(panelId);
61031                 if(p){
61032                     return p;
61033                 }
61034             }
61035         }
61036         return null;
61037     },
61038
61039     /**
61040      * Searches all regions for a panel with the specified id and activates (shows) it.
61041      * @param {String/panel.Content} panelId The panels id or the panel itself
61042      * @return {Roo.panel.Content} The shown panel or null
61043      */
61044     showPanel : function(panelId) {
61045       var rs = this.regions;
61046       for(var target in rs){
61047          var r = rs[target];
61048          if(typeof r != "function"){
61049             if(r.hasPanel(panelId)){
61050                return r.showPanel(panelId);
61051             }
61052          }
61053       }
61054       return null;
61055    },
61056
61057    /**
61058      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
61059      * @param {Roo.state.Provider} provider (optional) An alternate state provider
61060      */
61061     restoreState : function(provider){
61062         if(!provider){
61063             provider = Roo.state.Manager;
61064         }
61065         var sm = new Roo.layout.StateManager();
61066         sm.init(this, provider);
61067     },
61068
61069     /**
61070      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
61071      * object should contain properties for each region to add ContentPanels to, and each property's value should be
61072      * a valid ContentPanel config object.  Example:
61073      * <pre><code>
61074 // Create the main layout
61075 var layout = new Roo.layout.Border('main-ct', {
61076     west: {
61077         split:true,
61078         minSize: 175,
61079         titlebar: true
61080     },
61081     center: {
61082         title:'Components'
61083     }
61084 }, 'main-ct');
61085
61086 // Create and add multiple ContentPanels at once via configs
61087 layout.batchAdd({
61088    west: {
61089        id: 'source-files',
61090        autoCreate:true,
61091        title:'Ext Source Files',
61092        autoScroll:true,
61093        fitToFrame:true
61094    },
61095    center : {
61096        el: cview,
61097        autoScroll:true,
61098        fitToFrame:true,
61099        toolbar: tb,
61100        resizeEl:'cbody'
61101    }
61102 });
61103 </code></pre>
61104      * @param {Object} regions An object containing ContentPanel configs by region name
61105      */
61106     batchAdd : function(regions){
61107         this.beginUpdate();
61108         for(var rname in regions){
61109             var lr = this.regions[rname];
61110             if(lr){
61111                 this.addTypedPanels(lr, regions[rname]);
61112             }
61113         }
61114         this.endUpdate();
61115     },
61116
61117     // private
61118     addTypedPanels : function(lr, ps){
61119         if(typeof ps == 'string'){
61120             lr.add(new Roo.panel.Content(ps));
61121         }
61122         else if(ps instanceof Array){
61123             for(var i =0, len = ps.length; i < len; i++){
61124                 this.addTypedPanels(lr, ps[i]);
61125             }
61126         }
61127         else if(!ps.events){ // raw config?
61128             var el = ps.el;
61129             delete ps.el; // prevent conflict
61130             lr.add(new Roo.panel.Content(el || Roo.id(), ps));
61131         }
61132         else {  // panel object assumed!
61133             lr.add(ps);
61134         }
61135     },
61136     /**
61137      * Adds a xtype elements to the layout.
61138      * <pre><code>
61139
61140 layout.addxtype({
61141        xtype : 'ContentPanel',
61142        region: 'west',
61143        items: [ .... ]
61144    }
61145 );
61146
61147 layout.addxtype({
61148         xtype : 'NestedLayoutPanel',
61149         region: 'west',
61150         layout: {
61151            center: { },
61152            west: { }   
61153         },
61154         items : [ ... list of content panels or nested layout panels.. ]
61155    }
61156 );
61157 </code></pre>
61158      * @param {Object} cfg Xtype definition of item to add.
61159      */
61160     addxtype : function(cfg)
61161     {
61162         // basically accepts a pannel...
61163         // can accept a layout region..!?!?
61164         //Roo.log('Roo.layout.Border add ' + cfg.xtype)
61165         
61166         // if (!cfg.xtype.match(/Panel$/)) {
61167         //     return false;
61168         // }
61169         var ret = false;
61170
61171         if (typeof(cfg.region) == 'undefined') {
61172             Roo.log("Failed to add Panel, region was not set");
61173             Roo.log(cfg);
61174             return false;
61175         }
61176         var region = cfg.region;
61177         delete cfg.region;
61178         
61179           
61180         var xitems = [];
61181         if (cfg.items) {
61182             xitems = cfg.items;
61183             delete cfg.items;
61184         }
61185         var nb = false;
61186         
61187         switch(cfg.xtype) 
61188         {
61189             case 'Content':
61190                 if(cfg.autoCreate) {
61191                     ret = new Roo.panel[cfg.xtype](cfg); // new panel!!!!!
61192                 } else {
61193                     var el = this.el.createChild();
61194                     ret = new Roo.panel[cfg.xtype](el, cfg); // new panel!!!!!
61195                 }
61196                 
61197                 this.add(region, ret);
61198                 break;
61199             case 'Grid':
61200                 // needs grid and region
61201                 
61202                 //var el = this.getRegion(region).el.createChild();
61203                 var el = this.el.createChild();
61204                 // create the grid first...
61205                 
61206                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
61207                 delete cfg.grid;
61208                 if (region == 'center' && this.active ) {
61209                     cfg.background = false;
61210                 }
61211                 ret = new Roo.panel[cfg.xtype](grid, cfg); // new panel!!!!!
61212                 
61213                 this.add(region, ret);
61214                 if (cfg.background) {
61215                     ret.on('activate', function(gp) {
61216                         if (!gp.grid.rendered) {
61217                             gp.grid.render();
61218                         }
61219                     });
61220                 } else {
61221                     grid.render();
61222                 }
61223                 break;
61224             case 'NestedLayout': 
61225                 // create a new Layout (which is  a Border Layout...
61226                 var el = this.el.createChild();
61227                 var clayout = cfg.layout;
61228                 delete cfg.layout;
61229                 clayout.items   = clayout.items  || [];
61230                 // replace this exitems with the clayout ones..
61231                 xitems = clayout.items;
61232                  
61233                 
61234                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
61235                     cfg.background = false;
61236                 }
61237                 var layout = new Roo.layout.Border(el, clayout);
61238                 
61239                 ret = new Roo.panel[cfg.xtype](layout, cfg); // new panel!!!!!
61240                 //console.log('adding nested layout panel '  + cfg.toSource());
61241                 this.add(region, ret);
61242                 nb = {}; /// find first...
61243                 break;
61244                 
61245             case 'Calendar':
61246                 ret = new Roo.panel[cfg.xtype](cfg); // new panel!!!!!
61247                 this.add(region, ret);
61248                 break;
61249             case 'Tree': // our new panel!
61250                 cfg.el = this.el.createChild();
61251                 ret = new Roo.panel[cfg.xtype](cfg); // new panel!!!!!
61252                 this.add(region, ret);
61253                 break;
61254             case 'ContentPanel':
61255             case 'ScrollPanel':  // ContentPanel (el, cfg)
61256             case 'ViewPanel': 
61257                 if(cfg.autoCreate) {
61258                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
61259                 } else {
61260                     var el = this.el.createChild();
61261                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
61262                 }
61263                 
61264                 this.add(region, ret);
61265                 break;
61266             
61267             
61268             case 'TreePanel': // our new panel!
61269                 cfg.el = this.el.createChild();
61270                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
61271                 this.add(region, ret);
61272                 break;
61273             
61274             case 'NestedLayoutPanel': 
61275                 // create a new Layout (which is  a Border Layout...
61276                 var el = this.el.createChild();
61277                 var clayout = cfg.layout;
61278                 delete cfg.layout;
61279                 clayout.items   = clayout.items  || [];
61280                 // replace this exitems with the clayout ones..
61281                 xitems = clayout.items;
61282                  
61283                 
61284                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
61285                     cfg.background = false;
61286                 }
61287                 var layout = new Roo.layout.Border(el, clayout);
61288                 
61289                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
61290                 //console.log('adding nested layout panel '  + cfg.toSource());
61291                 this.add(region, ret);
61292                 nb = {}; /// find first...
61293                 break;
61294                 
61295             case 'GridPanel': 
61296             
61297                 // needs grid and region
61298                 
61299                 //var el = this.getRegion(region).el.createChild();
61300                 var el = this.el.createChild();
61301                 // create the grid first...
61302                 
61303                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
61304                 delete cfg.grid;
61305                 if (region == 'center' && this.active ) {
61306                     cfg.background = false;
61307                 }
61308                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
61309                 
61310                 this.add(region, ret);
61311                 if (cfg.background) {
61312                     ret.on('activate', function(gp) {
61313                         if (!gp.grid.rendered) {
61314                             gp.grid.render();
61315                         }
61316                     });
61317                 } else {
61318                     grid.render();
61319                 }
61320                 break;
61321            
61322            
61323            
61324                 
61325                 
61326                 
61327             default:
61328                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
61329                     
61330                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
61331                     this.add(region, ret);
61332                 } else {
61333                 
61334                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
61335                     return null;
61336                 }
61337                 
61338              // GridPanel (grid, cfg)
61339             
61340         }
61341         this.beginUpdate();
61342         // add children..
61343         var region = '';
61344         var abn = {};
61345         Roo.each(xitems, function(i)  {
61346             region = nb && i.region ? i.region : false;
61347             
61348             var add = ret.addxtype(i);
61349            
61350             if (region) {
61351                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
61352                 if (!i.background) {
61353                     abn[region] = nb[region] ;
61354                 }
61355             }
61356             
61357         });
61358         this.endUpdate();
61359
61360         // make the last non-background panel active..
61361         //if (nb) { Roo.log(abn); }
61362         if (nb) {
61363             
61364             for(var r in abn) {
61365                 region = this.getRegion(r);
61366                 if (region) {
61367                     // tried using nb[r], but it does not work..
61368                      
61369                     region.showPanel(abn[r]);
61370                    
61371                 }
61372             }
61373         }
61374         return ret;
61375         
61376     }
61377 });
61378
61379 /**
61380  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
61381  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
61382  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
61383  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
61384  * <pre><code>
61385 // shorthand
61386 var CP = Roo.ContentPanel;
61387
61388 var layout = Roo.layout.Border.create({
61389     north: {
61390         initialSize: 25,
61391         titlebar: false,
61392         panels: [new CP("north", "North")]
61393     },
61394     west: {
61395         split:true,
61396         initialSize: 200,
61397         minSize: 175,
61398         maxSize: 400,
61399         titlebar: true,
61400         collapsible: true,
61401         panels: [new CP("west", {title: "West"})]
61402     },
61403     east: {
61404         split:true,
61405         initialSize: 202,
61406         minSize: 175,
61407         maxSize: 400,
61408         titlebar: true,
61409         collapsible: true,
61410         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
61411     },
61412     south: {
61413         split:true,
61414         initialSize: 100,
61415         minSize: 100,
61416         maxSize: 200,
61417         titlebar: true,
61418         collapsible: true,
61419         panels: [new CP("south", {title: "South", closable: true})]
61420     },
61421     center: {
61422         titlebar: true,
61423         autoScroll:true,
61424         resizeTabs: true,
61425         minTabWidth: 50,
61426         preferredTabWidth: 150,
61427         panels: [
61428             new CP("center1", {title: "Close Me", closable: true}),
61429             new CP("center2", {title: "Center Panel", closable: false})
61430         ]
61431     }
61432 }, document.body);
61433
61434 layout.getRegion("center").showPanel("center1");
61435 </code></pre>
61436  * @param config
61437  * @param targetEl
61438  */
61439 Roo.layout.Border.create = function(config, targetEl){
61440     var layout = new Roo.layout.Border(targetEl || document.body, config);
61441     layout.beginUpdate();
61442     var regions = Roo.layout.Border.RegionFactory.validRegions;
61443     for(var j = 0, jlen = regions.length; j < jlen; j++){
61444         var lr = regions[j];
61445         if(layout.regions[lr] && config[lr].panels){
61446             var r = layout.regions[lr];
61447             var ps = config[lr].panels;
61448             layout.addTypedPanels(r, ps);
61449         }
61450     }
61451     layout.endUpdate();
61452     return layout;
61453 };
61454
61455 // private
61456 Roo.layout.Border.RegionFactory = {
61457     // private
61458     validRegions : ["north","south","east","west","center"],
61459
61460     // private
61461     create : function(target, mgr, config){
61462         target = target.toLowerCase();
61463         if(config.lightweight || config.basic){
61464             return new Roo.layout.BasicRegion(mgr, config, target);
61465         }
61466                 var cn = target.charAt(0).toUpperCase() + target.slice(1);
61467                 if (typeof (Roo.layout[cn]) == 'undefined') {
61468                         throw 'Layout region "'+target+'" not supported.';
61469                 }
61470                 return new Roo.layout[cn](mgr, config);
61471          
61472         
61473     }
61474 };/*
61475  * Based on:
61476  * Ext JS Library 1.1.1
61477  * Copyright(c) 2006-2007, Ext JS, LLC.
61478  *
61479  * Originally Released Under LGPL - original licence link has changed is not relivant.
61480  *
61481  * Fork - LGPL
61482  * <script type="text/javascript">
61483  */
61484  
61485 /**
61486  * @class Roo.layout.BasicRegion
61487  * @extends Roo.util.Observable
61488  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
61489  * and does not have a titlebar, tabs or any other features. All it does is size and position 
61490  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
61491  */
61492 Roo.layout.BasicRegion= function(mgr, config, pos, skipConfig){
61493     this.mgr = mgr;
61494     this.position  = pos;
61495     this.events = {
61496         /**
61497          * @scope Roo.layout.BasicRegion
61498          */
61499         
61500         /**
61501          * @event beforeremove
61502          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
61503          * @param {Roo.layout.Region} this
61504          * @param {Roo.panel.Content} panel The panel
61505          * @param {Object} e The cancel event object
61506          */
61507         "beforeremove" : true,
61508         /**
61509          * @event invalidated
61510          * Fires when the layout for this region is changed.
61511          * @param {Roo.layout.Region} this
61512          */
61513         "invalidated" : true,
61514         /**
61515          * @event visibilitychange
61516          * Fires when this region is shown or hidden 
61517          * @param {Roo.layout.Region} this
61518          * @param {Boolean} visibility true or false
61519          */
61520         "visibilitychange" : true,
61521         /**
61522          * @event paneladded
61523          * Fires when a panel is added. 
61524          * @param {Roo.layout.Region} this
61525          * @param {Roo.panel.Content} panel The panel
61526          */
61527         "paneladded" : true,
61528         /**
61529          * @event panelremoved
61530          * Fires when a panel is removed. 
61531          * @param {Roo.layout.Region} this
61532          * @param {Roo.panel.Content} panel The panel
61533          */
61534         "panelremoved" : true,
61535         /**
61536          * @event beforecollapse
61537          * Fires when this region before collapse.
61538          * @param {Roo.layout.Region} this
61539          */
61540         "beforecollapse" : true,
61541         /**
61542          * @event collapsed
61543          * Fires when this region is collapsed.
61544          * @param {Roo.layout.Region} this
61545          */
61546         "collapsed" : true,
61547         /**
61548          * @event expanded
61549          * Fires when this region is expanded.
61550          * @param {Roo.layout.Region} this
61551          */
61552         "expanded" : true,
61553         /**
61554          * @event slideshow
61555          * Fires when this region is slid into view.
61556          * @param {Roo.layout.Region} this
61557          */
61558         "slideshow" : true,
61559         /**
61560          * @event slidehide
61561          * Fires when this region slides out of view. 
61562          * @param {Roo.layout.Region} this
61563          */
61564         "slidehide" : true,
61565         /**
61566          * @event panelactivated
61567          * Fires when a panel is activated. 
61568          * @param {Roo.layout.Region} this
61569          * @param {Roo.panel.Content} panel The activated panel
61570          */
61571         "panelactivated" : true,
61572         /**
61573          * @event resized
61574          * Fires when the user resizes this region. 
61575          * @param {Roo.layout.Region} this
61576          * @param {Number} newSize The new size (width for east/west, height for north/south)
61577          */
61578         "resized" : true
61579     };
61580     /** A collection of panels in this region. @type Roo.util.MixedCollection */
61581     this.panels = new Roo.util.MixedCollection();
61582     this.panels.getKey = this.getPanelId.createDelegate(this);
61583     this.box = null;
61584     this.activePanel = null;
61585     // ensure listeners are added...
61586     
61587     if (config.listeners || config.events) {
61588         Roo.layout.BasicRegion.superclass.constructor.call(this, {
61589             listeners : config.listeners || {},
61590             events : config.events || {}
61591         });
61592     }
61593     
61594     if(skipConfig !== true){
61595         this.applyConfig(config);
61596     }
61597 };
61598
61599 Roo.extend(Roo.layout.BasicRegion, Roo.util.Observable, {
61600     getPanelId : function(p){
61601         return p.getId();
61602     },
61603     
61604     applyConfig : function(config){
61605         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
61606         this.config = config;
61607         
61608     },
61609     
61610     /**
61611      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
61612      * the width, for horizontal (north, south) the height.
61613      * @param {Number} newSize The new width or height
61614      */
61615     resizeTo : function(newSize){
61616         var el = this.el ? this.el :
61617                  (this.activePanel ? this.activePanel.getEl() : null);
61618         if(el){
61619             switch(this.position){
61620                 case "east":
61621                 case "west":
61622                     el.setWidth(newSize);
61623                     this.fireEvent("resized", this, newSize);
61624                 break;
61625                 case "north":
61626                 case "south":
61627                     el.setHeight(newSize);
61628                     this.fireEvent("resized", this, newSize);
61629                 break;                
61630             }
61631         }
61632     },
61633     
61634     getBox : function(){
61635         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
61636     },
61637     
61638     getMargins : function(){
61639         return this.margins;
61640     },
61641     
61642     updateBox : function(box){
61643         this.box = box;
61644         var el = this.activePanel.getEl();
61645         el.dom.style.left = box.x + "px";
61646         el.dom.style.top = box.y + "px";
61647         this.activePanel.setSize(box.width, box.height);
61648     },
61649     
61650     /**
61651      * Returns the container element for this region.
61652      * @return {Roo.Element}
61653      */
61654     getEl : function(){
61655         return this.activePanel;
61656     },
61657     
61658     /**
61659      * Returns true if this region is currently visible.
61660      * @return {Boolean}
61661      */
61662     isVisible : function(){
61663         return this.activePanel ? true : false;
61664     },
61665     
61666     setActivePanel : function(panel){
61667         panel = this.getPanel(panel);
61668         if(this.activePanel && this.activePanel != panel){
61669             this.activePanel.setActiveState(false);
61670             this.activePanel.getEl().setLeftTop(-10000,-10000);
61671         }
61672         this.activePanel = panel;
61673         panel.setActiveState(true);
61674         if(this.box){
61675             panel.setSize(this.box.width, this.box.height);
61676         }
61677         this.fireEvent("panelactivated", this, panel);
61678         this.fireEvent("invalidated");
61679     },
61680     
61681     /**
61682      * Show the specified panel.
61683      * @param {Number/String/panel.Content} panelId The panels index, id or the panel itself
61684      * @return {Roo.panel.Content} The shown panel or null
61685      */
61686     showPanel : function(panel){
61687         if(panel = this.getPanel(panel)){
61688             this.setActivePanel(panel);
61689         }
61690         return panel;
61691     },
61692     
61693     /**
61694      * Get the active panel for this region.
61695      * @return {Roo.panel.Content} The active panel or null
61696      */
61697     getActivePanel : function(){
61698         return this.activePanel;
61699     },
61700     
61701     /**
61702      * Add the passed ContentPanel(s)
61703      * @param {panel.Content...} panel The ContentPanel(s) to add (you can pass more than one)
61704      * @return {Roo.panel.Content} The panel added (if only one was added)
61705      */
61706     add : function(panel){
61707         if(arguments.length > 1){
61708             for(var i = 0, len = arguments.length; i < len; i++) {
61709                 this.add(arguments[i]);
61710             }
61711             return null;
61712         }
61713         if(this.hasPanel(panel)){
61714             this.showPanel(panel);
61715             return panel;
61716         }
61717         var el = panel.getEl();
61718         if(el.dom.parentNode != this.mgr.el.dom){
61719             this.mgr.el.dom.appendChild(el.dom);
61720         }
61721         if(panel.setRegion){
61722             panel.setRegion(this);
61723         }
61724         this.panels.add(panel);
61725         el.setStyle("position", "absolute");
61726         if(!panel.background){
61727             this.setActivePanel(panel);
61728             if(this.config.initialSize && this.panels.getCount()==1){
61729                 this.resizeTo(this.config.initialSize);
61730             }
61731         }
61732         this.fireEvent("paneladded", this, panel);
61733         return panel;
61734     },
61735     
61736     /**
61737      * Returns true if the panel is in this region.
61738      * @param {Number/String/panel.Content} panel The panels index, id or the panel itself
61739      * @return {Boolean}
61740      */
61741     hasPanel : function(panel){
61742         if(typeof panel == "object"){ // must be panel obj
61743             panel = panel.getId();
61744         }
61745         return this.getPanel(panel) ? true : false;
61746     },
61747     
61748     /**
61749      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
61750      * @param {Number/String/panel.Content} panel The panels index, id or the panel itself
61751      * @param {Boolean} preservePanel Overrides the config preservePanel option
61752      * @return {Roo.panel.Content} The panel that was removed
61753      */
61754     remove : function(panel, preservePanel){
61755         panel = this.getPanel(panel);
61756         if(!panel){
61757             return null;
61758         }
61759         var e = {};
61760         this.fireEvent("beforeremove", this, panel, e);
61761         if(e.cancel === true){
61762             return null;
61763         }
61764         var panelId = panel.getId();
61765         this.panels.removeKey(panelId);
61766         return panel;
61767     },
61768     
61769     /**
61770      * Returns the panel specified or null if it's not in this region.
61771      * @param {Number/String/panel.Content} panel The panels index, id or the panel itself
61772      * @return {Roo.panel.Content}
61773      */
61774     getPanel : function(id){
61775         if(typeof id == "object"){ // must be panel obj
61776             return id;
61777         }
61778         return this.panels.get(id);
61779     },
61780     
61781     /**
61782      * Returns this regions position (north/south/east/west/center).
61783      * @return {String} 
61784      */
61785     getPosition: function(){
61786         return this.position;    
61787     }
61788 });/*
61789  * Based on:
61790  * Ext JS Library 1.1.1
61791  * Copyright(c) 2006-2007, Ext JS, LLC.
61792  *
61793  * Originally Released Under LGPL - original licence link has changed is not relivant.
61794  *
61795  * Fork - LGPL
61796  * <script type="text/javascript">
61797  */
61798  
61799 /**
61800  * @class Roo.layout.Region
61801  * @extends Roo.layout.BasicRegion
61802  * This class represents a region in a layout manager.
61803  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
61804  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
61805  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
61806  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
61807  * @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})
61808  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
61809  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
61810  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
61811  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
61812  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
61813  * @cfg {String}    title           The title for the region (overrides panel titles)
61814  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
61815  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
61816  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
61817  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
61818  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
61819  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
61820  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
61821  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
61822  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
61823  * @cfg {Boolean}   showPin         True to show a pin button
61824  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
61825  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
61826  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
61827  * @cfg {Number}    width           For East/West panels
61828  * @cfg {Number}    height          For North/South panels
61829  * @cfg {Boolean}   split           To show the splitter
61830  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
61831  */
61832 Roo.layout.Region = function(mgr, config, pos){
61833     Roo.layout.Region.superclass.constructor.call(this, mgr, config, pos, true);
61834     var dh = Roo.DomHelper;
61835     /** This region's container element 
61836     * @type Roo.Element */
61837     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
61838     /** This region's title element 
61839     * @type Roo.Element */
61840
61841     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
61842         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
61843         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
61844     ]}, true);
61845     this.titleEl.enableDisplayMode();
61846     /** This region's title text element 
61847     * @type HTMLElement */
61848     this.titleTextEl = this.titleEl.dom.firstChild;
61849     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
61850     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
61851     this.closeBtn.enableDisplayMode();
61852     this.closeBtn.on("click", this.closeClicked, this);
61853     this.closeBtn.hide();
61854
61855     this.createBody(config);
61856     this.visible = true;
61857     this.collapsed = false;
61858
61859     if(config.hideWhenEmpty){
61860         this.hide();
61861         this.on("paneladded", this.validateVisibility, this);
61862         this.on("panelremoved", this.validateVisibility, this);
61863     }
61864     this.applyConfig(config);
61865 };
61866
61867 Roo.extend(Roo.layout.Region, Roo.layout.BasicRegion, {
61868
61869     createBody : function(){
61870         /** This region's body element 
61871         * @type Roo.Element */
61872         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
61873     },
61874
61875     applyConfig : function(c){
61876         if(c.collapsible && this.position != "center" && !this.collapsedEl){
61877             var dh = Roo.DomHelper;
61878             if(c.titlebar !== false){
61879                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
61880                 this.collapseBtn.on("click", this.collapse, this);
61881                 this.collapseBtn.enableDisplayMode();
61882
61883                 if(c.showPin === true || this.showPin){
61884                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
61885                     this.stickBtn.enableDisplayMode();
61886                     this.stickBtn.on("click", this.expand, this);
61887                     this.stickBtn.hide();
61888                 }
61889             }
61890             /** This region's collapsed element
61891             * @type Roo.Element */
61892             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
61893                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
61894             ]}, true);
61895             if(c.floatable !== false){
61896                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
61897                this.collapsedEl.on("click", this.collapseClick, this);
61898             }
61899
61900             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
61901                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
61902                    id: "message", unselectable: "on", style:{"float":"left"}});
61903                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
61904              }
61905             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
61906             this.expandBtn.on("click", this.expand, this);
61907         }
61908         if(this.collapseBtn){
61909             this.collapseBtn.setVisible(c.collapsible == true);
61910         }
61911         this.cmargins = c.cmargins || this.cmargins ||
61912                          (this.position == "west" || this.position == "east" ?
61913                              {top: 0, left: 2, right:2, bottom: 0} :
61914                              {top: 2, left: 0, right:0, bottom: 2});
61915         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
61916         this.bottomTabs = c.tabPosition != "top";
61917         this.autoScroll = c.autoScroll || false;
61918         if(this.autoScroll){
61919             this.bodyEl.setStyle("overflow", "auto");
61920         }else{
61921             this.bodyEl.setStyle("overflow", "hidden");
61922         }
61923         //if(c.titlebar !== false){
61924             if((!c.titlebar && !c.title) || c.titlebar === false){
61925                 this.titleEl.hide();
61926             }else{
61927                 this.titleEl.show();
61928                 if(c.title){
61929                     this.titleTextEl.innerHTML = c.title;
61930                 }
61931             }
61932         //}
61933         this.duration = c.duration || .30;
61934         this.slideDuration = c.slideDuration || .45;
61935         this.config = c;
61936         if(c.collapsed){
61937             this.collapse(true);
61938         }
61939         if(c.hidden){
61940             this.hide();
61941         }
61942     },
61943     /**
61944      * Returns true if this region is currently visible.
61945      * @return {Boolean}
61946      */
61947     isVisible : function(){
61948         return this.visible;
61949     },
61950
61951     /**
61952      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
61953      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
61954      */
61955     setCollapsedTitle : function(title){
61956         title = title || "&#160;";
61957         if(this.collapsedTitleTextEl){
61958             this.collapsedTitleTextEl.innerHTML = title;
61959         }
61960     },
61961
61962     getBox : function(){
61963         var b;
61964         if(!this.collapsed){
61965             b = this.el.getBox(false, true);
61966         }else{
61967             b = this.collapsedEl.getBox(false, true);
61968         }
61969         return b;
61970     },
61971
61972     getMargins : function(){
61973         return this.collapsed ? this.cmargins : this.margins;
61974     },
61975
61976     highlight : function(){
61977         this.el.addClass("x-layout-panel-dragover");
61978     },
61979
61980     unhighlight : function(){
61981         this.el.removeClass("x-layout-panel-dragover");
61982     },
61983
61984     updateBox : function(box){
61985         this.box = box;
61986         if(!this.collapsed){
61987             this.el.dom.style.left = box.x + "px";
61988             this.el.dom.style.top = box.y + "px";
61989             this.updateBody(box.width, box.height);
61990         }else{
61991             this.collapsedEl.dom.style.left = box.x + "px";
61992             this.collapsedEl.dom.style.top = box.y + "px";
61993             this.collapsedEl.setSize(box.width, box.height);
61994         }
61995         if(this.tabs){
61996             this.tabs.autoSizeTabs();
61997         }
61998     },
61999
62000     updateBody : function(w, h){
62001         if(w !== null){
62002             this.el.setWidth(w);
62003             w -= this.el.getBorderWidth("rl");
62004             if(this.config.adjustments){
62005                 w += this.config.adjustments[0];
62006             }
62007         }
62008         if(h !== null){
62009             this.el.setHeight(h);
62010             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
62011             h -= this.el.getBorderWidth("tb");
62012             if(this.config.adjustments){
62013                 h += this.config.adjustments[1];
62014             }
62015             this.bodyEl.setHeight(h);
62016             if(this.tabs){
62017                 h = this.tabs.syncHeight(h);
62018             }
62019         }
62020         if(this.panelSize){
62021             w = w !== null ? w : this.panelSize.width;
62022             h = h !== null ? h : this.panelSize.height;
62023         }
62024         if(this.activePanel){
62025             var el = this.activePanel.getEl();
62026             w = w !== null ? w : el.getWidth();
62027             h = h !== null ? h : el.getHeight();
62028             this.panelSize = {width: w, height: h};
62029             this.activePanel.setSize(w, h);
62030         }
62031         if(Roo.isIE && this.tabs){
62032             this.tabs.el.repaint();
62033         }
62034     },
62035
62036     /**
62037      * Returns the container element for this region.
62038      * @return {Roo.Element}
62039      */
62040     getEl : function(){
62041         return this.el;
62042     },
62043
62044     /**
62045      * Hides this region.
62046      */
62047     hide : function(){
62048         if(!this.collapsed){
62049             this.el.dom.style.left = "-2000px";
62050             this.el.hide();
62051         }else{
62052             this.collapsedEl.dom.style.left = "-2000px";
62053             this.collapsedEl.hide();
62054         }
62055         this.visible = false;
62056         this.fireEvent("visibilitychange", this, false);
62057     },
62058
62059     /**
62060      * Shows this region if it was previously hidden.
62061      */
62062     show : function(){
62063         if(!this.collapsed){
62064             this.el.show();
62065         }else{
62066             this.collapsedEl.show();
62067         }
62068         this.visible = true;
62069         this.fireEvent("visibilitychange", this, true);
62070     },
62071
62072     closeClicked : function(){
62073         if(this.activePanel){
62074             this.remove(this.activePanel);
62075         }
62076     },
62077
62078     collapseClick : function(e){
62079         if(this.isSlid){
62080            e.stopPropagation();
62081            this.slideIn();
62082         }else{
62083            e.stopPropagation();
62084            this.slideOut();
62085         }
62086     },
62087
62088     /**
62089      * Collapses this region.
62090      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
62091      */
62092     collapse : function(skipAnim, skipCheck){
62093         if(this.collapsed) {
62094             return;
62095         }
62096         
62097         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
62098             
62099             this.collapsed = true;
62100             if(this.split){
62101                 this.split.el.hide();
62102             }
62103             if(this.config.animate && skipAnim !== true){
62104                 this.fireEvent("invalidated", this);
62105                 this.animateCollapse();
62106             }else{
62107                 this.el.setLocation(-20000,-20000);
62108                 this.el.hide();
62109                 this.collapsedEl.show();
62110                 this.fireEvent("collapsed", this);
62111                 this.fireEvent("invalidated", this);
62112             }
62113         }
62114         
62115     },
62116
62117     animateCollapse : function(){
62118         // overridden
62119     },
62120
62121     /**
62122      * Expands this region if it was previously collapsed.
62123      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
62124      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
62125      */
62126     expand : function(e, skipAnim){
62127         if(e) {
62128             e.stopPropagation();
62129         }
62130         if(!this.collapsed || this.el.hasActiveFx()) {
62131             return;
62132         }
62133         if(this.isSlid){
62134             this.afterSlideIn();
62135             skipAnim = true;
62136         }
62137         this.collapsed = false;
62138         if(this.config.animate && skipAnim !== true){
62139             this.animateExpand();
62140         }else{
62141             this.el.show();
62142             if(this.split){
62143                 this.split.el.show();
62144             }
62145             this.collapsedEl.setLocation(-2000,-2000);
62146             this.collapsedEl.hide();
62147             this.fireEvent("invalidated", this);
62148             this.fireEvent("expanded", this);
62149         }
62150     },
62151
62152     animateExpand : function(){
62153         // overridden
62154     },
62155
62156     initTabs : function()
62157     {
62158         this.bodyEl.setStyle("overflow", "hidden");
62159         var ts = new Roo.panel.Tab(
62160                 this.bodyEl.dom,
62161                 {
62162                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
62163                     disableTooltips: this.config.disableTabTips,
62164                     toolbar : this.config.toolbar
62165                 }
62166         );
62167         if(this.config.hideTabs){
62168             ts.stripWrap.setDisplayed(false);
62169         }
62170         this.tabs = ts;
62171         ts.resizeTabs = this.config.resizeTabs === true;
62172         ts.minTabWidth = this.config.minTabWidth || 40;
62173         ts.maxTabWidth = this.config.maxTabWidth || 250;
62174         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
62175         ts.monitorResize = false;
62176         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
62177         ts.bodyEl.addClass('x-layout-tabs-body');
62178         this.panels.each(this.initPanelAsTab, this);
62179     },
62180
62181     initPanelAsTab : function(panel){
62182         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
62183                     this.config.closeOnTab && panel.isClosable());
62184         if(panel.tabTip !== undefined){
62185             ti.setTooltip(panel.tabTip);
62186         }
62187         ti.on("activate", function(){
62188               this.setActivePanel(panel);
62189         }, this);
62190         if(this.config.closeOnTab){
62191             ti.on("beforeclose", function(t, e){
62192                 e.cancel = true;
62193                 this.remove(panel);
62194             }, this);
62195         }
62196         return ti;
62197     },
62198
62199     updatePanelTitle : function(panel, title){
62200         if(this.activePanel == panel){
62201             this.updateTitle(title);
62202         }
62203         if(this.tabs){
62204             var ti = this.tabs.getTab(panel.getEl().id);
62205             ti.setText(title);
62206             if(panel.tabTip !== undefined){
62207                 ti.setTooltip(panel.tabTip);
62208             }
62209         }
62210     },
62211
62212     updateTitle : function(title){
62213         if(this.titleTextEl && !this.config.title){
62214             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
62215         }
62216     },
62217
62218     setActivePanel : function(panel){
62219         panel = this.getPanel(panel);
62220         if(this.activePanel && this.activePanel != panel){
62221             this.activePanel.setActiveState(false);
62222         }
62223         this.activePanel = panel;
62224         panel.setActiveState(true);
62225         if(this.panelSize){
62226             panel.setSize(this.panelSize.width, this.panelSize.height);
62227         }
62228         if(this.closeBtn){
62229             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
62230         }
62231         this.updateTitle(panel.getTitle());
62232         if(this.tabs){
62233             this.fireEvent("invalidated", this);
62234         }
62235         this.fireEvent("panelactivated", this, panel);
62236     },
62237
62238     /**
62239      * Shows the specified panel.
62240      * @param {Number/String/panel.Content} panelId The panel's index, id or the panel itself
62241      * @return {Roo.panel.Content} The shown panel, or null if a panel could not be found from panelId
62242      */
62243     showPanel : function(panel)
62244     {
62245         panel = this.getPanel(panel);
62246         if(panel){
62247             if(this.tabs){
62248                 var tab = this.tabs.getTab(panel.getEl().id);
62249                 if(tab.isHidden()){
62250                     this.tabs.unhideTab(tab.id);
62251                 }
62252                 tab.activate();
62253             }else{
62254                 this.setActivePanel(panel);
62255             }
62256         }
62257         return panel;
62258     },
62259
62260     /**
62261      * Get the active panel for this region.
62262      * @return {Roo.panel.Content} The active panel or null
62263      */
62264     getActivePanel : function(){
62265         return this.activePanel;
62266     },
62267
62268     validateVisibility : function(){
62269         if(this.panels.getCount() < 1){
62270             this.updateTitle("&#160;");
62271             this.closeBtn.hide();
62272             this.hide();
62273         }else{
62274             if(!this.isVisible()){
62275                 this.show();
62276             }
62277         }
62278     },
62279
62280     /**
62281      * Adds the passed ContentPanel(s) to this region.
62282      * @param {panel.Content...} panel The ContentPanel(s) to add (you can pass more than one)
62283      * @return {Roo.panel.Content} The panel added (if only one was added; null otherwise)
62284      */
62285     add : function(panel){
62286         if(arguments.length > 1){
62287             for(var i = 0, len = arguments.length; i < len; i++) {
62288                 this.add(arguments[i]);
62289             }
62290             return null;
62291         }
62292         if(this.hasPanel(panel)){
62293             this.showPanel(panel);
62294             return panel;
62295         }
62296         panel.setRegion(this);
62297         this.panels.add(panel);
62298         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
62299             this.bodyEl.dom.appendChild(panel.getEl().dom);
62300             if(panel.background !== true){
62301                 this.setActivePanel(panel);
62302             }
62303             this.fireEvent("paneladded", this, panel);
62304             return panel;
62305         }
62306         if(!this.tabs){
62307             this.initTabs();
62308         }else{
62309             this.initPanelAsTab(panel);
62310         }
62311         if(panel.background !== true){
62312             this.tabs.activate(panel.getEl().id);
62313         }
62314         this.fireEvent("paneladded", this, panel);
62315         return panel;
62316     },
62317
62318     /**
62319      * Hides the tab for the specified panel.
62320      * @param {Number/String/panel.Content} panel The panel's index, id or the panel itself
62321      */
62322     hidePanel : function(panel){
62323         if(this.tabs && (panel = this.getPanel(panel))){
62324             this.tabs.hideTab(panel.getEl().id);
62325         }
62326     },
62327
62328     /**
62329      * Unhides the tab for a previously hidden panel.
62330      * @param {Number/String/panel.Content} panel The panel's index, id or the panel itself
62331      */
62332     unhidePanel : function(panel){
62333         if(this.tabs && (panel = this.getPanel(panel))){
62334             this.tabs.unhideTab(panel.getEl().id);
62335         }
62336     },
62337
62338     clearPanels : function(){
62339         while(this.panels.getCount() > 0){
62340              this.remove(this.panels.first());
62341         }
62342     },
62343
62344     /**
62345      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
62346      * @param {Number/String/panel.Content} panel The panel's index, id or the panel itself
62347      * @param {Boolean} preservePanel Overrides the config preservePanel option
62348      * @return {Roo.panel.Content} The panel that was removed
62349      */
62350     remove : function(panel, preservePanel){
62351         panel = this.getPanel(panel);
62352         if(!panel){
62353             return null;
62354         }
62355         var e = {};
62356         this.fireEvent("beforeremove", this, panel, e);
62357         if(e.cancel === true){
62358             return null;
62359         }
62360         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
62361         var panelId = panel.getId();
62362         this.panels.removeKey(panelId);
62363         if(preservePanel){
62364             document.body.appendChild(panel.getEl().dom);
62365         }
62366         if(this.tabs){
62367             this.tabs.removeTab(panel.getEl().id);
62368         }else if (!preservePanel){
62369             this.bodyEl.dom.removeChild(panel.getEl().dom);
62370         }
62371         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
62372             var p = this.panels.first();
62373             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
62374             tempEl.appendChild(p.getEl().dom);
62375             this.bodyEl.update("");
62376             this.bodyEl.dom.appendChild(p.getEl().dom);
62377             tempEl = null;
62378             this.updateTitle(p.getTitle());
62379             this.tabs = null;
62380             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
62381             this.setActivePanel(p);
62382         }
62383         panel.setRegion(null);
62384         if(this.activePanel == panel){
62385             this.activePanel = null;
62386         }
62387         if(this.config.autoDestroy !== false && preservePanel !== true){
62388             try{panel.destroy();}catch(e){}
62389         }
62390         this.fireEvent("panelremoved", this, panel);
62391         return panel;
62392     },
62393
62394     /**
62395      * Returns the TabPanel component used by this region
62396      * @return {Roo.panel.Tab}
62397      */
62398     getTabs : function(){
62399         return this.tabs;
62400     },
62401
62402     createTool : function(parentEl, className){
62403         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
62404             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
62405         btn.addClassOnOver("x-layout-tools-button-over");
62406         return btn;
62407     }
62408 });/*
62409  * Based on:
62410  * Ext JS Library 1.1.1
62411  * Copyright(c) 2006-2007, Ext JS, LLC.
62412  *
62413  * Originally Released Under LGPL - original licence link has changed is not relivant.
62414  *
62415  * Fork - LGPL
62416  * <script type="text/javascript">
62417  */
62418  
62419
62420
62421 /**
62422  * @class Roo.layout.SplitRegion
62423  * @extends Roo.layout.Region
62424  * Adds a splitbar and other (private) useful functionality to a {@link Roo.layout.Region}.
62425  */
62426 Roo.layout.SplitRegion = function(mgr, config, pos, cursor){
62427     this.cursor = cursor;
62428     Roo.layout.SplitRegion.superclass.constructor.call(this, mgr, config, pos);
62429 };
62430
62431 Roo.extend(Roo.layout.SplitRegion, Roo.layout.Region, {
62432     splitTip : "Drag to resize.",
62433     collapsibleSplitTip : "Drag to resize. Double click to hide.",
62434     useSplitTips : false,
62435
62436     applyConfig : function(config){
62437         Roo.layout.SplitRegion.superclass.applyConfig.call(this, config);
62438         if(config.split){
62439             if(!this.split){
62440                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
62441                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
62442                 /** The SplitBar for this region 
62443                 * @type Roo.SplitBar */
62444                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
62445                 this.split.on("moved", this.onSplitMove, this);
62446                 this.split.useShim = config.useShim === true;
62447                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
62448                 if(this.useSplitTips){
62449                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
62450                 }
62451                 if(config.collapsible){
62452                     this.split.el.on("dblclick", this.collapse,  this);
62453                 }
62454             }
62455             if(typeof config.minSize != "undefined"){
62456                 this.split.minSize = config.minSize;
62457             }
62458             if(typeof config.maxSize != "undefined"){
62459                 this.split.maxSize = config.maxSize;
62460             }
62461             if(config.hideWhenEmpty || config.hidden || config.collapsed){
62462                 this.hideSplitter();
62463             }
62464         }
62465     },
62466
62467     getHMaxSize : function(){
62468          var cmax = this.config.maxSize || 10000;
62469          var center = this.mgr.getRegion("center");
62470          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
62471     },
62472
62473     getVMaxSize : function(){
62474          var cmax = this.config.maxSize || 10000;
62475          var center = this.mgr.getRegion("center");
62476          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
62477     },
62478
62479     onSplitMove : function(split, newSize){
62480         this.fireEvent("resized", this, newSize);
62481     },
62482     
62483     /** 
62484      * Returns the {@link Roo.SplitBar} for this region.
62485      * @return {Roo.SplitBar}
62486      */
62487     getSplitBar : function(){
62488         return this.split;
62489     },
62490     
62491     hide : function(){
62492         this.hideSplitter();
62493         Roo.layout.SplitRegion.superclass.hide.call(this);
62494     },
62495
62496     hideSplitter : function(){
62497         if(this.split){
62498             this.split.el.setLocation(-2000,-2000);
62499             this.split.el.hide();
62500         }
62501     },
62502
62503     show : function(){
62504         if(this.split){
62505             this.split.el.show();
62506         }
62507         Roo.layout.SplitRegion.superclass.show.call(this);
62508     },
62509     
62510     beforeSlide: function(){
62511         if(Roo.isGecko){// firefox overflow auto bug workaround
62512             this.bodyEl.clip();
62513             if(this.tabs) {
62514                 this.tabs.bodyEl.clip();
62515             }
62516             if(this.activePanel){
62517                 this.activePanel.getEl().clip();
62518                 
62519                 if(this.activePanel.beforeSlide){
62520                     this.activePanel.beforeSlide();
62521                 }
62522             }
62523         }
62524     },
62525     
62526     afterSlide : function(){
62527         if(Roo.isGecko){// firefox overflow auto bug workaround
62528             this.bodyEl.unclip();
62529             if(this.tabs) {
62530                 this.tabs.bodyEl.unclip();
62531             }
62532             if(this.activePanel){
62533                 this.activePanel.getEl().unclip();
62534                 if(this.activePanel.afterSlide){
62535                     this.activePanel.afterSlide();
62536                 }
62537             }
62538         }
62539     },
62540
62541     initAutoHide : function(){
62542         if(this.autoHide !== false){
62543             if(!this.autoHideHd){
62544                 var st = new Roo.util.DelayedTask(this.slideIn, this);
62545                 this.autoHideHd = {
62546                     "mouseout": function(e){
62547                         if(!e.within(this.el, true)){
62548                             st.delay(500);
62549                         }
62550                     },
62551                     "mouseover" : function(e){
62552                         st.cancel();
62553                     },
62554                     scope : this
62555                 };
62556             }
62557             this.el.on(this.autoHideHd);
62558         }
62559     },
62560
62561     clearAutoHide : function(){
62562         if(this.autoHide !== false){
62563             this.el.un("mouseout", this.autoHideHd.mouseout);
62564             this.el.un("mouseover", this.autoHideHd.mouseover);
62565         }
62566     },
62567
62568     clearMonitor : function(){
62569         Roo.get(document).un("click", this.slideInIf, this);
62570     },
62571
62572     // these names are backwards but not changed for compat
62573     slideOut : function(){
62574         if(this.isSlid || this.el.hasActiveFx()){
62575             return;
62576         }
62577         this.isSlid = true;
62578         if(this.collapseBtn){
62579             this.collapseBtn.hide();
62580         }
62581         this.closeBtnState = this.closeBtn.getStyle('display');
62582         this.closeBtn.hide();
62583         if(this.stickBtn){
62584             this.stickBtn.show();
62585         }
62586         this.el.show();
62587         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
62588         this.beforeSlide();
62589         this.el.setStyle("z-index", 10001);
62590         this.el.slideIn(this.getSlideAnchor(), {
62591             callback: function(){
62592                 this.afterSlide();
62593                 this.initAutoHide();
62594                 Roo.get(document).on("click", this.slideInIf, this);
62595                 this.fireEvent("slideshow", this);
62596             },
62597             scope: this,
62598             block: true
62599         });
62600     },
62601
62602     afterSlideIn : function(){
62603         this.clearAutoHide();
62604         this.isSlid = false;
62605         this.clearMonitor();
62606         this.el.setStyle("z-index", "");
62607         if(this.collapseBtn){
62608             this.collapseBtn.show();
62609         }
62610         this.closeBtn.setStyle('display', this.closeBtnState);
62611         if(this.stickBtn){
62612             this.stickBtn.hide();
62613         }
62614         this.fireEvent("slidehide", this);
62615     },
62616
62617     slideIn : function(cb){
62618         if(!this.isSlid || this.el.hasActiveFx()){
62619             Roo.callback(cb);
62620             return;
62621         }
62622         this.isSlid = false;
62623         this.beforeSlide();
62624         this.el.slideOut(this.getSlideAnchor(), {
62625             callback: function(){
62626                 this.el.setLeftTop(-10000, -10000);
62627                 this.afterSlide();
62628                 this.afterSlideIn();
62629                 Roo.callback(cb);
62630             },
62631             scope: this,
62632             block: true
62633         });
62634     },
62635     
62636     slideInIf : function(e){
62637         if(!e.within(this.el)){
62638             this.slideIn();
62639         }
62640     },
62641
62642     animateCollapse : function(){
62643         this.beforeSlide();
62644         this.el.setStyle("z-index", 20000);
62645         var anchor = this.getSlideAnchor();
62646         this.el.slideOut(anchor, {
62647             callback : function(){
62648                 this.el.setStyle("z-index", "");
62649                 this.collapsedEl.slideIn(anchor, {duration:.3});
62650                 this.afterSlide();
62651                 this.el.setLocation(-10000,-10000);
62652                 this.el.hide();
62653                 this.fireEvent("collapsed", this);
62654             },
62655             scope: this,
62656             block: true
62657         });
62658     },
62659
62660     animateExpand : function(){
62661         this.beforeSlide();
62662         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
62663         this.el.setStyle("z-index", 20000);
62664         this.collapsedEl.hide({
62665             duration:.1
62666         });
62667         this.el.slideIn(this.getSlideAnchor(), {
62668             callback : function(){
62669                 this.el.setStyle("z-index", "");
62670                 this.afterSlide();
62671                 if(this.split){
62672                     this.split.el.show();
62673                 }
62674                 this.fireEvent("invalidated", this);
62675                 this.fireEvent("expanded", this);
62676             },
62677             scope: this,
62678             block: true
62679         });
62680     },
62681
62682     anchors : {
62683         "west" : "left",
62684         "east" : "right",
62685         "north" : "top",
62686         "south" : "bottom"
62687     },
62688
62689     sanchors : {
62690         "west" : "l",
62691         "east" : "r",
62692         "north" : "t",
62693         "south" : "b"
62694     },
62695
62696     canchors : {
62697         "west" : "tl-tr",
62698         "east" : "tr-tl",
62699         "north" : "tl-bl",
62700         "south" : "bl-tl"
62701     },
62702
62703     getAnchor : function(){
62704         return this.anchors[this.position];
62705     },
62706
62707     getCollapseAnchor : function(){
62708         return this.canchors[this.position];
62709     },
62710
62711     getSlideAnchor : function(){
62712         return this.sanchors[this.position];
62713     },
62714
62715     getAlignAdj : function(){
62716         var cm = this.cmargins;
62717         switch(this.position){
62718             case "west":
62719                 return [0, 0];
62720             break;
62721             case "east":
62722                 return [0, 0];
62723             break;
62724             case "north":
62725                 return [0, 0];
62726             break;
62727             case "south":
62728                 return [0, 0];
62729             break;
62730         }
62731     },
62732
62733     getExpandAdj : function(){
62734         var c = this.collapsedEl, cm = this.cmargins;
62735         switch(this.position){
62736             case "west":
62737                 return [-(cm.right+c.getWidth()+cm.left), 0];
62738             break;
62739             case "east":
62740                 return [cm.right+c.getWidth()+cm.left, 0];
62741             break;
62742             case "north":
62743                 return [0, -(cm.top+cm.bottom+c.getHeight())];
62744             break;
62745             case "south":
62746                 return [0, cm.top+cm.bottom+c.getHeight()];
62747             break;
62748         }
62749     }
62750 });/*
62751  * Based on:
62752  * Ext JS Library 1.1.1
62753  * Copyright(c) 2006-2007, Ext JS, LLC.
62754  *
62755  * Originally Released Under LGPL - original licence link has changed is not relivant.
62756  *
62757  * Fork - LGPL
62758  * <script type="text/javascript">
62759  */
62760 /*
62761  * These classes are private internal classes
62762  */
62763 Roo.layout.Center = function(mgr, config){
62764     Roo.layout.Region.call(this, mgr, config, "center");
62765     this.visible = true;
62766     this.minWidth = config.minWidth || 20;
62767     this.minHeight = config.minHeight || 20;
62768 };
62769
62770 Roo.extend(Roo.layout.Center, Roo.layout.Region, {
62771     hide : function(){
62772         // center panel can't be hidden
62773     },
62774     
62775     show : function(){
62776         // center panel can't be hidden
62777     },
62778     
62779     getMinWidth: function(){
62780         return this.minWidth;
62781     },
62782     
62783     getMinHeight: function(){
62784         return this.minHeight;
62785     }
62786 });
62787 Roo.layout.West = function(mgr, config){
62788     Roo.layout.SplitRegion.call(this, mgr, config, "west", "w-resize");
62789     if(this.split){
62790         this.split.placement = Roo.SplitBar.LEFT;
62791         this.split.orientation = Roo.SplitBar.HORIZONTAL;
62792         this.split.el.addClass("x-layout-split-h");
62793     }
62794     var size = config.initialSize || config.width;
62795     if(typeof size != "undefined"){
62796         this.el.setWidth(size);
62797     }
62798 };
62799 Roo.extend(Roo.layout.West, Roo.layout.SplitRegion, {
62800     orientation: Roo.SplitBar.HORIZONTAL,
62801     getBox : function(){
62802         if(this.collapsed){
62803             return this.collapsedEl.getBox();
62804         }
62805         var box = this.el.getBox();
62806         if(this.split){
62807             box.width += this.split.el.getWidth();
62808         }
62809         return box;
62810     },
62811     
62812     updateBox : function(box){
62813         if(this.split && !this.collapsed){
62814             var sw = this.split.el.getWidth();
62815             box.width -= sw;
62816             this.split.el.setLeft(box.x+box.width);
62817             this.split.el.setTop(box.y);
62818             this.split.el.setHeight(box.height);
62819         }
62820         if(this.collapsed){
62821             this.updateBody(null, box.height);
62822         }
62823         Roo.layout.Region.prototype.updateBox.call(this, box);
62824     }
62825 });
62826 Roo.layout.East = function(mgr, config){
62827     Roo.layout.SplitRegion.call(this, mgr, config, "east", "e-resize");
62828     if(this.split){
62829         this.split.placement = Roo.SplitBar.RIGHT;
62830         this.split.orientation = Roo.SplitBar.HORIZONTAL;
62831         this.split.el.addClass("x-layout-split-h");
62832     }
62833     var size = config.initialSize || config.width;
62834     if(typeof size != "undefined"){
62835         this.el.setWidth(size);
62836     }
62837 };
62838 Roo.extend(Roo.layout.East, Roo.layout.SplitRegion, {
62839     orientation: Roo.SplitBar.HORIZONTAL,
62840     getBox : function(){
62841         if(this.collapsed){
62842             return this.collapsedEl.getBox();
62843         }
62844         var box = this.el.getBox();
62845         if(this.split){
62846             var sw = this.split.el.getWidth();
62847             box.width += sw;
62848             box.x -= sw;
62849         }
62850         return box;
62851     },
62852
62853     updateBox : function(box){
62854         if(this.split && !this.collapsed){
62855             var sw = this.split.el.getWidth();
62856             box.width -= sw;
62857             this.split.el.setLeft(box.x);
62858             this.split.el.setTop(box.y);
62859             this.split.el.setHeight(box.height);
62860             box.x += sw;
62861         }
62862         if(this.collapsed){
62863             this.updateBody(null, box.height);
62864         }
62865         Roo.layout.Region.prototype.updateBox.call(this, box);
62866     }
62867 });Roo.layout.South = function(mgr, config){
62868     Roo.layout.SplitRegion.call(this, mgr, config, "south", "s-resize");
62869     if(this.split){
62870         this.split.placement = Roo.SplitBar.BOTTOM;
62871         this.split.orientation = Roo.SplitBar.VERTICAL;
62872         this.split.el.addClass("x-layout-split-v");
62873     }
62874     var size = config.initialSize || config.height;
62875     if(typeof size != "undefined"){
62876         this.el.setHeight(size);
62877     }
62878 };
62879 Roo.extend(Roo.layout.South, Roo.layout.SplitRegion, {
62880     orientation: Roo.SplitBar.VERTICAL,
62881     getBox : function(){
62882         if(this.collapsed){
62883             return this.collapsedEl.getBox();
62884         }
62885         var box = this.el.getBox();
62886         if(this.split){
62887             var sh = this.split.el.getHeight();
62888             box.height += sh;
62889             box.y -= sh;
62890         }
62891         return box;
62892     },
62893     
62894     updateBox : function(box){
62895         if(this.split && !this.collapsed){
62896             var sh = this.split.el.getHeight();
62897             box.height -= sh;
62898             box.y += sh;
62899             this.split.el.setLeft(box.x);
62900             this.split.el.setTop(box.y-sh);
62901             this.split.el.setWidth(box.width);
62902         }
62903         if(this.collapsed){
62904             this.updateBody(box.width, null);
62905         }
62906         Roo.layout.Region.prototype.updateBox.call(this, box);
62907     }
62908 });
62909
62910
62911 Roo.layout.North = function(mgr, config){
62912     Roo.layout.Region.call(this, mgr, config, "north", "n-resize");
62913     if(this.split){
62914         this.split.placement = Roo.SplitBar.TOP;
62915         this.split.orientation = Roo.SplitBar.VERTICAL;
62916         this.split.el.addClass("x-layout-split-v");
62917     }
62918     var size = config.initialSize || config.height;
62919     if(typeof size != "undefined"){
62920         this.el.setHeight(size);
62921     }
62922 };
62923 Roo.extend(Roo.layout.North, Roo.layout.SplitRegion, {
62924     orientation: Roo.SplitBar.VERTICAL,
62925     getBox : function(){
62926         if(this.collapsed){
62927             return this.collapsedEl.getBox();
62928         }
62929         var box = this.el.getBox();
62930         if(this.split){
62931             box.height += this.split.el.getHeight();
62932         }
62933         return box;
62934     },
62935     
62936     updateBox : function(box){
62937         if(this.split && !this.collapsed){
62938             box.height -= this.split.el.getHeight();
62939             this.split.el.setLeft(box.x);
62940             this.split.el.setTop(box.y+box.height);
62941             this.split.el.setWidth(box.width);
62942         }
62943         if(this.collapsed){
62944             this.updateBody(box.width, null);
62945         }
62946         Roo.layout.Region.prototype.updateBox.call(this, box);
62947     }
62948 });/*
62949  * Based on:
62950  * Ext JS Library 1.1.1
62951  * Copyright(c) 2006-2007, Ext JS, LLC.
62952  *
62953  * Originally Released Under LGPL - original licence link has changed is not relivant.
62954  *
62955  * Fork - LGPL
62956  * <script type="text/javascript">
62957  */
62958  
62959  
62960 /*
62961  * Private internal class for reading and applying state
62962  */
62963 Roo.layout.StateManager = function(layout){
62964      // default empty state
62965      this.state = {
62966         north: {},
62967         south: {},
62968         east: {},
62969         west: {}       
62970     };
62971 };
62972
62973 Roo.layout.StateManager.prototype = {
62974     init : function(layout, provider){
62975         this.provider = provider;
62976         var state = provider.get(layout.id+"-layout-state");
62977         if(state){
62978             var wasUpdating = layout.isUpdating();
62979             if(!wasUpdating){
62980                 layout.beginUpdate();
62981             }
62982             for(var key in state){
62983                 if(typeof state[key] != "function"){
62984                     var rstate = state[key];
62985                     var r = layout.getRegion(key);
62986                     if(r && rstate){
62987                         if(rstate.size){
62988                             r.resizeTo(rstate.size);
62989                         }
62990                         if(rstate.collapsed == true){
62991                             r.collapse(true);
62992                         }else{
62993                             r.expand(null, true);
62994                         }
62995                     }
62996                 }
62997             }
62998             if(!wasUpdating){
62999                 layout.endUpdate();
63000             }
63001             this.state = state; 
63002         }
63003         this.layout = layout;
63004         layout.on("regionresized", this.onRegionResized, this);
63005         layout.on("regioncollapsed", this.onRegionCollapsed, this);
63006         layout.on("regionexpanded", this.onRegionExpanded, this);
63007     },
63008     
63009     storeState : function(){
63010         this.provider.set(this.layout.id+"-layout-state", this.state);
63011     },
63012     
63013     onRegionResized : function(region, newSize){
63014         this.state[region.getPosition()].size = newSize;
63015         this.storeState();
63016     },
63017     
63018     onRegionCollapsed : function(region){
63019         this.state[region.getPosition()].collapsed = true;
63020         this.storeState();
63021     },
63022     
63023     onRegionExpanded : function(region){
63024         this.state[region.getPosition()].collapsed = false;
63025         this.storeState();
63026     }
63027 };/*
63028  * Based on:
63029  * Ext JS Library 1.1.1
63030  * Copyright(c) 2006-2007, Ext JS, LLC.
63031  *
63032  * Originally Released Under LGPL - original licence link has changed is not relivant.
63033  *
63034  * Fork - LGPL
63035  * <script type="text/javascript">
63036  */
63037 /**
63038  * @class Roo.panel.Content
63039  * @extends Roo.util.Observable
63040  * @children Roo.form.Form Roo.JsonView Roo.View
63041  * @parent Roo.layout.Border Roo.LayoutDialog builder
63042  * A basic Content Panel element.
63043  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
63044  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
63045  * @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
63046  * @cfg {Boolean}   closable      True if the panel can be closed/removed
63047  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
63048  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
63049  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
63050  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
63051  * @cfg {String} title          The title for this panel
63052  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
63053  * @cfg {String} url            Calls {@link #setUrl} with this value
63054  * @cfg {String} region (center|north|south|east|west) [required] which region to put this panel on (when used with xtype constructors)
63055  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
63056  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
63057  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
63058  * @cfg {String}    style  Extra style to add to the content panel
63059  * @cfg {Roo.menu.Menu} menu  popup menu
63060
63061  * @constructor
63062  * Create a new Content Panel.
63063  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
63064  * @param {String/Object} config A string to set only the title or a config object
63065  * @param {String} content (optional) Set the HTML content for this panel
63066  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
63067  */
63068 Roo.panel.Content = function(el, config, content){
63069     
63070     /*
63071     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
63072         config = el;
63073         el = Roo.id();
63074     }
63075     if (config && config.parentLayout) { 
63076         el = config.parentLayout.el.createChild(); 
63077     }
63078     */
63079     if(el.autoCreate){ // xtype is available if this is called from factory
63080         config = el;
63081         el = Roo.id();
63082     }
63083     this.el = Roo.get(el);
63084     if(!this.el && config && config.autoCreate){
63085         if(typeof config.autoCreate == "object"){
63086             if(!config.autoCreate.id){
63087                 config.autoCreate.id = config.id||el;
63088             }
63089             this.el = Roo.DomHelper.append(document.body,
63090                         config.autoCreate, true);
63091         }else{
63092             this.el = Roo.DomHelper.append(document.body,
63093                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
63094         }
63095     }
63096     
63097     
63098     this.closable = false;
63099     this.loaded = false;
63100     this.active = false;
63101     if(typeof config == "string"){
63102         this.title = config;
63103     }else{
63104         Roo.apply(this, config);
63105     }
63106     
63107     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
63108         this.wrapEl = this.el.wrap();
63109         this.toolbar.container = this.el.insertSibling(false, 'before');
63110         this.toolbar = new Roo.Toolbar(this.toolbar);
63111     }
63112     
63113     // xtype created footer. - not sure if will work as we normally have to render first..
63114     if (this.footer && !this.footer.el && this.footer.xtype) {
63115         if (!this.wrapEl) {
63116             this.wrapEl = this.el.wrap();
63117         }
63118     
63119         this.footer.container = this.wrapEl.createChild();
63120          
63121         this.footer = Roo.factory(this.footer, Roo);
63122         
63123     }
63124     
63125     if(this.resizeEl){
63126         this.resizeEl = Roo.get(this.resizeEl, true);
63127     }else{
63128         this.resizeEl = this.el;
63129     }
63130     // handle view.xtype
63131     
63132  
63133     
63134     
63135     this.addEvents({
63136         /**
63137          * @event activate
63138          * Fires when this panel is activated. 
63139          * @param {Roo.panel.Content} this
63140          */
63141         "activate" : true,
63142         /**
63143          * @event deactivate
63144          * Fires when this panel is activated. 
63145          * @param {Roo.panel.Content} this
63146          */
63147         "deactivate" : true,
63148
63149         /**
63150          * @event resize
63151          * Fires when this panel is resized if fitToFrame is true.
63152          * @param {Roo.panel.Content} this
63153          * @param {Number} width The width after any component adjustments
63154          * @param {Number} height The height after any component adjustments
63155          */
63156         "resize" : true,
63157         
63158          /**
63159          * @event render
63160          * Fires when this tab is created
63161          * @param {Roo.panel.Content} this
63162          */
63163         "render" : true
63164          
63165         
63166     });
63167     
63168
63169     
63170     
63171     if(this.autoScroll){
63172         this.resizeEl.setStyle("overflow", "auto");
63173     } else {
63174         // fix randome scrolling
63175         this.el.on('scroll', function() {
63176             Roo.log('fix random scolling');
63177             this.scrollTo('top',0); 
63178         });
63179     }
63180     content = content || this.content;
63181     if(content){
63182         this.setContent(content);
63183     }
63184     if(config && config.url){
63185         this.setUrl(this.url, this.params, this.loadOnce);
63186     }
63187     
63188     
63189     
63190     Roo.panel.Content.superclass.constructor.call(this);
63191     
63192     if (this.view && typeof(this.view.xtype) != 'undefined') {
63193         this.view.el = this.el.appendChild(document.createElement("div"));
63194         this.view = Roo.factory(this.view); 
63195         this.view.render  &&  this.view.render(false, '');  
63196     }
63197     
63198     
63199     this.fireEvent('render', this);
63200 };
63201
63202 Roo.extend(Roo.panel.Content, Roo.util.Observable, {
63203     tabTip:'',
63204     setRegion : function(region){
63205         this.region = region;
63206         if(region){
63207            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
63208         }else{
63209            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
63210         } 
63211     },
63212     
63213     /**
63214      * Returns the toolbar for this Panel if one was configured. 
63215      * @return {Roo.Toolbar} 
63216      */
63217     getToolbar : function(){
63218         return this.toolbar;
63219     },
63220     
63221     setActiveState : function(active){
63222         this.active = active;
63223         if(!active){
63224             this.fireEvent("deactivate", this);
63225         }else{
63226             this.fireEvent("activate", this);
63227         }
63228     },
63229     /**
63230      * Updates this panel's element
63231      * @param {String} content The new content
63232      * @param {Boolean} loadScripts (optional) true to look for and process scripts
63233     */
63234     setContent : function(content, loadScripts){
63235         this.el.update(content, loadScripts);
63236     },
63237
63238     ignoreResize : function(w, h){
63239         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
63240             return true;
63241         }else{
63242             this.lastSize = {width: w, height: h};
63243             return false;
63244         }
63245     },
63246     /**
63247      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
63248      * @return {Roo.UpdateManager} The UpdateManager
63249      */
63250     getUpdateManager : function(){
63251         return this.el.getUpdateManager();
63252     },
63253      /**
63254      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
63255      * @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:
63256 <pre><code>
63257 panel.load({
63258     url: "your-url.php",
63259     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
63260     callback: yourFunction,
63261     scope: yourObject, //(optional scope)
63262     discardUrl: false,
63263     nocache: false,
63264     text: "Loading...",
63265     timeout: 30,
63266     scripts: false
63267 });
63268 </code></pre>
63269      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
63270      * 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.
63271      * @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}
63272      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
63273      * @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.
63274      * @return {Roo.panel.Content} this
63275      */
63276     load : function(){
63277         var um = this.el.getUpdateManager();
63278         um.update.apply(um, arguments);
63279         return this;
63280     },
63281
63282
63283     /**
63284      * 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.
63285      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
63286      * @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)
63287      * @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)
63288      * @return {Roo.UpdateManager} The UpdateManager
63289      */
63290     setUrl : function(url, params, loadOnce){
63291         if(this.refreshDelegate){
63292             this.removeListener("activate", this.refreshDelegate);
63293         }
63294         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
63295         this.on("activate", this.refreshDelegate);
63296         return this.el.getUpdateManager();
63297     },
63298     
63299     _handleRefresh : function(url, params, loadOnce){
63300         if(!loadOnce || !this.loaded){
63301             var updater = this.el.getUpdateManager();
63302             updater.update(url, params, this._setLoaded.createDelegate(this));
63303         }
63304     },
63305     
63306     _setLoaded : function(){
63307         this.loaded = true;
63308     }, 
63309     
63310     /**
63311      * Returns this panel's id
63312      * @return {String} 
63313      */
63314     getId : function(){
63315         return this.el.id;
63316     },
63317     
63318     /** 
63319      * Returns this panel's element - used by regiosn to add.
63320      * @return {Roo.Element} 
63321      */
63322     getEl : function(){
63323         return this.wrapEl || this.el;
63324     },
63325     
63326     adjustForComponents : function(width, height)
63327     {
63328         //Roo.log('adjustForComponents ');
63329         if(this.resizeEl != this.el){
63330             width -= this.el.getFrameWidth('lr');
63331             height -= this.el.getFrameWidth('tb');
63332         }
63333         if(this.toolbar){
63334             var te = this.toolbar.getEl();
63335             height -= te.getHeight();
63336             te.setWidth(width);
63337         }
63338         if(this.footer){
63339             var te = this.footer.getEl();
63340             //Roo.log("footer:" + te.getHeight());
63341             
63342             height -= te.getHeight();
63343             te.setWidth(width);
63344         }
63345         
63346         
63347         if(this.adjustments){
63348             width += this.adjustments[0];
63349             height += this.adjustments[1];
63350         }
63351         return {"width": width, "height": height};
63352     },
63353     
63354     setSize : function(width, height){
63355         if(this.fitToFrame && !this.ignoreResize(width, height)){
63356             if(this.fitContainer && this.resizeEl != this.el){
63357                 this.el.setSize(width, height);
63358             }
63359             var size = this.adjustForComponents(width, height);
63360             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
63361             this.fireEvent('resize', this, size.width, size.height);
63362         }
63363     },
63364     
63365     /**
63366      * Returns this panel's title
63367      * @return {String} 
63368      */
63369     getTitle : function(){
63370         return this.title;
63371     },
63372     
63373     /**
63374      * Set this panel's title
63375      * @param {String} title
63376      */
63377     setTitle : function(title){
63378         this.title = title;
63379         if(this.region){
63380             this.region.updatePanelTitle(this, title);
63381         }
63382     },
63383     
63384     /**
63385      * Returns true is this panel was configured to be closable
63386      * @return {Boolean} 
63387      */
63388     isClosable : function(){
63389         return this.closable;
63390     },
63391     
63392     beforeSlide : function(){
63393         this.el.clip();
63394         this.resizeEl.clip();
63395     },
63396     
63397     afterSlide : function(){
63398         this.el.unclip();
63399         this.resizeEl.unclip();
63400     },
63401     
63402     /**
63403      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
63404      *   Will fail silently if the {@link #setUrl} method has not been called.
63405      *   This does not activate the panel, just updates its content.
63406      */
63407     refresh : function(){
63408         if(this.refreshDelegate){
63409            this.loaded = false;
63410            this.refreshDelegate();
63411         }
63412     },
63413     
63414     /**
63415      * Destroys this panel
63416      */
63417     destroy : function(){
63418         this.el.removeAllListeners();
63419         var tempEl = document.createElement("span");
63420         tempEl.appendChild(this.el.dom);
63421         tempEl.innerHTML = "";
63422         this.el.remove();
63423         this.el = null;
63424     },
63425     
63426     /**
63427      * form - if the content panel contains a form - this is a reference to it.
63428      * @type {Roo.form.Form}
63429      */
63430     form : false,
63431     /**
63432      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
63433      *    This contains a reference to it.
63434      * @type {Roo.View}
63435      */
63436     view : false,
63437     
63438       /**
63439      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
63440      * <pre><code>
63441
63442 layout.addxtype({
63443        xtype : 'Form',
63444        items: [ .... ]
63445    }
63446 );
63447
63448 </code></pre>
63449      * @param {Object} cfg Xtype definition of item to add.
63450      */
63451     
63452     addxtype : function(cfg) {
63453         if(cfg.xtype.match(/^Cropbox$/)) {
63454
63455             this.cropbox = new Roo.factory(cfg);
63456
63457             this.cropbox.render(this.el);
63458
63459             return this.cropbox;
63460         }
63461         // add form..
63462         if (cfg.xtype.match(/^Form$/)) {
63463             
63464             var el;
63465             //if (this.footer) {
63466             //    el = this.footer.container.insertSibling(false, 'before');
63467             //} else {
63468                 el = this.el.createChild();
63469             //}
63470
63471             this.form = new  Roo.form.Form(cfg);
63472             
63473             
63474             if ( this.form.allItems.length) {
63475                 this.form.render(el.dom);
63476             }
63477             return this.form;
63478         }
63479         // should only have one of theses..
63480         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
63481             // views.. should not be just added - used named prop 'view''
63482             
63483             cfg.el = this.el.appendChild(document.createElement("div"));
63484             // factory?
63485             
63486             var ret = new Roo.factory(cfg);
63487              
63488              ret.render && ret.render(false, ''); // render blank..
63489             this.view = ret;
63490             return ret;
63491         }
63492         return false;
63493     }
63494 });
63495
63496
63497
63498
63499
63500
63501
63502
63503
63504
63505
63506
63507 /**
63508  * @class Roo.panel.Grid
63509  * @extends Roo.panel.Content
63510  * @parent Roo.layout.Border Roo.LayoutDialog builder
63511  * @constructor
63512  * Create a new GridPanel.
63513  * @cfg {Roo.grid.Grid} grid The grid for this panel
63514  */
63515 Roo.panel.Grid = function(grid, config){
63516     
63517     // universal ctor...
63518     if (typeof(grid.grid) != 'undefined') {
63519         config = grid;
63520         grid = config.grid;
63521     }
63522     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
63523         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
63524         
63525     this.wrapper.dom.appendChild(grid.getGridEl().dom);
63526     
63527     Roo.panel.Grid.superclass.constructor.call(this, this.wrapper, config);
63528     
63529     if(this.toolbar){
63530         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
63531     }
63532     // xtype created footer. - not sure if will work as we normally have to render first..
63533     if (this.footer && !this.footer.el && this.footer.xtype) {
63534         
63535         this.footer.container = this.grid.getView().getFooterPanel(true);
63536         this.footer.dataSource = this.grid.dataSource;
63537         this.footer = Roo.factory(this.footer, Roo);
63538         
63539     }
63540     
63541     grid.monitorWindowResize = false; // turn off autosizing
63542     grid.autoHeight = false;
63543     grid.autoWidth = false;
63544     this.grid = grid;
63545     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
63546 };
63547
63548 Roo.extend(Roo.panel.Grid, Roo.panel.Content, {
63549     getId : function(){
63550         return this.grid.id;
63551     },
63552     
63553     /**
63554      * Returns the grid for this panel
63555      * @return {Roo.grid.Grid} 
63556      */
63557     getGrid : function(){
63558         return this.grid;    
63559     },
63560     
63561     setSize : function(width, height){
63562         if(!this.ignoreResize(width, height)){
63563             var grid = this.grid;
63564             var size = this.adjustForComponents(width, height);
63565             grid.getGridEl().setSize(size.width, size.height);
63566             grid.autoSize();
63567         }
63568     },
63569     
63570     beforeSlide : function(){
63571         this.grid.getView().scroller.clip();
63572     },
63573     
63574     afterSlide : function(){
63575         this.grid.getView().scroller.unclip();
63576     },
63577     
63578     destroy : function(){
63579         this.grid.destroy();
63580         delete this.grid;
63581         Roo.panel.Grid.superclass.destroy.call(this); 
63582     }
63583 });
63584
63585
63586 /**
63587  * @class Roo.panel.NestedLayout
63588  * @extends Roo.panel.Content
63589  * @parent Roo.layout.Border Roo.LayoutDialog builder
63590  * @cfg {Roo.layout.Border} layout   [required] The layout for this panel
63591  *
63592  * 
63593  * @constructor
63594  * Create a new NestedLayoutPanel.
63595  * 
63596  * 
63597  * @param {Roo.layout.Border} layout [required] The layout for this panel
63598  * @param {String/Object} config A string to set only the title or a config object
63599  */
63600 Roo.panel.NestedLayout = function(layout, config)
63601 {
63602     // construct with only one argument..
63603     /* FIXME - implement nicer consturctors
63604     if (layout.layout) {
63605         config = layout;
63606         layout = config.layout;
63607         delete config.layout;
63608     }
63609     if (layout.xtype && !layout.getEl) {
63610         // then layout needs constructing..
63611         layout = Roo.factory(layout, Roo);
63612     }
63613     */
63614     
63615     
63616     Roo.panel.NestedLayout.superclass.constructor.call(this, layout.getEl(), config);
63617     
63618     layout.monitorWindowResize = false; // turn off autosizing
63619     this.layout = layout;
63620     this.layout.getEl().addClass("x-layout-nested-layout");
63621     
63622     
63623     
63624     
63625 };
63626
63627 Roo.extend(Roo.panel.NestedLayout, Roo.panel.Content, {
63628
63629     layout : false,
63630
63631     setSize : function(width, height){
63632         if(!this.ignoreResize(width, height)){
63633             var size = this.adjustForComponents(width, height);
63634             var el = this.layout.getEl();
63635             el.setSize(size.width, size.height);
63636             var touch = el.dom.offsetWidth;
63637             this.layout.layout();
63638             // ie requires a double layout on the first pass
63639             if(Roo.isIE && !this.initialized){
63640                 this.initialized = true;
63641                 this.layout.layout();
63642             }
63643         }
63644     },
63645     
63646     // activate all subpanels if not currently active..
63647     
63648     setActiveState : function(active){
63649         this.active = active;
63650         if(!active){
63651             this.fireEvent("deactivate", this);
63652             return;
63653         }
63654         
63655         this.fireEvent("activate", this);
63656         // not sure if this should happen before or after..
63657         if (!this.layout) {
63658             return; // should not happen..
63659         }
63660         var reg = false;
63661         for (var r in this.layout.regions) {
63662             reg = this.layout.getRegion(r);
63663             if (reg.getActivePanel()) {
63664                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
63665                 reg.setActivePanel(reg.getActivePanel());
63666                 continue;
63667             }
63668             if (!reg.panels.length) {
63669                 continue;
63670             }
63671             reg.showPanel(reg.getPanel(0));
63672         }
63673         
63674         
63675         
63676         
63677     },
63678     
63679     /**
63680      * Returns the nested BorderLayout for this panel
63681      * @return {Roo.layout.Border}
63682      */
63683     getLayout : function(){
63684         return this.layout;
63685     },
63686     
63687      /**
63688      * Adds a xtype elements to the layout of the nested panel
63689      * <pre><code>
63690
63691 panel.addxtype({
63692        xtype : 'ContentPanel',
63693        region: 'west',
63694        items: [ .... ]
63695    }
63696 );
63697
63698 panel.addxtype({
63699         xtype : 'panel.NestedLayout',
63700         region: 'west',
63701         layout: {
63702            center: { },
63703            west: { }   
63704         },
63705         items : [ ... list of content panels or nested layout panels.. ]
63706    }
63707 );
63708 </code></pre>
63709      * @param {Object} cfg Xtype definition of item to add.
63710      */
63711     addxtype : function(cfg) {
63712         return this.layout.addxtype(cfg);
63713     
63714     }
63715 });
63716
63717 Roo.panel.Scroll = function(el, config, content){
63718     config = config || {};
63719     config.fitToFrame = true;
63720     Roo.panel.Scroll.superclass.constructor.call(this, el, config, content);
63721     
63722     this.el.dom.style.overflow = "hidden";
63723     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
63724     this.el.removeClass("x-layout-inactive-content");
63725     this.el.on("mousewheel", this.onWheel, this);
63726
63727     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
63728     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
63729     up.unselectable(); down.unselectable();
63730     up.on("click", this.scrollUp, this);
63731     down.on("click", this.scrollDown, this);
63732     up.addClassOnOver("x-scroller-btn-over");
63733     down.addClassOnOver("x-scroller-btn-over");
63734     up.addClassOnClick("x-scroller-btn-click");
63735     down.addClassOnClick("x-scroller-btn-click");
63736     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
63737
63738     this.resizeEl = this.el;
63739     this.el = wrap; this.up = up; this.down = down;
63740 };
63741
63742 Roo.extend(Roo.panel.Scroll, Roo.panel.Content, {
63743     increment : 100,
63744     wheelIncrement : 5,
63745     scrollUp : function(){
63746         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
63747     },
63748
63749     scrollDown : function(){
63750         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
63751     },
63752
63753     afterScroll : function(){
63754         var el = this.resizeEl;
63755         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
63756         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
63757         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
63758     },
63759
63760     setSize : function(){
63761         Roo.panel.Scroll.superclass.setSize.apply(this, arguments);
63762         this.afterScroll();
63763     },
63764
63765     onWheel : function(e){
63766         var d = e.getWheelDelta();
63767         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
63768         this.afterScroll();
63769         e.stopEvent();
63770     },
63771
63772     setContent : function(content, loadScripts){
63773         this.resizeEl.update(content, loadScripts);
63774     }
63775
63776 });
63777
63778
63779
63780 /**
63781  * @class Roo.panel.Tree
63782  * @extends Roo.panel.Content
63783  * @parent Roo.layout.Border Roo.LayoutDialog builder
63784  * Treepanel component
63785  * 
63786  * @constructor
63787  * Create a new TreePanel. - defaults to fit/scoll contents.
63788  * @param {String/Object} config A string to set only the panel's title, or a config object
63789  */
63790 Roo.panel.Tree = function(config){
63791     var el = config.el;
63792     var tree = config.tree;
63793     delete config.tree; 
63794     delete config.el; // hopefull!
63795     
63796     // wrapper for IE7 strict & safari scroll issue
63797     
63798     var treeEl = el.createChild();
63799     config.resizeEl = treeEl;
63800     
63801     
63802     
63803     Roo.panel.Tree.superclass.constructor.call(this, el, config);
63804  
63805  
63806     this.tree = new Roo.tree.TreePanel(treeEl , tree);
63807     //console.log(tree);
63808     this.on('activate', function()
63809     {
63810         if (this.tree.rendered) {
63811             return;
63812         }
63813         //console.log('render tree');
63814         this.tree.render();
63815     });
63816     // this should not be needed.. - it's actually the 'el' that resizes?
63817     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
63818     
63819     //this.on('resize',  function (cp, w, h) {
63820     //        this.tree.innerCt.setWidth(w);
63821     //        this.tree.innerCt.setHeight(h);
63822     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
63823     //});
63824
63825         
63826     
63827 };
63828
63829 Roo.extend(Roo.panel.Tree, Roo.panel.Content, {   
63830     fitToFrame : true,
63831     autoScroll : true,
63832     /*
63833      * @cfg {Roo.tree.panel.Tree} tree [required] The tree TreePanel, with config etc.
63834      */
63835     tree : false
63836
63837 });
63838 /*
63839  * Based on:
63840  * Ext JS Library 1.1.1
63841  * Copyright(c) 2006-2007, Ext JS, LLC.
63842  *
63843  * Originally Released Under LGPL - original licence link has changed is not relivant.
63844  *
63845  * Fork - LGPL
63846  * <script type="text/javascript">
63847  */
63848  
63849
63850 /**
63851  * @class Roo.layout.Reader
63852  * @extends Roo.layout.Border
63853  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
63854  * center region containing two nested regions (a top one for a list view and one for item preview below),
63855  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
63856  * The setup and configuration work exactly the same as it does for a {@link Roo.layout.Border} - this class simply
63857  * expedites the setup of the overall layout and regions for this common application style.
63858  * Example:
63859  <pre><code>
63860 var reader = new Roo.layout.Reader();
63861 var CP = Roo.panel.Content;  // shortcut for adding
63862
63863 reader.beginUpdate();
63864 reader.add("north", new CP("north", "North"));
63865 reader.add("west", new CP("west", {title: "West"}));
63866 reader.add("east", new CP("east", {title: "East"}));
63867
63868 reader.regions.listView.add(new CP("listView", "List"));
63869 reader.regions.preview.add(new CP("preview", "Preview"));
63870 reader.endUpdate();
63871 </code></pre>
63872 * @constructor
63873 * Create a new ReaderLayout
63874 * @param {Object} config Configuration options
63875 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
63876 * document.body if omitted)
63877 */
63878 Roo.layout.Reader = function(config, renderTo){
63879     var c = config || {size:{}};
63880     Roo.layout.Reader.superclass.constructor.call(this, renderTo || document.body, {
63881         north: c.north !== false ? Roo.apply({
63882             split:false,
63883             initialSize: 32,
63884             titlebar: false
63885         }, c.north) : false,
63886         west: c.west !== false ? Roo.apply({
63887             split:true,
63888             initialSize: 200,
63889             minSize: 175,
63890             maxSize: 400,
63891             titlebar: true,
63892             collapsible: true,
63893             animate: true,
63894             margins:{left:5,right:0,bottom:5,top:5},
63895             cmargins:{left:5,right:5,bottom:5,top:5}
63896         }, c.west) : false,
63897         east: c.east !== false ? Roo.apply({
63898             split:true,
63899             initialSize: 200,
63900             minSize: 175,
63901             maxSize: 400,
63902             titlebar: true,
63903             collapsible: true,
63904             animate: true,
63905             margins:{left:0,right:5,bottom:5,top:5},
63906             cmargins:{left:5,right:5,bottom:5,top:5}
63907         }, c.east) : false,
63908         center: Roo.apply({
63909             tabPosition: 'top',
63910             autoScroll:false,
63911             closeOnTab: true,
63912             titlebar:false,
63913             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
63914         }, c.center)
63915     });
63916
63917     this.el.addClass('x-reader');
63918
63919     this.beginUpdate();
63920
63921     var inner = new Roo.layout.Border(Roo.get(document.body).createChild(), {
63922         south: c.preview !== false ? Roo.apply({
63923             split:true,
63924             initialSize: 200,
63925             minSize: 100,
63926             autoScroll:true,
63927             collapsible:true,
63928             titlebar: true,
63929             cmargins:{top:5,left:0, right:0, bottom:0}
63930         }, c.preview) : false,
63931         center: Roo.apply({
63932             autoScroll:false,
63933             titlebar:false,
63934             minHeight:200
63935         }, c.listView)
63936     });
63937     this.add('center', new Roo.panel.NestedLayout(inner,
63938             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
63939
63940     this.endUpdate();
63941
63942     this.regions.preview = inner.getRegion('south');
63943     this.regions.listView = inner.getRegion('center');
63944 };
63945
63946 Roo.extend(Roo.layout.Reader, Roo.layout.Border);/*
63947  * Based on:
63948  * Ext JS Library 1.1.1
63949  * Copyright(c) 2006-2007, Ext JS, LLC.
63950  *
63951  * Originally Released Under LGPL - original licence link has changed is not relivant.
63952  *
63953  * Fork - LGPL
63954  * <script type="text/javascript">
63955  */
63956  
63957 /**
63958  * @class Roo.grid.Grid
63959  * @extends Roo.util.Observable
63960  * This class represents the primary interface of a component based grid control.
63961  * <br><br>Usage:<pre><code>
63962  var grid = new Roo.grid.Grid("my-container-id", {
63963      ds: myDataStore,
63964      cm: myColModel,
63965      selModel: mySelectionModel,
63966      autoSizeColumns: true,
63967      monitorWindowResize: false,
63968      trackMouseOver: true
63969  });
63970  // set any options
63971  grid.render();
63972  * </code></pre>
63973  * <b>Common Problems:</b><br/>
63974  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
63975  * element will correct this<br/>
63976  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
63977  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
63978  * are unpredictable.<br/>
63979  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
63980  * grid to calculate dimensions/offsets.<br/>
63981   * @constructor
63982  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
63983  * The container MUST have some type of size defined for the grid to fill. The container will be
63984  * automatically set to position relative if it isn't already.
63985  * @param {Object} config A config object that sets properties on this grid.
63986  */
63987 Roo.grid.Grid = function(container, config){
63988         // initialize the container
63989         this.container = Roo.get(container);
63990         this.container.update("");
63991         this.container.setStyle("overflow", "hidden");
63992     this.container.addClass('x-grid-container');
63993
63994     this.id = this.container.id;
63995
63996     Roo.apply(this, config);
63997     // check and correct shorthanded configs
63998     if(this.ds){
63999         this.dataSource = this.ds;
64000         delete this.ds;
64001     }
64002     if(this.cm){
64003         this.colModel = this.cm;
64004         delete this.cm;
64005     }
64006     if(this.sm){
64007         this.selModel = this.sm;
64008         delete this.sm;
64009     }
64010
64011     if (this.selModel) {
64012         this.selModel = Roo.factory(this.selModel, Roo.grid);
64013         this.sm = this.selModel;
64014         this.sm.xmodule = this.xmodule || false;
64015     }
64016     if (typeof(this.colModel.config) == 'undefined') {
64017         this.colModel = new Roo.grid.ColumnModel(this.colModel);
64018         this.cm = this.colModel;
64019         this.cm.xmodule = this.xmodule || false;
64020     }
64021     if (this.dataSource) {
64022         this.dataSource= Roo.factory(this.dataSource, Roo.data);
64023         this.ds = this.dataSource;
64024         this.ds.xmodule = this.xmodule || false;
64025          
64026     }
64027     
64028     
64029     
64030     if(this.width){
64031         this.container.setWidth(this.width);
64032     }
64033
64034     if(this.height){
64035         this.container.setHeight(this.height);
64036     }
64037     /** @private */
64038         this.addEvents({
64039         // raw events
64040         /**
64041          * @event click
64042          * The raw click event for the entire grid.
64043          * @param {Roo.EventObject} e
64044          */
64045         "click" : true,
64046         /**
64047          * @event dblclick
64048          * The raw dblclick event for the entire grid.
64049          * @param {Roo.EventObject} e
64050          */
64051         "dblclick" : true,
64052         /**
64053          * @event contextmenu
64054          * The raw contextmenu event for the entire grid.
64055          * @param {Roo.EventObject} e
64056          */
64057         "contextmenu" : true,
64058         /**
64059          * @event mousedown
64060          * The raw mousedown event for the entire grid.
64061          * @param {Roo.EventObject} e
64062          */
64063         "mousedown" : true,
64064         /**
64065          * @event mouseup
64066          * The raw mouseup event for the entire grid.
64067          * @param {Roo.EventObject} e
64068          */
64069         "mouseup" : true,
64070         /**
64071          * @event mouseover
64072          * The raw mouseover event for the entire grid.
64073          * @param {Roo.EventObject} e
64074          */
64075         "mouseover" : true,
64076         /**
64077          * @event mouseout
64078          * The raw mouseout event for the entire grid.
64079          * @param {Roo.EventObject} e
64080          */
64081         "mouseout" : true,
64082         /**
64083          * @event keypress
64084          * The raw keypress event for the entire grid.
64085          * @param {Roo.EventObject} e
64086          */
64087         "keypress" : true,
64088         /**
64089          * @event keydown
64090          * The raw keydown event for the entire grid.
64091          * @param {Roo.EventObject} e
64092          */
64093         "keydown" : true,
64094
64095         // custom events
64096
64097         /**
64098          * @event cellclick
64099          * Fires when a cell is clicked
64100          * @param {Grid} this
64101          * @param {Number} rowIndex
64102          * @param {Number} columnIndex
64103          * @param {Roo.EventObject} e
64104          */
64105         "cellclick" : true,
64106         /**
64107          * @event celldblclick
64108          * Fires when a cell is double clicked
64109          * @param {Grid} this
64110          * @param {Number} rowIndex
64111          * @param {Number} columnIndex
64112          * @param {Roo.EventObject} e
64113          */
64114         "celldblclick" : true,
64115         /**
64116          * @event rowclick
64117          * Fires when a row is clicked
64118          * @param {Grid} this
64119          * @param {Number} rowIndex
64120          * @param {Roo.EventObject} e
64121          */
64122         "rowclick" : true,
64123         /**
64124          * @event rowdblclick
64125          * Fires when a row is double clicked
64126          * @param {Grid} this
64127          * @param {Number} rowIndex
64128          * @param {Roo.EventObject} e
64129          */
64130         "rowdblclick" : true,
64131         /**
64132          * @event headerclick
64133          * Fires when a header is clicked
64134          * @param {Grid} this
64135          * @param {Number} columnIndex
64136          * @param {Roo.EventObject} e
64137          */
64138         "headerclick" : true,
64139         /**
64140          * @event headerdblclick
64141          * Fires when a header cell is double clicked
64142          * @param {Grid} this
64143          * @param {Number} columnIndex
64144          * @param {Roo.EventObject} e
64145          */
64146         "headerdblclick" : true,
64147         /**
64148          * @event rowcontextmenu
64149          * Fires when a row is right clicked
64150          * @param {Grid} this
64151          * @param {Number} rowIndex
64152          * @param {Roo.EventObject} e
64153          */
64154         "rowcontextmenu" : true,
64155         /**
64156          * @event cellcontextmenu
64157          * Fires when a cell is right clicked
64158          * @param {Grid} this
64159          * @param {Number} rowIndex
64160          * @param {Number} cellIndex
64161          * @param {Roo.EventObject} e
64162          */
64163          "cellcontextmenu" : true,
64164         /**
64165          * @event headercontextmenu
64166          * Fires when a header is right clicked
64167          * @param {Grid} this
64168          * @param {Number} columnIndex
64169          * @param {Roo.EventObject} e
64170          */
64171         "headercontextmenu" : true,
64172         /**
64173          * @event bodyscroll
64174          * Fires when the body element is scrolled
64175          * @param {Number} scrollLeft
64176          * @param {Number} scrollTop
64177          */
64178         "bodyscroll" : true,
64179         /**
64180          * @event columnresize
64181          * Fires when the user resizes a column
64182          * @param {Number} columnIndex
64183          * @param {Number} newSize
64184          */
64185         "columnresize" : true,
64186         /**
64187          * @event columnmove
64188          * Fires when the user moves a column
64189          * @param {Number} oldIndex
64190          * @param {Number} newIndex
64191          */
64192         "columnmove" : true,
64193         /**
64194          * @event startdrag
64195          * Fires when row(s) start being dragged
64196          * @param {Grid} this
64197          * @param {Roo.GridDD} dd The drag drop object
64198          * @param {event} e The raw browser event
64199          */
64200         "startdrag" : true,
64201         /**
64202          * @event enddrag
64203          * Fires when a drag operation is complete
64204          * @param {Grid} this
64205          * @param {Roo.GridDD} dd The drag drop object
64206          * @param {event} e The raw browser event
64207          */
64208         "enddrag" : true,
64209         /**
64210          * @event dragdrop
64211          * Fires when dragged row(s) are dropped on a valid DD target
64212          * @param {Grid} this
64213          * @param {Roo.GridDD} dd The drag drop object
64214          * @param {String} targetId The target drag drop object
64215          * @param {event} e The raw browser event
64216          */
64217         "dragdrop" : true,
64218         /**
64219          * @event dragover
64220          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
64221          * @param {Grid} this
64222          * @param {Roo.GridDD} dd The drag drop object
64223          * @param {String} targetId The target drag drop object
64224          * @param {event} e The raw browser event
64225          */
64226         "dragover" : true,
64227         /**
64228          * @event dragenter
64229          *  Fires when the dragged row(s) first cross another DD target while being dragged
64230          * @param {Grid} this
64231          * @param {Roo.GridDD} dd The drag drop object
64232          * @param {String} targetId The target drag drop object
64233          * @param {event} e The raw browser event
64234          */
64235         "dragenter" : true,
64236         /**
64237          * @event dragout
64238          * Fires when the dragged row(s) leave another DD target while being dragged
64239          * @param {Grid} this
64240          * @param {Roo.GridDD} dd The drag drop object
64241          * @param {String} targetId The target drag drop object
64242          * @param {event} e The raw browser event
64243          */
64244         "dragout" : true,
64245         /**
64246          * @event rowclass
64247          * Fires when a row is rendered, so you can change add a style to it.
64248          * @param {GridView} gridview   The grid view
64249          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
64250          */
64251         'rowclass' : true,
64252
64253         /**
64254          * @event render
64255          * Fires when the grid is rendered
64256          * @param {Grid} grid
64257          */
64258         'render' : true
64259     });
64260
64261     Roo.grid.Grid.superclass.constructor.call(this);
64262 };
64263 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
64264     
64265     /**
64266          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
64267          */
64268         /**
64269          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
64270          */
64271         /**
64272          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
64273          */
64274         /**
64275          * @cfg {Roo.data.Store} ds The data store for the grid
64276          */
64277         /**
64278          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
64279          */
64280          
64281          /**
64282          * @cfg {Roo.PagingToolbar} footer the paging toolbar
64283          */
64284         
64285         /**
64286      * @cfg {String} ddGroup - drag drop group.
64287      */
64288       /**
64289      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
64290      */
64291
64292     /**
64293      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
64294      */
64295     minColumnWidth : 25,
64296
64297     /**
64298      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
64299      * <b>on initial render.</b> It is more efficient to explicitly size the columns
64300      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
64301      */
64302     autoSizeColumns : false,
64303
64304     /**
64305      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
64306      */
64307     autoSizeHeaders : true,
64308
64309     /**
64310      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
64311      */
64312     monitorWindowResize : true,
64313
64314     /**
64315      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
64316      * rows measured to get a columns size. Default is 0 (all rows).
64317      */
64318     maxRowsToMeasure : 0,
64319
64320     /**
64321      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
64322      */
64323     trackMouseOver : true,
64324
64325     /**
64326     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
64327     */
64328       /**
64329     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
64330     */
64331     
64332     /**
64333     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
64334     */
64335     enableDragDrop : false,
64336     
64337     /**
64338     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
64339     */
64340     enableColumnMove : true,
64341     
64342     /**
64343     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
64344     */
64345     enableColumnHide : true,
64346     
64347     /**
64348     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
64349     */
64350     enableRowHeightSync : false,
64351     
64352     /**
64353     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
64354     */
64355     stripeRows : true,
64356     
64357     /**
64358     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
64359     */
64360     autoHeight : false,
64361
64362     /**
64363      * @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.
64364      */
64365     autoExpandColumn : false,
64366
64367     /**
64368     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
64369     * Default is 50.
64370     */
64371     autoExpandMin : 50,
64372
64373     /**
64374     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
64375     */
64376     autoExpandMax : 1000,
64377
64378     /**
64379     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
64380     */
64381     view : null,
64382
64383     /**
64384     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
64385     */
64386     loadMask : false,
64387     /**
64388     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
64389     */
64390     dropTarget: false,
64391      /**
64392     * @cfg {boolean} sortColMenu Sort the column order menu when it shows (usefull for long lists..) default false
64393     */ 
64394     sortColMenu : false,
64395     
64396     // private
64397     rendered : false,
64398
64399     /**
64400     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
64401     * of a fixed width. Default is false.
64402     */
64403     /**
64404     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
64405     */
64406     
64407     
64408     /**
64409     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
64410     * %0 is replaced with the number of selected rows.
64411     */
64412     ddText : "{0} selected row{1}",
64413     
64414     
64415     /**
64416      * Called once after all setup has been completed and the grid is ready to be rendered.
64417      * @return {Roo.grid.Grid} this
64418      */
64419     render : function()
64420     {
64421         var c = this.container;
64422         // try to detect autoHeight/width mode
64423         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
64424             this.autoHeight = true;
64425         }
64426         var view = this.getView();
64427         view.init(this);
64428
64429         c.on("click", this.onClick, this);
64430         c.on("dblclick", this.onDblClick, this);
64431         c.on("contextmenu", this.onContextMenu, this);
64432         c.on("keydown", this.onKeyDown, this);
64433         if (Roo.isTouch) {
64434             c.on("touchstart", this.onTouchStart, this);
64435         }
64436
64437         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
64438
64439         this.getSelectionModel().init(this);
64440
64441         view.render();
64442
64443         if(this.loadMask){
64444             this.loadMask = new Roo.LoadMask(this.container,
64445                     Roo.apply({store:this.dataSource}, this.loadMask));
64446         }
64447         
64448         
64449         if (this.toolbar && this.toolbar.xtype) {
64450             this.toolbar.container = this.getView().getHeaderPanel(true);
64451             this.toolbar = new Roo.Toolbar(this.toolbar);
64452         }
64453         if (this.footer && this.footer.xtype) {
64454             this.footer.dataSource = this.getDataSource();
64455             this.footer.container = this.getView().getFooterPanel(true);
64456             this.footer = Roo.factory(this.footer, Roo);
64457         }
64458         if (this.dropTarget && this.dropTarget.xtype) {
64459             delete this.dropTarget.xtype;
64460             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
64461         }
64462         
64463         
64464         this.rendered = true;
64465         this.fireEvent('render', this);
64466         return this;
64467     },
64468
64469     /**
64470      * Reconfigures the grid to use a different Store and Column Model.
64471      * The View will be bound to the new objects and refreshed.
64472      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
64473      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
64474      */
64475     reconfigure : function(dataSource, colModel){
64476         if(this.loadMask){
64477             this.loadMask.destroy();
64478             this.loadMask = new Roo.LoadMask(this.container,
64479                     Roo.apply({store:dataSource}, this.loadMask));
64480         }
64481         this.view.bind(dataSource, colModel);
64482         this.dataSource = dataSource;
64483         this.colModel = colModel;
64484         this.view.refresh(true);
64485     },
64486     /**
64487      * addColumns
64488      * Add's a column, default at the end..
64489      
64490      * @param {int} position to add (default end)
64491      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
64492      */
64493     addColumns : function(pos, ar)
64494     {
64495         
64496         for (var i =0;i< ar.length;i++) {
64497             var cfg = ar[i];
64498             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
64499             this.cm.lookup[cfg.id] = cfg;
64500         }
64501         
64502         
64503         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
64504             pos = this.cm.config.length; //this.cm.config.push(cfg);
64505         } 
64506         pos = Math.max(0,pos);
64507         ar.unshift(0);
64508         ar.unshift(pos);
64509         this.cm.config.splice.apply(this.cm.config, ar);
64510         
64511         
64512         
64513         this.view.generateRules(this.cm);
64514         this.view.refresh(true);
64515         
64516     },
64517     
64518     
64519     
64520     
64521     // private
64522     onKeyDown : function(e){
64523         this.fireEvent("keydown", e);
64524     },
64525
64526     /**
64527      * Destroy this grid.
64528      * @param {Boolean} removeEl True to remove the element
64529      */
64530     destroy : function(removeEl, keepListeners){
64531         if(this.loadMask){
64532             this.loadMask.destroy();
64533         }
64534         var c = this.container;
64535         c.removeAllListeners();
64536         this.view.destroy();
64537         this.colModel.purgeListeners();
64538         if(!keepListeners){
64539             this.purgeListeners();
64540         }
64541         c.update("");
64542         if(removeEl === true){
64543             c.remove();
64544         }
64545     },
64546
64547     // private
64548     processEvent : function(name, e){
64549         // does this fire select???
64550         //Roo.log('grid:processEvent '  + name);
64551         
64552         if (name != 'touchstart' ) {
64553             this.fireEvent(name, e);    
64554         }
64555         
64556         var t = e.getTarget();
64557         var v = this.view;
64558         var header = v.findHeaderIndex(t);
64559         if(header !== false){
64560             var ename = name == 'touchstart' ? 'click' : name;
64561              
64562             this.fireEvent("header" + ename, this, header, e);
64563         }else{
64564             var row = v.findRowIndex(t);
64565             var cell = v.findCellIndex(t);
64566             if (name == 'touchstart') {
64567                 // first touch is always a click.
64568                 // hopefull this happens after selection is updated.?
64569                 name = false;
64570                 
64571                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
64572                     var cs = this.selModel.getSelectedCell();
64573                     if (row == cs[0] && cell == cs[1]){
64574                         name = 'dblclick';
64575                     }
64576                 }
64577                 if (typeof(this.selModel.getSelections) != 'undefined') {
64578                     var cs = this.selModel.getSelections();
64579                     var ds = this.dataSource;
64580                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
64581                         name = 'dblclick';
64582                     }
64583                 }
64584                 if (!name) {
64585                     return;
64586                 }
64587             }
64588             
64589             
64590             if(row !== false){
64591                 this.fireEvent("row" + name, this, row, e);
64592                 if(cell !== false){
64593                     this.fireEvent("cell" + name, this, row, cell, e);
64594                 }
64595             }
64596         }
64597     },
64598
64599     // private
64600     onClick : function(e){
64601         this.processEvent("click", e);
64602     },
64603    // private
64604     onTouchStart : function(e){
64605         this.processEvent("touchstart", e);
64606     },
64607
64608     // private
64609     onContextMenu : function(e, t){
64610         this.processEvent("contextmenu", e);
64611     },
64612
64613     // private
64614     onDblClick : function(e){
64615         this.processEvent("dblclick", e);
64616     },
64617
64618     // private
64619     walkCells : function(row, col, step, fn, scope){
64620         var cm = this.colModel, clen = cm.getColumnCount();
64621         var ds = this.dataSource, rlen = ds.getCount(), first = true;
64622         if(step < 0){
64623             if(col < 0){
64624                 row--;
64625                 first = false;
64626             }
64627             while(row >= 0){
64628                 if(!first){
64629                     col = clen-1;
64630                 }
64631                 first = false;
64632                 while(col >= 0){
64633                     if(fn.call(scope || this, row, col, cm) === true){
64634                         return [row, col];
64635                     }
64636                     col--;
64637                 }
64638                 row--;
64639             }
64640         } else {
64641             if(col >= clen){
64642                 row++;
64643                 first = false;
64644             }
64645             while(row < rlen){
64646                 if(!first){
64647                     col = 0;
64648                 }
64649                 first = false;
64650                 while(col < clen){
64651                     if(fn.call(scope || this, row, col, cm) === true){
64652                         return [row, col];
64653                     }
64654                     col++;
64655                 }
64656                 row++;
64657             }
64658         }
64659         return null;
64660     },
64661
64662     // private
64663     getSelections : function(){
64664         return this.selModel.getSelections();
64665     },
64666
64667     /**
64668      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
64669      * but if manual update is required this method will initiate it.
64670      */
64671     autoSize : function(){
64672         if(this.rendered){
64673             this.view.layout();
64674             if(this.view.adjustForScroll){
64675                 this.view.adjustForScroll();
64676             }
64677         }
64678     },
64679
64680     /**
64681      * Returns the grid's underlying element.
64682      * @return {Element} The element
64683      */
64684     getGridEl : function(){
64685         return this.container;
64686     },
64687
64688     // private for compatibility, overridden by editor grid
64689     stopEditing : function(){},
64690
64691     /**
64692      * Returns the grid's SelectionModel.
64693      * @return {SelectionModel}
64694      */
64695     getSelectionModel : function(){
64696         if(!this.selModel){
64697             this.selModel = new Roo.grid.RowSelectionModel();
64698         }
64699         return this.selModel;
64700     },
64701
64702     /**
64703      * Returns the grid's DataSource.
64704      * @return {DataSource}
64705      */
64706     getDataSource : function(){
64707         return this.dataSource;
64708     },
64709
64710     /**
64711      * Returns the grid's ColumnModel.
64712      * @return {ColumnModel}
64713      */
64714     getColumnModel : function(){
64715         return this.colModel;
64716     },
64717
64718     /**
64719      * Returns the grid's GridView object.
64720      * @return {GridView}
64721      */
64722     getView : function(){
64723         if(!this.view){
64724             this.view = new Roo.grid.GridView(this.viewConfig);
64725             this.relayEvents(this.view, [
64726                 "beforerowremoved", "beforerowsinserted",
64727                 "beforerefresh", "rowremoved",
64728                 "rowsinserted", "rowupdated" ,"refresh"
64729             ]);
64730         }
64731         return this.view;
64732     },
64733     /**
64734      * Called to get grid's drag proxy text, by default returns this.ddText.
64735      * Override this to put something different in the dragged text.
64736      * @return {String}
64737      */
64738     getDragDropText : function(){
64739         var count = this.selModel.getCount();
64740         return String.format(this.ddText, count, count == 1 ? '' : 's');
64741     }
64742 });
64743 /*
64744  * Based on:
64745  * Ext JS Library 1.1.1
64746  * Copyright(c) 2006-2007, Ext JS, LLC.
64747  *
64748  * Originally Released Under LGPL - original licence link has changed is not relivant.
64749  *
64750  * Fork - LGPL
64751  * <script type="text/javascript">
64752  */
64753  /**
64754  * @class Roo.grid.AbstractGridView
64755  * @extends Roo.util.Observable
64756  * @abstract
64757  * Abstract base class for grid Views
64758  * @constructor
64759  */
64760 Roo.grid.AbstractGridView = function(){
64761         this.grid = null;
64762         
64763         this.events = {
64764             "beforerowremoved" : true,
64765             "beforerowsinserted" : true,
64766             "beforerefresh" : true,
64767             "rowremoved" : true,
64768             "rowsinserted" : true,
64769             "rowupdated" : true,
64770             "refresh" : true
64771         };
64772     Roo.grid.AbstractGridView.superclass.constructor.call(this);
64773 };
64774
64775 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
64776     rowClass : "x-grid-row",
64777     cellClass : "x-grid-cell",
64778     tdClass : "x-grid-td",
64779     hdClass : "x-grid-hd",
64780     splitClass : "x-grid-hd-split",
64781     
64782     init: function(grid){
64783         this.grid = grid;
64784                 var cid = this.grid.getGridEl().id;
64785         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
64786         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
64787         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
64788         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
64789         },
64790         
64791     getColumnRenderers : function(){
64792         var renderers = [];
64793         var cm = this.grid.colModel;
64794         var colCount = cm.getColumnCount();
64795         for(var i = 0; i < colCount; i++){
64796             renderers[i] = cm.getRenderer(i);
64797         }
64798         return renderers;
64799     },
64800     
64801     getColumnIds : function(){
64802         var ids = [];
64803         var cm = this.grid.colModel;
64804         var colCount = cm.getColumnCount();
64805         for(var i = 0; i < colCount; i++){
64806             ids[i] = cm.getColumnId(i);
64807         }
64808         return ids;
64809     },
64810     
64811     getDataIndexes : function(){
64812         if(!this.indexMap){
64813             this.indexMap = this.buildIndexMap();
64814         }
64815         return this.indexMap.colToData;
64816     },
64817     
64818     getColumnIndexByDataIndex : function(dataIndex){
64819         if(!this.indexMap){
64820             this.indexMap = this.buildIndexMap();
64821         }
64822         return this.indexMap.dataToCol[dataIndex];
64823     },
64824     
64825     /**
64826      * Set a css style for a column dynamically. 
64827      * @param {Number} colIndex The index of the column
64828      * @param {String} name The css property name
64829      * @param {String} value The css value
64830      */
64831     setCSSStyle : function(colIndex, name, value){
64832         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
64833         Roo.util.CSS.updateRule(selector, name, value);
64834     },
64835     
64836     generateRules : function(cm){
64837         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
64838         Roo.util.CSS.removeStyleSheet(rulesId);
64839         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
64840             var cid = cm.getColumnId(i);
64841             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
64842                          this.tdSelector, cid, " {\n}\n",
64843                          this.hdSelector, cid, " {\n}\n",
64844                          this.splitSelector, cid, " {\n}\n");
64845         }
64846         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
64847     }
64848 });/*
64849  * Based on:
64850  * Ext JS Library 1.1.1
64851  * Copyright(c) 2006-2007, Ext JS, LLC.
64852  *
64853  * Originally Released Under LGPL - original licence link has changed is not relivant.
64854  *
64855  * Fork - LGPL
64856  * <script type="text/javascript">
64857  */
64858
64859 // private
64860 // This is a support class used internally by the Grid components
64861 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
64862     this.grid = grid;
64863     this.view = grid.getView();
64864     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
64865     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
64866     if(hd2){
64867         this.setHandleElId(Roo.id(hd));
64868         this.setOuterHandleElId(Roo.id(hd2));
64869     }
64870     this.scroll = false;
64871 };
64872 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
64873     maxDragWidth: 120,
64874     getDragData : function(e){
64875         var t = Roo.lib.Event.getTarget(e);
64876         var h = this.view.findHeaderCell(t);
64877         if(h){
64878             return {ddel: h.firstChild, header:h};
64879         }
64880         return false;
64881     },
64882
64883     onInitDrag : function(e){
64884         this.view.headersDisabled = true;
64885         var clone = this.dragData.ddel.cloneNode(true);
64886         clone.id = Roo.id();
64887         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
64888         this.proxy.update(clone);
64889         return true;
64890     },
64891
64892     afterValidDrop : function(){
64893         var v = this.view;
64894         setTimeout(function(){
64895             v.headersDisabled = false;
64896         }, 50);
64897     },
64898
64899     afterInvalidDrop : function(){
64900         var v = this.view;
64901         setTimeout(function(){
64902             v.headersDisabled = false;
64903         }, 50);
64904     }
64905 });
64906 /*
64907  * Based on:
64908  * Ext JS Library 1.1.1
64909  * Copyright(c) 2006-2007, Ext JS, LLC.
64910  *
64911  * Originally Released Under LGPL - original licence link has changed is not relivant.
64912  *
64913  * Fork - LGPL
64914  * <script type="text/javascript">
64915  */
64916 // private
64917 // This is a support class used internally by the Grid components
64918 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
64919     this.grid = grid;
64920     this.view = grid.getView();
64921     // split the proxies so they don't interfere with mouse events
64922     this.proxyTop = Roo.DomHelper.append(document.body, {
64923         cls:"col-move-top", html:"&#160;"
64924     }, true);
64925     this.proxyBottom = Roo.DomHelper.append(document.body, {
64926         cls:"col-move-bottom", html:"&#160;"
64927     }, true);
64928     this.proxyTop.hide = this.proxyBottom.hide = function(){
64929         this.setLeftTop(-100,-100);
64930         this.setStyle("visibility", "hidden");
64931     };
64932     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
64933     // temporarily disabled
64934     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
64935     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
64936 };
64937 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
64938     proxyOffsets : [-4, -9],
64939     fly: Roo.Element.fly,
64940
64941     getTargetFromEvent : function(e){
64942         var t = Roo.lib.Event.getTarget(e);
64943         var cindex = this.view.findCellIndex(t);
64944         if(cindex !== false){
64945             return this.view.getHeaderCell(cindex);
64946         }
64947         return null;
64948     },
64949
64950     nextVisible : function(h){
64951         var v = this.view, cm = this.grid.colModel;
64952         h = h.nextSibling;
64953         while(h){
64954             if(!cm.isHidden(v.getCellIndex(h))){
64955                 return h;
64956             }
64957             h = h.nextSibling;
64958         }
64959         return null;
64960     },
64961
64962     prevVisible : function(h){
64963         var v = this.view, cm = this.grid.colModel;
64964         h = h.prevSibling;
64965         while(h){
64966             if(!cm.isHidden(v.getCellIndex(h))){
64967                 return h;
64968             }
64969             h = h.prevSibling;
64970         }
64971         return null;
64972     },
64973
64974     positionIndicator : function(h, n, e){
64975         var x = Roo.lib.Event.getPageX(e);
64976         var r = Roo.lib.Dom.getRegion(n.firstChild);
64977         var px, pt, py = r.top + this.proxyOffsets[1];
64978         if((r.right - x) <= (r.right-r.left)/2){
64979             px = r.right+this.view.borderWidth;
64980             pt = "after";
64981         }else{
64982             px = r.left;
64983             pt = "before";
64984         }
64985         var oldIndex = this.view.getCellIndex(h);
64986         var newIndex = this.view.getCellIndex(n);
64987
64988         if(this.grid.colModel.isFixed(newIndex)){
64989             return false;
64990         }
64991
64992         var locked = this.grid.colModel.isLocked(newIndex);
64993
64994         if(pt == "after"){
64995             newIndex++;
64996         }
64997         if(oldIndex < newIndex){
64998             newIndex--;
64999         }
65000         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
65001             return false;
65002         }
65003         px +=  this.proxyOffsets[0];
65004         this.proxyTop.setLeftTop(px, py);
65005         this.proxyTop.show();
65006         if(!this.bottomOffset){
65007             this.bottomOffset = this.view.mainHd.getHeight();
65008         }
65009         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
65010         this.proxyBottom.show();
65011         return pt;
65012     },
65013
65014     onNodeEnter : function(n, dd, e, data){
65015         if(data.header != n){
65016             this.positionIndicator(data.header, n, e);
65017         }
65018     },
65019
65020     onNodeOver : function(n, dd, e, data){
65021         var result = false;
65022         if(data.header != n){
65023             result = this.positionIndicator(data.header, n, e);
65024         }
65025         if(!result){
65026             this.proxyTop.hide();
65027             this.proxyBottom.hide();
65028         }
65029         return result ? this.dropAllowed : this.dropNotAllowed;
65030     },
65031
65032     onNodeOut : function(n, dd, e, data){
65033         this.proxyTop.hide();
65034         this.proxyBottom.hide();
65035     },
65036
65037     onNodeDrop : function(n, dd, e, data){
65038         var h = data.header;
65039         if(h != n){
65040             var cm = this.grid.colModel;
65041             var x = Roo.lib.Event.getPageX(e);
65042             var r = Roo.lib.Dom.getRegion(n.firstChild);
65043             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
65044             var oldIndex = this.view.getCellIndex(h);
65045             var newIndex = this.view.getCellIndex(n);
65046             var locked = cm.isLocked(newIndex);
65047             if(pt == "after"){
65048                 newIndex++;
65049             }
65050             if(oldIndex < newIndex){
65051                 newIndex--;
65052             }
65053             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
65054                 return false;
65055             }
65056             cm.setLocked(oldIndex, locked, true);
65057             cm.moveColumn(oldIndex, newIndex);
65058             this.grid.fireEvent("columnmove", oldIndex, newIndex);
65059             return true;
65060         }
65061         return false;
65062     }
65063 });
65064 /*
65065  * Based on:
65066  * Ext JS Library 1.1.1
65067  * Copyright(c) 2006-2007, Ext JS, LLC.
65068  *
65069  * Originally Released Under LGPL - original licence link has changed is not relivant.
65070  *
65071  * Fork - LGPL
65072  * <script type="text/javascript">
65073  */
65074   
65075 /**
65076  * @class Roo.grid.GridView
65077  * @extends Roo.util.Observable
65078  *
65079  * @constructor
65080  * @param {Object} config
65081  */
65082 Roo.grid.GridView = function(config){
65083     Roo.grid.GridView.superclass.constructor.call(this);
65084     this.el = null;
65085
65086     Roo.apply(this, config);
65087 };
65088
65089 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
65090
65091     unselectable :  'unselectable="on"',
65092     unselectableCls :  'x-unselectable',
65093     
65094     
65095     rowClass : "x-grid-row",
65096
65097     cellClass : "x-grid-col",
65098
65099     tdClass : "x-grid-td",
65100
65101     hdClass : "x-grid-hd",
65102
65103     splitClass : "x-grid-split",
65104
65105     sortClasses : ["sort-asc", "sort-desc"],
65106
65107     enableMoveAnim : false,
65108
65109     hlColor: "C3DAF9",
65110
65111     dh : Roo.DomHelper,
65112
65113     fly : Roo.Element.fly,
65114
65115     css : Roo.util.CSS,
65116
65117     borderWidth: 1,
65118
65119     splitOffset: 3,
65120
65121     scrollIncrement : 22,
65122
65123     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
65124
65125     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
65126
65127     bind : function(ds, cm){
65128         if(this.ds){
65129             this.ds.un("load", this.onLoad, this);
65130             this.ds.un("datachanged", this.onDataChange, this);
65131             this.ds.un("add", this.onAdd, this);
65132             this.ds.un("remove", this.onRemove, this);
65133             this.ds.un("update", this.onUpdate, this);
65134             this.ds.un("clear", this.onClear, this);
65135         }
65136         if(ds){
65137             ds.on("load", this.onLoad, this);
65138             ds.on("datachanged", this.onDataChange, this);
65139             ds.on("add", this.onAdd, this);
65140             ds.on("remove", this.onRemove, this);
65141             ds.on("update", this.onUpdate, this);
65142             ds.on("clear", this.onClear, this);
65143         }
65144         this.ds = ds;
65145
65146         if(this.cm){
65147             this.cm.un("widthchange", this.onColWidthChange, this);
65148             this.cm.un("headerchange", this.onHeaderChange, this);
65149             this.cm.un("hiddenchange", this.onHiddenChange, this);
65150             this.cm.un("columnmoved", this.onColumnMove, this);
65151             this.cm.un("columnlockchange", this.onColumnLock, this);
65152         }
65153         if(cm){
65154             this.generateRules(cm);
65155             cm.on("widthchange", this.onColWidthChange, this);
65156             cm.on("headerchange", this.onHeaderChange, this);
65157             cm.on("hiddenchange", this.onHiddenChange, this);
65158             cm.on("columnmoved", this.onColumnMove, this);
65159             cm.on("columnlockchange", this.onColumnLock, this);
65160         }
65161         this.cm = cm;
65162     },
65163
65164     init: function(grid){
65165         Roo.grid.GridView.superclass.init.call(this, grid);
65166
65167         this.bind(grid.dataSource, grid.colModel);
65168
65169         grid.on("headerclick", this.handleHeaderClick, this);
65170
65171         if(grid.trackMouseOver){
65172             grid.on("mouseover", this.onRowOver, this);
65173             grid.on("mouseout", this.onRowOut, this);
65174         }
65175         grid.cancelTextSelection = function(){};
65176         this.gridId = grid.id;
65177
65178         var tpls = this.templates || {};
65179
65180         if(!tpls.master){
65181             tpls.master = new Roo.Template(
65182                '<div class="x-grid" hidefocus="true">',
65183                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
65184                   '<div class="x-grid-topbar"></div>',
65185                   '<div class="x-grid-scroller"><div></div></div>',
65186                   '<div class="x-grid-locked">',
65187                       '<div class="x-grid-header">{lockedHeader}</div>',
65188                       '<div class="x-grid-body">{lockedBody}</div>',
65189                   "</div>",
65190                   '<div class="x-grid-viewport">',
65191                       '<div class="x-grid-header">{header}</div>',
65192                       '<div class="x-grid-body">{body}</div>',
65193                   "</div>",
65194                   '<div class="x-grid-bottombar"></div>',
65195                  
65196                   '<div class="x-grid-resize-proxy">&#160;</div>',
65197                "</div>"
65198             );
65199             tpls.master.disableformats = true;
65200         }
65201
65202         if(!tpls.header){
65203             tpls.header = new Roo.Template(
65204                '<table border="0" cellspacing="0" cellpadding="0">',
65205                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
65206                "</table>{splits}"
65207             );
65208             tpls.header.disableformats = true;
65209         }
65210         tpls.header.compile();
65211
65212         if(!tpls.hcell){
65213             tpls.hcell = new Roo.Template(
65214                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
65215                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
65216                 "</div></td>"
65217              );
65218              tpls.hcell.disableFormats = true;
65219         }
65220         tpls.hcell.compile();
65221
65222         if(!tpls.hsplit){
65223             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
65224                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
65225             tpls.hsplit.disableFormats = true;
65226         }
65227         tpls.hsplit.compile();
65228
65229         if(!tpls.body){
65230             tpls.body = new Roo.Template(
65231                '<table border="0" cellspacing="0" cellpadding="0">',
65232                "<tbody>{rows}</tbody>",
65233                "</table>"
65234             );
65235             tpls.body.disableFormats = true;
65236         }
65237         tpls.body.compile();
65238
65239         if(!tpls.row){
65240             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
65241             tpls.row.disableFormats = true;
65242         }
65243         tpls.row.compile();
65244
65245         if(!tpls.cell){
65246             tpls.cell = new Roo.Template(
65247                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
65248                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
65249                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
65250                 "</td>"
65251             );
65252             tpls.cell.disableFormats = true;
65253         }
65254         tpls.cell.compile();
65255
65256         this.templates = tpls;
65257     },
65258
65259     // remap these for backwards compat
65260     onColWidthChange : function(){
65261         this.updateColumns.apply(this, arguments);
65262     },
65263     onHeaderChange : function(){
65264         this.updateHeaders.apply(this, arguments);
65265     }, 
65266     onHiddenChange : function(){
65267         this.handleHiddenChange.apply(this, arguments);
65268     },
65269     onColumnMove : function(){
65270         this.handleColumnMove.apply(this, arguments);
65271     },
65272     onColumnLock : function(){
65273         this.handleLockChange.apply(this, arguments);
65274     },
65275
65276     onDataChange : function(){
65277         this.refresh();
65278         this.updateHeaderSortState();
65279     },
65280
65281     onClear : function(){
65282         this.refresh();
65283     },
65284
65285     onUpdate : function(ds, record){
65286         this.refreshRow(record);
65287     },
65288
65289     refreshRow : function(record){
65290         var ds = this.ds, index;
65291         if(typeof record == 'number'){
65292             index = record;
65293             record = ds.getAt(index);
65294         }else{
65295             index = ds.indexOf(record);
65296         }
65297         this.insertRows(ds, index, index, true);
65298         this.onRemove(ds, record, index+1, true);
65299         this.syncRowHeights(index, index);
65300         this.layout();
65301         this.fireEvent("rowupdated", this, index, record);
65302     },
65303
65304     onAdd : function(ds, records, index){
65305         this.insertRows(ds, index, index + (records.length-1));
65306     },
65307
65308     onRemove : function(ds, record, index, isUpdate){
65309         if(isUpdate !== true){
65310             this.fireEvent("beforerowremoved", this, index, record);
65311         }
65312         var bt = this.getBodyTable(), lt = this.getLockedTable();
65313         if(bt.rows[index]){
65314             bt.firstChild.removeChild(bt.rows[index]);
65315         }
65316         if(lt.rows[index]){
65317             lt.firstChild.removeChild(lt.rows[index]);
65318         }
65319         if(isUpdate !== true){
65320             this.stripeRows(index);
65321             this.syncRowHeights(index, index);
65322             this.layout();
65323             this.fireEvent("rowremoved", this, index, record);
65324         }
65325     },
65326
65327     onLoad : function(){
65328         this.scrollToTop();
65329     },
65330
65331     /**
65332      * Scrolls the grid to the top
65333      */
65334     scrollToTop : function(){
65335         if(this.scroller){
65336             this.scroller.dom.scrollTop = 0;
65337             this.syncScroll();
65338         }
65339     },
65340
65341     /**
65342      * Gets a panel in the header of the grid that can be used for toolbars etc.
65343      * After modifying the contents of this panel a call to grid.autoSize() may be
65344      * required to register any changes in size.
65345      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
65346      * @return Roo.Element
65347      */
65348     getHeaderPanel : function(doShow){
65349         if(doShow){
65350             this.headerPanel.show();
65351         }
65352         return this.headerPanel;
65353     },
65354
65355     /**
65356      * Gets a panel in the footer of the grid that can be used for toolbars etc.
65357      * After modifying the contents of this panel a call to grid.autoSize() may be
65358      * required to register any changes in size.
65359      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
65360      * @return Roo.Element
65361      */
65362     getFooterPanel : function(doShow){
65363         if(doShow){
65364             this.footerPanel.show();
65365         }
65366         return this.footerPanel;
65367     },
65368
65369     initElements : function(){
65370         var E = Roo.Element;
65371         var el = this.grid.getGridEl().dom.firstChild;
65372         var cs = el.childNodes;
65373
65374         this.el = new E(el);
65375         
65376          this.focusEl = new E(el.firstChild);
65377         this.focusEl.swallowEvent("click", true);
65378         
65379         this.headerPanel = new E(cs[1]);
65380         this.headerPanel.enableDisplayMode("block");
65381
65382         this.scroller = new E(cs[2]);
65383         this.scrollSizer = new E(this.scroller.dom.firstChild);
65384
65385         this.lockedWrap = new E(cs[3]);
65386         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
65387         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
65388
65389         this.mainWrap = new E(cs[4]);
65390         this.mainHd = new E(this.mainWrap.dom.firstChild);
65391         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
65392
65393         this.footerPanel = new E(cs[5]);
65394         this.footerPanel.enableDisplayMode("block");
65395
65396         this.resizeProxy = new E(cs[6]);
65397
65398         this.headerSelector = String.format(
65399            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
65400            this.lockedHd.id, this.mainHd.id
65401         );
65402
65403         this.splitterSelector = String.format(
65404            '#{0} div.x-grid-split, #{1} div.x-grid-split',
65405            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
65406         );
65407     },
65408     idToCssName : function(s)
65409     {
65410         return s.replace(/[^a-z0-9]+/ig, '-');
65411     },
65412
65413     getHeaderCell : function(index){
65414         return Roo.DomQuery.select(this.headerSelector)[index];
65415     },
65416
65417     getHeaderCellMeasure : function(index){
65418         return this.getHeaderCell(index).firstChild;
65419     },
65420
65421     getHeaderCellText : function(index){
65422         return this.getHeaderCell(index).firstChild.firstChild;
65423     },
65424
65425     getLockedTable : function(){
65426         return this.lockedBody.dom.firstChild;
65427     },
65428
65429     getBodyTable : function(){
65430         return this.mainBody.dom.firstChild;
65431     },
65432
65433     getLockedRow : function(index){
65434         return this.getLockedTable().rows[index];
65435     },
65436
65437     getRow : function(index){
65438         return this.getBodyTable().rows[index];
65439     },
65440
65441     getRowComposite : function(index){
65442         if(!this.rowEl){
65443             this.rowEl = new Roo.CompositeElementLite();
65444         }
65445         var els = [], lrow, mrow;
65446         if(lrow = this.getLockedRow(index)){
65447             els.push(lrow);
65448         }
65449         if(mrow = this.getRow(index)){
65450             els.push(mrow);
65451         }
65452         this.rowEl.elements = els;
65453         return this.rowEl;
65454     },
65455     /**
65456      * Gets the 'td' of the cell
65457      * 
65458      * @param {Integer} rowIndex row to select
65459      * @param {Integer} colIndex column to select
65460      * 
65461      * @return {Object} 
65462      */
65463     getCell : function(rowIndex, colIndex){
65464         var locked = this.cm.getLockedCount();
65465         var source;
65466         if(colIndex < locked){
65467             source = this.lockedBody.dom.firstChild;
65468         }else{
65469             source = this.mainBody.dom.firstChild;
65470             colIndex -= locked;
65471         }
65472         return source.rows[rowIndex].childNodes[colIndex];
65473     },
65474
65475     getCellText : function(rowIndex, colIndex){
65476         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
65477     },
65478
65479     getCellBox : function(cell){
65480         var b = this.fly(cell).getBox();
65481         if(Roo.isOpera){ // opera fails to report the Y
65482             b.y = cell.offsetTop + this.mainBody.getY();
65483         }
65484         return b;
65485     },
65486
65487     getCellIndex : function(cell){
65488         var id = String(cell.className).match(this.cellRE);
65489         if(id){
65490             return parseInt(id[1], 10);
65491         }
65492         return 0;
65493     },
65494
65495     findHeaderIndex : function(n){
65496         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
65497         return r ? this.getCellIndex(r) : false;
65498     },
65499
65500     findHeaderCell : function(n){
65501         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
65502         return r ? r : false;
65503     },
65504
65505     findRowIndex : function(n){
65506         if(!n){
65507             return false;
65508         }
65509         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
65510         return r ? r.rowIndex : false;
65511     },
65512
65513     findCellIndex : function(node){
65514         var stop = this.el.dom;
65515         while(node && node != stop){
65516             if(this.findRE.test(node.className)){
65517                 return this.getCellIndex(node);
65518             }
65519             node = node.parentNode;
65520         }
65521         return false;
65522     },
65523
65524     getColumnId : function(index){
65525         return this.cm.getColumnId(index);
65526     },
65527
65528     getSplitters : function()
65529     {
65530         if(this.splitterSelector){
65531            return Roo.DomQuery.select(this.splitterSelector);
65532         }else{
65533             return null;
65534       }
65535     },
65536
65537     getSplitter : function(index){
65538         return this.getSplitters()[index];
65539     },
65540
65541     onRowOver : function(e, t){
65542         var row;
65543         if((row = this.findRowIndex(t)) !== false){
65544             this.getRowComposite(row).addClass("x-grid-row-over");
65545         }
65546     },
65547
65548     onRowOut : function(e, t){
65549         var row;
65550         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
65551             this.getRowComposite(row).removeClass("x-grid-row-over");
65552         }
65553     },
65554
65555     renderHeaders : function(){
65556         var cm = this.cm;
65557         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
65558         var cb = [], lb = [], sb = [], lsb = [], p = {};
65559         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
65560             p.cellId = "x-grid-hd-0-" + i;
65561             p.splitId = "x-grid-csplit-0-" + i;
65562             p.id = cm.getColumnId(i);
65563             p.value = cm.getColumnHeader(i) || "";
65564             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
65565             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
65566             if(!cm.isLocked(i)){
65567                 cb[cb.length] = ct.apply(p);
65568                 sb[sb.length] = st.apply(p);
65569             }else{
65570                 lb[lb.length] = ct.apply(p);
65571                 lsb[lsb.length] = st.apply(p);
65572             }
65573         }
65574         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
65575                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
65576     },
65577
65578     updateHeaders : function(){
65579         var html = this.renderHeaders();
65580         this.lockedHd.update(html[0]);
65581         this.mainHd.update(html[1]);
65582     },
65583
65584     /**
65585      * Focuses the specified row.
65586      * @param {Number} row The row index
65587      */
65588     focusRow : function(row)
65589     {
65590         //Roo.log('GridView.focusRow');
65591         var x = this.scroller.dom.scrollLeft;
65592         this.focusCell(row, 0, false);
65593         this.scroller.dom.scrollLeft = x;
65594     },
65595
65596     /**
65597      * Focuses the specified cell.
65598      * @param {Number} row The row index
65599      * @param {Number} col The column index
65600      * @param {Boolean} hscroll false to disable horizontal scrolling
65601      */
65602     focusCell : function(row, col, hscroll)
65603     {
65604         //Roo.log('GridView.focusCell');
65605         var el = this.ensureVisible(row, col, hscroll);
65606         this.focusEl.alignTo(el, "tl-tl");
65607         if(Roo.isGecko){
65608             this.focusEl.focus();
65609         }else{
65610             this.focusEl.focus.defer(1, this.focusEl);
65611         }
65612     },
65613
65614     /**
65615      * Scrolls the specified cell into view
65616      * @param {Number} row The row index
65617      * @param {Number} col The column index
65618      * @param {Boolean} hscroll false to disable horizontal scrolling
65619      */
65620     ensureVisible : function(row, col, hscroll)
65621     {
65622         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
65623         //return null; //disable for testing.
65624         if(typeof row != "number"){
65625             row = row.rowIndex;
65626         }
65627         if(row < 0 && row >= this.ds.getCount()){
65628             return  null;
65629         }
65630         col = (col !== undefined ? col : 0);
65631         var cm = this.grid.colModel;
65632         while(cm.isHidden(col)){
65633             col++;
65634         }
65635
65636         var el = this.getCell(row, col);
65637         if(!el){
65638             return null;
65639         }
65640         var c = this.scroller.dom;
65641
65642         var ctop = parseInt(el.offsetTop, 10);
65643         var cleft = parseInt(el.offsetLeft, 10);
65644         var cbot = ctop + el.offsetHeight;
65645         var cright = cleft + el.offsetWidth;
65646         
65647         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
65648         var stop = parseInt(c.scrollTop, 10);
65649         var sleft = parseInt(c.scrollLeft, 10);
65650         var sbot = stop + ch;
65651         var sright = sleft + c.clientWidth;
65652         /*
65653         Roo.log('GridView.ensureVisible:' +
65654                 ' ctop:' + ctop +
65655                 ' c.clientHeight:' + c.clientHeight +
65656                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
65657                 ' stop:' + stop +
65658                 ' cbot:' + cbot +
65659                 ' sbot:' + sbot +
65660                 ' ch:' + ch  
65661                 );
65662         */
65663         if(ctop < stop){
65664             c.scrollTop = ctop;
65665             //Roo.log("set scrolltop to ctop DISABLE?");
65666         }else if(cbot > sbot){
65667             //Roo.log("set scrolltop to cbot-ch");
65668             c.scrollTop = cbot-ch;
65669         }
65670         
65671         if(hscroll !== false){
65672             if(cleft < sleft){
65673                 c.scrollLeft = cleft;
65674             }else if(cright > sright){
65675                 c.scrollLeft = cright-c.clientWidth;
65676             }
65677         }
65678          
65679         return el;
65680     },
65681
65682     updateColumns : function(){
65683         this.grid.stopEditing();
65684         var cm = this.grid.colModel, colIds = this.getColumnIds();
65685         //var totalWidth = cm.getTotalWidth();
65686         var pos = 0;
65687         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
65688             //if(cm.isHidden(i)) continue;
65689             var w = cm.getColumnWidth(i);
65690             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
65691             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
65692         }
65693         this.updateSplitters();
65694     },
65695
65696     generateRules : function(cm){
65697         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
65698         Roo.util.CSS.removeStyleSheet(rulesId);
65699         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
65700             var cid = cm.getColumnId(i);
65701             var align = '';
65702             if(cm.config[i].align){
65703                 align = 'text-align:'+cm.config[i].align+';';
65704             }
65705             var hidden = '';
65706             if(cm.isHidden(i)){
65707                 hidden = 'display:none;';
65708             }
65709             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
65710             ruleBuf.push(
65711                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
65712                     this.hdSelector, cid, " {\n", align, width, "}\n",
65713                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
65714                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
65715         }
65716         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
65717     },
65718
65719     updateSplitters : function(){
65720         var cm = this.cm, s = this.getSplitters();
65721         if(s){ // splitters not created yet
65722             var pos = 0, locked = true;
65723             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
65724                 if(cm.isHidden(i)) {
65725                     continue;
65726                 }
65727                 var w = cm.getColumnWidth(i); // make sure it's a number
65728                 if(!cm.isLocked(i) && locked){
65729                     pos = 0;
65730                     locked = false;
65731                 }
65732                 pos += w;
65733                 s[i].style.left = (pos-this.splitOffset) + "px";
65734             }
65735         }
65736     },
65737
65738     handleHiddenChange : function(colModel, colIndex, hidden){
65739         if(hidden){
65740             this.hideColumn(colIndex);
65741         }else{
65742             this.unhideColumn(colIndex);
65743         }
65744     },
65745
65746     hideColumn : function(colIndex){
65747         var cid = this.getColumnId(colIndex);
65748         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
65749         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
65750         if(Roo.isSafari){
65751             this.updateHeaders();
65752         }
65753         this.updateSplitters();
65754         this.layout();
65755     },
65756
65757     unhideColumn : function(colIndex){
65758         var cid = this.getColumnId(colIndex);
65759         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
65760         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
65761
65762         if(Roo.isSafari){
65763             this.updateHeaders();
65764         }
65765         this.updateSplitters();
65766         this.layout();
65767     },
65768
65769     insertRows : function(dm, firstRow, lastRow, isUpdate){
65770         if(firstRow == 0 && lastRow == dm.getCount()-1){
65771             this.refresh();
65772         }else{
65773             if(!isUpdate){
65774                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
65775             }
65776             var s = this.getScrollState();
65777             var markup = this.renderRows(firstRow, lastRow);
65778             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
65779             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
65780             this.restoreScroll(s);
65781             if(!isUpdate){
65782                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
65783                 this.syncRowHeights(firstRow, lastRow);
65784                 this.stripeRows(firstRow);
65785                 this.layout();
65786             }
65787         }
65788     },
65789
65790     bufferRows : function(markup, target, index){
65791         var before = null, trows = target.rows, tbody = target.tBodies[0];
65792         if(index < trows.length){
65793             before = trows[index];
65794         }
65795         var b = document.createElement("div");
65796         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
65797         var rows = b.firstChild.rows;
65798         for(var i = 0, len = rows.length; i < len; i++){
65799             if(before){
65800                 tbody.insertBefore(rows[0], before);
65801             }else{
65802                 tbody.appendChild(rows[0]);
65803             }
65804         }
65805         b.innerHTML = "";
65806         b = null;
65807     },
65808
65809     deleteRows : function(dm, firstRow, lastRow){
65810         if(dm.getRowCount()<1){
65811             this.fireEvent("beforerefresh", this);
65812             this.mainBody.update("");
65813             this.lockedBody.update("");
65814             this.fireEvent("refresh", this);
65815         }else{
65816             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
65817             var bt = this.getBodyTable();
65818             var tbody = bt.firstChild;
65819             var rows = bt.rows;
65820             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
65821                 tbody.removeChild(rows[firstRow]);
65822             }
65823             this.stripeRows(firstRow);
65824             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
65825         }
65826     },
65827
65828     updateRows : function(dataSource, firstRow, lastRow){
65829         var s = this.getScrollState();
65830         this.refresh();
65831         this.restoreScroll(s);
65832     },
65833
65834     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
65835         if(!noRefresh){
65836            this.refresh();
65837         }
65838         this.updateHeaderSortState();
65839     },
65840
65841     getScrollState : function(){
65842         
65843         var sb = this.scroller.dom;
65844         return {left: sb.scrollLeft, top: sb.scrollTop};
65845     },
65846
65847     stripeRows : function(startRow){
65848         if(!this.grid.stripeRows || this.ds.getCount() < 1){
65849             return;
65850         }
65851         startRow = startRow || 0;
65852         var rows = this.getBodyTable().rows;
65853         var lrows = this.getLockedTable().rows;
65854         var cls = ' x-grid-row-alt ';
65855         for(var i = startRow, len = rows.length; i < len; i++){
65856             var row = rows[i], lrow = lrows[i];
65857             var isAlt = ((i+1) % 2 == 0);
65858             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
65859             if(isAlt == hasAlt){
65860                 continue;
65861             }
65862             if(isAlt){
65863                 row.className += " x-grid-row-alt";
65864             }else{
65865                 row.className = row.className.replace("x-grid-row-alt", "");
65866             }
65867             if(lrow){
65868                 lrow.className = row.className;
65869             }
65870         }
65871     },
65872
65873     restoreScroll : function(state){
65874         //Roo.log('GridView.restoreScroll');
65875         var sb = this.scroller.dom;
65876         sb.scrollLeft = state.left;
65877         sb.scrollTop = state.top;
65878         this.syncScroll();
65879     },
65880
65881     syncScroll : function(){
65882         //Roo.log('GridView.syncScroll');
65883         var sb = this.scroller.dom;
65884         var sh = this.mainHd.dom;
65885         var bs = this.mainBody.dom;
65886         var lv = this.lockedBody.dom;
65887         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
65888         lv.scrollTop = bs.scrollTop = sb.scrollTop;
65889     },
65890
65891     handleScroll : function(e){
65892         this.syncScroll();
65893         var sb = this.scroller.dom;
65894         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
65895         e.stopEvent();
65896     },
65897
65898     handleWheel : function(e){
65899         var d = e.getWheelDelta();
65900         this.scroller.dom.scrollTop -= d*22;
65901         // set this here to prevent jumpy scrolling on large tables
65902         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
65903         e.stopEvent();
65904     },
65905
65906     renderRows : function(startRow, endRow){
65907         // pull in all the crap needed to render rows
65908         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
65909         var colCount = cm.getColumnCount();
65910
65911         if(ds.getCount() < 1){
65912             return ["", ""];
65913         }
65914
65915         // build a map for all the columns
65916         var cs = [];
65917         for(var i = 0; i < colCount; i++){
65918             var name = cm.getDataIndex(i);
65919             cs[i] = {
65920                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
65921                 renderer : cm.getRenderer(i),
65922                 id : cm.getColumnId(i),
65923                 locked : cm.isLocked(i),
65924                 has_editor : cm.isCellEditable(i)
65925             };
65926         }
65927
65928         startRow = startRow || 0;
65929         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
65930
65931         // records to render
65932         var rs = ds.getRange(startRow, endRow);
65933
65934         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
65935     },
65936
65937     // As much as I hate to duplicate code, this was branched because FireFox really hates
65938     // [].join("") on strings. The performance difference was substantial enough to
65939     // branch this function
65940     doRender : Roo.isGecko ?
65941             function(cs, rs, ds, startRow, colCount, stripe){
65942                 var ts = this.templates, ct = ts.cell, rt = ts.row;
65943                 // buffers
65944                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
65945                 
65946                 var hasListener = this.grid.hasListener('rowclass');
65947                 var rowcfg = {};
65948                 for(var j = 0, len = rs.length; j < len; j++){
65949                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
65950                     for(var i = 0; i < colCount; i++){
65951                         c = cs[i];
65952                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
65953                         p.id = c.id;
65954                         p.css = p.attr = "";
65955                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
65956                         if(p.value == undefined || p.value === "") {
65957                             p.value = "&#160;";
65958                         }
65959                         if(c.has_editor){
65960                             p.css += ' x-grid-editable-cell';
65961                         }
65962                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
65963                             p.css +=  ' x-grid-dirty-cell';
65964                         }
65965                         var markup = ct.apply(p);
65966                         if(!c.locked){
65967                             cb+= markup;
65968                         }else{
65969                             lcb+= markup;
65970                         }
65971                     }
65972                     var alt = [];
65973                     if(stripe && ((rowIndex+1) % 2 == 0)){
65974                         alt.push("x-grid-row-alt")
65975                     }
65976                     if(r.dirty){
65977                         alt.push(  " x-grid-dirty-row");
65978                     }
65979                     rp.cells = lcb;
65980                     if(this.getRowClass){
65981                         alt.push(this.getRowClass(r, rowIndex));
65982                     }
65983                     if (hasListener) {
65984                         rowcfg = {
65985                              
65986                             record: r,
65987                             rowIndex : rowIndex,
65988                             rowClass : ''
65989                         };
65990                         this.grid.fireEvent('rowclass', this, rowcfg);
65991                         alt.push(rowcfg.rowClass);
65992                     }
65993                     rp.alt = alt.join(" ");
65994                     lbuf+= rt.apply(rp);
65995                     rp.cells = cb;
65996                     buf+=  rt.apply(rp);
65997                 }
65998                 return [lbuf, buf];
65999             } :
66000             function(cs, rs, ds, startRow, colCount, stripe){
66001                 var ts = this.templates, ct = ts.cell, rt = ts.row;
66002                 // buffers
66003                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
66004                 var hasListener = this.grid.hasListener('rowclass');
66005  
66006                 var rowcfg = {};
66007                 for(var j = 0, len = rs.length; j < len; j++){
66008                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
66009                     for(var i = 0; i < colCount; i++){
66010                         c = cs[i];
66011                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
66012                         p.id = c.id;
66013                         p.css = p.attr = "";
66014                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
66015                         if(p.value == undefined || p.value === "") {
66016                             p.value = "&#160;";
66017                         }
66018                         //Roo.log(c);
66019                          if(c.has_editor){
66020                             p.css += ' x-grid-editable-cell';
66021                         }
66022                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
66023                             p.css += ' x-grid-dirty-cell' 
66024                         }
66025                         
66026                         var markup = ct.apply(p);
66027                         if(!c.locked){
66028                             cb[cb.length] = markup;
66029                         }else{
66030                             lcb[lcb.length] = markup;
66031                         }
66032                     }
66033                     var alt = [];
66034                     if(stripe && ((rowIndex+1) % 2 == 0)){
66035                         alt.push( "x-grid-row-alt");
66036                     }
66037                     if(r.dirty){
66038                         alt.push(" x-grid-dirty-row");
66039                     }
66040                     rp.cells = lcb;
66041                     if(this.getRowClass){
66042                         alt.push( this.getRowClass(r, rowIndex));
66043                     }
66044                     if (hasListener) {
66045                         rowcfg = {
66046                              
66047                             record: r,
66048                             rowIndex : rowIndex,
66049                             rowClass : ''
66050                         };
66051                         this.grid.fireEvent('rowclass', this, rowcfg);
66052                         alt.push(rowcfg.rowClass);
66053                     }
66054                     
66055                     rp.alt = alt.join(" ");
66056                     rp.cells = lcb.join("");
66057                     lbuf[lbuf.length] = rt.apply(rp);
66058                     rp.cells = cb.join("");
66059                     buf[buf.length] =  rt.apply(rp);
66060                 }
66061                 return [lbuf.join(""), buf.join("")];
66062             },
66063
66064     renderBody : function(){
66065         var markup = this.renderRows();
66066         var bt = this.templates.body;
66067         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
66068     },
66069
66070     /**
66071      * Refreshes the grid
66072      * @param {Boolean} headersToo
66073      */
66074     refresh : function(headersToo){
66075         this.fireEvent("beforerefresh", this);
66076         this.grid.stopEditing();
66077         var result = this.renderBody();
66078         this.lockedBody.update(result[0]);
66079         this.mainBody.update(result[1]);
66080         if(headersToo === true){
66081             this.updateHeaders();
66082             this.updateColumns();
66083             this.updateSplitters();
66084             this.updateHeaderSortState();
66085         }
66086         this.syncRowHeights();
66087         this.layout();
66088         this.fireEvent("refresh", this);
66089     },
66090
66091     handleColumnMove : function(cm, oldIndex, newIndex){
66092         this.indexMap = null;
66093         var s = this.getScrollState();
66094         this.refresh(true);
66095         this.restoreScroll(s);
66096         this.afterMove(newIndex);
66097     },
66098
66099     afterMove : function(colIndex){
66100         if(this.enableMoveAnim && Roo.enableFx){
66101             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
66102         }
66103         // if multisort - fix sortOrder, and reload..
66104         if (this.grid.dataSource.multiSort) {
66105             // the we can call sort again..
66106             var dm = this.grid.dataSource;
66107             var cm = this.grid.colModel;
66108             var so = [];
66109             for(var i = 0; i < cm.config.length; i++ ) {
66110                 
66111                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
66112                     continue; // dont' bother, it's not in sort list or being set.
66113                 }
66114                 
66115                 so.push(cm.config[i].dataIndex);
66116             };
66117             dm.sortOrder = so;
66118             dm.load(dm.lastOptions);
66119             
66120             
66121         }
66122         
66123     },
66124
66125     updateCell : function(dm, rowIndex, dataIndex){
66126         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
66127         if(typeof colIndex == "undefined"){ // not present in grid
66128             return;
66129         }
66130         var cm = this.grid.colModel;
66131         var cell = this.getCell(rowIndex, colIndex);
66132         var cellText = this.getCellText(rowIndex, colIndex);
66133
66134         var p = {
66135             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
66136             id : cm.getColumnId(colIndex),
66137             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
66138         };
66139         var renderer = cm.getRenderer(colIndex);
66140         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
66141         if(typeof val == "undefined" || val === "") {
66142             val = "&#160;";
66143         }
66144         cellText.innerHTML = val;
66145         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
66146         this.syncRowHeights(rowIndex, rowIndex);
66147     },
66148
66149     calcColumnWidth : function(colIndex, maxRowsToMeasure){
66150         var maxWidth = 0;
66151         if(this.grid.autoSizeHeaders){
66152             var h = this.getHeaderCellMeasure(colIndex);
66153             maxWidth = Math.max(maxWidth, h.scrollWidth);
66154         }
66155         var tb, index;
66156         if(this.cm.isLocked(colIndex)){
66157             tb = this.getLockedTable();
66158             index = colIndex;
66159         }else{
66160             tb = this.getBodyTable();
66161             index = colIndex - this.cm.getLockedCount();
66162         }
66163         if(tb && tb.rows){
66164             var rows = tb.rows;
66165             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
66166             for(var i = 0; i < stopIndex; i++){
66167                 var cell = rows[i].childNodes[index].firstChild;
66168                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
66169             }
66170         }
66171         return maxWidth + /*margin for error in IE*/ 5;
66172     },
66173     /**
66174      * Autofit a column to its content.
66175      * @param {Number} colIndex
66176      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
66177      */
66178      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
66179          if(this.cm.isHidden(colIndex)){
66180              return; // can't calc a hidden column
66181          }
66182         if(forceMinSize){
66183             var cid = this.cm.getColumnId(colIndex);
66184             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
66185            if(this.grid.autoSizeHeaders){
66186                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
66187            }
66188         }
66189         var newWidth = this.calcColumnWidth(colIndex);
66190         this.cm.setColumnWidth(colIndex,
66191             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
66192         if(!suppressEvent){
66193             this.grid.fireEvent("columnresize", colIndex, newWidth);
66194         }
66195     },
66196
66197     /**
66198      * Autofits all columns to their content and then expands to fit any extra space in the grid
66199      */
66200      autoSizeColumns : function(){
66201         var cm = this.grid.colModel;
66202         var colCount = cm.getColumnCount();
66203         for(var i = 0; i < colCount; i++){
66204             this.autoSizeColumn(i, true, true);
66205         }
66206         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
66207             this.fitColumns();
66208         }else{
66209             this.updateColumns();
66210             this.layout();
66211         }
66212     },
66213
66214     /**
66215      * Autofits all columns to the grid's width proportionate with their current size
66216      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
66217      */
66218     fitColumns : function(reserveScrollSpace){
66219         var cm = this.grid.colModel;
66220         var colCount = cm.getColumnCount();
66221         var cols = [];
66222         var width = 0;
66223         var i, w;
66224         for (i = 0; i < colCount; i++){
66225             if(!cm.isHidden(i) && !cm.isFixed(i)){
66226                 w = cm.getColumnWidth(i);
66227                 cols.push(i);
66228                 cols.push(w);
66229                 width += w;
66230             }
66231         }
66232         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
66233         if(reserveScrollSpace){
66234             avail -= 17;
66235         }
66236         var frac = (avail - cm.getTotalWidth())/width;
66237         while (cols.length){
66238             w = cols.pop();
66239             i = cols.pop();
66240             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
66241         }
66242         this.updateColumns();
66243         this.layout();
66244     },
66245
66246     onRowSelect : function(rowIndex){
66247         var row = this.getRowComposite(rowIndex);
66248         row.addClass("x-grid-row-selected");
66249     },
66250
66251     onRowDeselect : function(rowIndex){
66252         var row = this.getRowComposite(rowIndex);
66253         row.removeClass("x-grid-row-selected");
66254     },
66255
66256     onCellSelect : function(row, col){
66257         var cell = this.getCell(row, col);
66258         if(cell){
66259             Roo.fly(cell).addClass("x-grid-cell-selected");
66260         }
66261     },
66262
66263     onCellDeselect : function(row, col){
66264         var cell = this.getCell(row, col);
66265         if(cell){
66266             Roo.fly(cell).removeClass("x-grid-cell-selected");
66267         }
66268     },
66269
66270     updateHeaderSortState : function(){
66271         
66272         // sort state can be single { field: xxx, direction : yyy}
66273         // or   { xxx=>ASC , yyy : DESC ..... }
66274         
66275         var mstate = {};
66276         if (!this.ds.multiSort) { 
66277             var state = this.ds.getSortState();
66278             if(!state){
66279                 return;
66280             }
66281             mstate[state.field] = state.direction;
66282             // FIXME... - this is not used here.. but might be elsewhere..
66283             this.sortState = state;
66284             
66285         } else {
66286             mstate = this.ds.sortToggle;
66287         }
66288         //remove existing sort classes..
66289         
66290         var sc = this.sortClasses;
66291         var hds = this.el.select(this.headerSelector).removeClass(sc);
66292         
66293         for(var f in mstate) {
66294         
66295             var sortColumn = this.cm.findColumnIndex(f);
66296             
66297             if(sortColumn != -1){
66298                 var sortDir = mstate[f];        
66299                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
66300             }
66301         }
66302         
66303          
66304         
66305     },
66306
66307
66308     handleHeaderClick : function(g, index,e){
66309         
66310         Roo.log("header click");
66311         
66312         if (Roo.isTouch) {
66313             // touch events on header are handled by context
66314             this.handleHdCtx(g,index,e);
66315             return;
66316         }
66317         
66318         
66319         if(this.headersDisabled){
66320             return;
66321         }
66322         var dm = g.dataSource, cm = g.colModel;
66323         if(!cm.isSortable(index)){
66324             return;
66325         }
66326         g.stopEditing();
66327         
66328         if (dm.multiSort) {
66329             // update the sortOrder
66330             var so = [];
66331             for(var i = 0; i < cm.config.length; i++ ) {
66332                 
66333                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
66334                     continue; // dont' bother, it's not in sort list or being set.
66335                 }
66336                 
66337                 so.push(cm.config[i].dataIndex);
66338             };
66339             dm.sortOrder = so;
66340         }
66341         
66342         
66343         dm.sort(cm.getDataIndex(index));
66344     },
66345
66346
66347     destroy : function(){
66348         if(this.colMenu){
66349             this.colMenu.removeAll();
66350             Roo.menu.MenuMgr.unregister(this.colMenu);
66351             this.colMenu.getEl().remove();
66352             delete this.colMenu;
66353         }
66354         if(this.hmenu){
66355             this.hmenu.removeAll();
66356             Roo.menu.MenuMgr.unregister(this.hmenu);
66357             this.hmenu.getEl().remove();
66358             delete this.hmenu;
66359         }
66360         if(this.grid.enableColumnMove){
66361             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
66362             if(dds){
66363                 for(var dd in dds){
66364                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
66365                         var elid = dds[dd].dragElId;
66366                         dds[dd].unreg();
66367                         Roo.get(elid).remove();
66368                     } else if(dds[dd].config.isTarget){
66369                         dds[dd].proxyTop.remove();
66370                         dds[dd].proxyBottom.remove();
66371                         dds[dd].unreg();
66372                     }
66373                     if(Roo.dd.DDM.locationCache[dd]){
66374                         delete Roo.dd.DDM.locationCache[dd];
66375                     }
66376                 }
66377                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
66378             }
66379         }
66380         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
66381         this.bind(null, null);
66382         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
66383     },
66384
66385     handleLockChange : function(){
66386         this.refresh(true);
66387     },
66388
66389     onDenyColumnLock : function(){
66390
66391     },
66392
66393     onDenyColumnHide : function(){
66394
66395     },
66396
66397     handleHdMenuClick : function(item){
66398         var index = this.hdCtxIndex;
66399         var cm = this.cm, ds = this.ds;
66400         switch(item.id){
66401             case "asc":
66402                 ds.sort(cm.getDataIndex(index), "ASC");
66403                 break;
66404             case "desc":
66405                 ds.sort(cm.getDataIndex(index), "DESC");
66406                 break;
66407             case "lock":
66408                 var lc = cm.getLockedCount();
66409                 if(cm.getColumnCount(true) <= lc+1){
66410                     this.onDenyColumnLock();
66411                     return;
66412                 }
66413                 if(lc != index){
66414                     cm.setLocked(index, true, true);
66415                     cm.moveColumn(index, lc);
66416                     this.grid.fireEvent("columnmove", index, lc);
66417                 }else{
66418                     cm.setLocked(index, true);
66419                 }
66420             break;
66421             case "unlock":
66422                 var lc = cm.getLockedCount();
66423                 if((lc-1) != index){
66424                     cm.setLocked(index, false, true);
66425                     cm.moveColumn(index, lc-1);
66426                     this.grid.fireEvent("columnmove", index, lc-1);
66427                 }else{
66428                     cm.setLocked(index, false);
66429                 }
66430             break;
66431             case 'wider': // used to expand cols on touch..
66432             case 'narrow':
66433                 var cw = cm.getColumnWidth(index);
66434                 cw += (item.id == 'wider' ? 1 : -1) * 50;
66435                 cw = Math.max(0, cw);
66436                 cw = Math.min(cw,4000);
66437                 cm.setColumnWidth(index, cw);
66438                 break;
66439                 
66440             default:
66441                 index = cm.getIndexById(item.id.substr(4));
66442                 if(index != -1){
66443                     if(item.checked && cm.getColumnCount(true) <= 1){
66444                         this.onDenyColumnHide();
66445                         return false;
66446                     }
66447                     cm.setHidden(index, item.checked);
66448                 }
66449         }
66450         return true;
66451     },
66452
66453     beforeColMenuShow : function(){
66454         var cm = this.cm,  colCount = cm.getColumnCount();
66455         this.colMenu.removeAll();
66456         
66457         var items = [];
66458         for(var i = 0; i < colCount; i++){
66459             items.push({
66460                 id: "col-"+cm.getColumnId(i),
66461                 text: cm.getColumnHeader(i),
66462                 checked: !cm.isHidden(i),
66463                 hideOnClick:false
66464             });
66465         }
66466         
66467         if (this.grid.sortColMenu) {
66468             items.sort(function(a,b) {
66469                 if (a.text == b.text) {
66470                     return 0;
66471                 }
66472                 return a.text.toUpperCase() > b.text.toUpperCase() ? 1 : -1;
66473             });
66474         }
66475         
66476         for(var i = 0; i < colCount; i++){
66477             this.colMenu.add(new Roo.menu.CheckItem(items[i]));
66478         }
66479     },
66480
66481     handleHdCtx : function(g, index, e){
66482         e.stopEvent();
66483         var hd = this.getHeaderCell(index);
66484         this.hdCtxIndex = index;
66485         var ms = this.hmenu.items, cm = this.cm;
66486         ms.get("asc").setDisabled(!cm.isSortable(index));
66487         ms.get("desc").setDisabled(!cm.isSortable(index));
66488         if(this.grid.enableColLock !== false){
66489             ms.get("lock").setDisabled(cm.isLocked(index));
66490             ms.get("unlock").setDisabled(!cm.isLocked(index));
66491         }
66492         this.hmenu.show(hd, "tl-bl");
66493     },
66494
66495     handleHdOver : function(e){
66496         var hd = this.findHeaderCell(e.getTarget());
66497         if(hd && !this.headersDisabled){
66498             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
66499                this.fly(hd).addClass("x-grid-hd-over");
66500             }
66501         }
66502     },
66503
66504     handleHdOut : function(e){
66505         var hd = this.findHeaderCell(e.getTarget());
66506         if(hd){
66507             this.fly(hd).removeClass("x-grid-hd-over");
66508         }
66509     },
66510
66511     handleSplitDblClick : function(e, t){
66512         var i = this.getCellIndex(t);
66513         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
66514             this.autoSizeColumn(i, true);
66515             this.layout();
66516         }
66517     },
66518
66519     render : function(){
66520
66521         var cm = this.cm;
66522         var colCount = cm.getColumnCount();
66523
66524         if(this.grid.monitorWindowResize === true){
66525             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
66526         }
66527         var header = this.renderHeaders();
66528         var body = this.templates.body.apply({rows:""});
66529         var html = this.templates.master.apply({
66530             lockedBody: body,
66531             body: body,
66532             lockedHeader: header[0],
66533             header: header[1]
66534         });
66535
66536         //this.updateColumns();
66537
66538         this.grid.getGridEl().dom.innerHTML = html;
66539
66540         this.initElements();
66541         
66542         // a kludge to fix the random scolling effect in webkit
66543         this.el.on("scroll", function() {
66544             this.el.dom.scrollTop=0; // hopefully not recursive..
66545         },this);
66546
66547         this.scroller.on("scroll", this.handleScroll, this);
66548         this.lockedBody.on("mousewheel", this.handleWheel, this);
66549         this.mainBody.on("mousewheel", this.handleWheel, this);
66550
66551         this.mainHd.on("mouseover", this.handleHdOver, this);
66552         this.mainHd.on("mouseout", this.handleHdOut, this);
66553         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
66554                 {delegate: "."+this.splitClass});
66555
66556         this.lockedHd.on("mouseover", this.handleHdOver, this);
66557         this.lockedHd.on("mouseout", this.handleHdOut, this);
66558         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
66559                 {delegate: "."+this.splitClass});
66560
66561         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
66562             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
66563         }
66564
66565         this.updateSplitters();
66566
66567         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
66568             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
66569             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
66570         }
66571
66572         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
66573             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
66574             this.hmenu.add(
66575                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
66576                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
66577             );
66578             if(this.grid.enableColLock !== false){
66579                 this.hmenu.add('-',
66580                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
66581                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
66582                 );
66583             }
66584             if (Roo.isTouch) {
66585                  this.hmenu.add('-',
66586                     {id:"wider", text: this.columnsWiderText},
66587                     {id:"narrow", text: this.columnsNarrowText }
66588                 );
66589                 
66590                  
66591             }
66592             
66593             if(this.grid.enableColumnHide !== false){
66594
66595                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
66596                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
66597                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
66598
66599                 this.hmenu.add('-',
66600                     {id:"columns", text: this.columnsText, menu: this.colMenu}
66601                 );
66602             }
66603             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
66604
66605             this.grid.on("headercontextmenu", this.handleHdCtx, this);
66606         }
66607
66608         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
66609             this.dd = new Roo.grid.GridDragZone(this.grid, {
66610                 ddGroup : this.grid.ddGroup || 'GridDD'
66611             });
66612             
66613         }
66614
66615         /*
66616         for(var i = 0; i < colCount; i++){
66617             if(cm.isHidden(i)){
66618                 this.hideColumn(i);
66619             }
66620             if(cm.config[i].align){
66621                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
66622                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
66623             }
66624         }*/
66625         
66626         this.updateHeaderSortState();
66627
66628         this.beforeInitialResize();
66629         this.layout(true);
66630
66631         // two part rendering gives faster view to the user
66632         this.renderPhase2.defer(1, this);
66633     },
66634
66635     renderPhase2 : function(){
66636         // render the rows now
66637         this.refresh();
66638         if(this.grid.autoSizeColumns){
66639             this.autoSizeColumns();
66640         }
66641     },
66642
66643     beforeInitialResize : function(){
66644
66645     },
66646
66647     onColumnSplitterMoved : function(i, w){
66648         this.userResized = true;
66649         var cm = this.grid.colModel;
66650         cm.setColumnWidth(i, w, true);
66651         var cid = cm.getColumnId(i);
66652         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
66653         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
66654         this.updateSplitters();
66655         this.layout();
66656         this.grid.fireEvent("columnresize", i, w);
66657     },
66658
66659     syncRowHeights : function(startIndex, endIndex){
66660         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
66661             startIndex = startIndex || 0;
66662             var mrows = this.getBodyTable().rows;
66663             var lrows = this.getLockedTable().rows;
66664             var len = mrows.length-1;
66665             endIndex = Math.min(endIndex || len, len);
66666             for(var i = startIndex; i <= endIndex; i++){
66667                 var m = mrows[i], l = lrows[i];
66668                 var h = Math.max(m.offsetHeight, l.offsetHeight);
66669                 m.style.height = l.style.height = h + "px";
66670             }
66671         }
66672     },
66673
66674     layout : function(initialRender, is2ndPass)
66675     {
66676         var g = this.grid;
66677         var auto = g.autoHeight;
66678         var scrollOffset = 16;
66679         var c = g.getGridEl(), cm = this.cm,
66680                 expandCol = g.autoExpandColumn,
66681                 gv = this;
66682         //c.beginMeasure();
66683
66684         if(!c.dom.offsetWidth){ // display:none?
66685             if(initialRender){
66686                 this.lockedWrap.show();
66687                 this.mainWrap.show();
66688             }
66689             return;
66690         }
66691
66692         var hasLock = this.cm.isLocked(0);
66693
66694         var tbh = this.headerPanel.getHeight();
66695         var bbh = this.footerPanel.getHeight();
66696
66697         if(auto){
66698             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
66699             var newHeight = ch + c.getBorderWidth("tb");
66700             if(g.maxHeight){
66701                 newHeight = Math.min(g.maxHeight, newHeight);
66702             }
66703             c.setHeight(newHeight);
66704         }
66705
66706         if(g.autoWidth){
66707             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
66708         }
66709
66710         var s = this.scroller;
66711
66712         var csize = c.getSize(true);
66713
66714         this.el.setSize(csize.width, csize.height);
66715
66716         this.headerPanel.setWidth(csize.width);
66717         this.footerPanel.setWidth(csize.width);
66718
66719         var hdHeight = this.mainHd.getHeight();
66720         var vw = csize.width;
66721         var vh = csize.height - (tbh + bbh);
66722
66723         s.setSize(vw, vh);
66724
66725         var bt = this.getBodyTable();
66726         
66727         if(cm.getLockedCount() == cm.config.length){
66728             bt = this.getLockedTable();
66729         }
66730         
66731         var ltWidth = hasLock ?
66732                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
66733
66734         var scrollHeight = bt.offsetHeight;
66735         var scrollWidth = ltWidth + bt.offsetWidth;
66736         var vscroll = false, hscroll = false;
66737
66738         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
66739
66740         var lw = this.lockedWrap, mw = this.mainWrap;
66741         var lb = this.lockedBody, mb = this.mainBody;
66742
66743         setTimeout(function(){
66744             var t = s.dom.offsetTop;
66745             var w = s.dom.clientWidth,
66746                 h = s.dom.clientHeight;
66747
66748             lw.setTop(t);
66749             lw.setSize(ltWidth, h);
66750
66751             mw.setLeftTop(ltWidth, t);
66752             mw.setSize(w-ltWidth, h);
66753
66754             lb.setHeight(h-hdHeight);
66755             mb.setHeight(h-hdHeight);
66756
66757             if(is2ndPass !== true && !gv.userResized && expandCol){
66758                 // high speed resize without full column calculation
66759                 
66760                 var ci = cm.getIndexById(expandCol);
66761                 if (ci < 0) {
66762                     ci = cm.findColumnIndex(expandCol);
66763                 }
66764                 ci = Math.max(0, ci); // make sure it's got at least the first col.
66765                 var expandId = cm.getColumnId(ci);
66766                 var  tw = cm.getTotalWidth(false);
66767                 var currentWidth = cm.getColumnWidth(ci);
66768                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
66769                 if(currentWidth != cw){
66770                     cm.setColumnWidth(ci, cw, true);
66771                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
66772                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
66773                     gv.updateSplitters();
66774                     gv.layout(false, true);
66775                 }
66776             }
66777
66778             if(initialRender){
66779                 lw.show();
66780                 mw.show();
66781             }
66782             //c.endMeasure();
66783         }, 10);
66784     },
66785
66786     onWindowResize : function(){
66787         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
66788             return;
66789         }
66790         this.layout();
66791     },
66792
66793     appendFooter : function(parentEl){
66794         return null;
66795     },
66796
66797     sortAscText : "Sort Ascending",
66798     sortDescText : "Sort Descending",
66799     lockText : "Lock Column",
66800     unlockText : "Unlock Column",
66801     columnsText : "Columns",
66802  
66803     columnsWiderText : "Wider",
66804     columnsNarrowText : "Thinner"
66805 });
66806
66807
66808 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
66809     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
66810     this.proxy.el.addClass('x-grid3-col-dd');
66811 };
66812
66813 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
66814     handleMouseDown : function(e){
66815
66816     },
66817
66818     callHandleMouseDown : function(e){
66819         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
66820     }
66821 });
66822 /*
66823  * Based on:
66824  * Ext JS Library 1.1.1
66825  * Copyright(c) 2006-2007, Ext JS, LLC.
66826  *
66827  * Originally Released Under LGPL - original licence link has changed is not relivant.
66828  *
66829  * Fork - LGPL
66830  * <script type="text/javascript">
66831  */
66832  /**
66833  * @extends Roo.dd.DDProxy
66834  * @class Roo.grid.SplitDragZone
66835  * Support for Column Header resizing
66836  * @constructor
66837  * @param {Object} config
66838  */
66839 // private
66840 // This is a support class used internally by the Grid components
66841 Roo.grid.SplitDragZone = function(grid, hd, hd2){
66842     this.grid = grid;
66843     this.view = grid.getView();
66844     this.proxy = this.view.resizeProxy;
66845     Roo.grid.SplitDragZone.superclass.constructor.call(
66846         this,
66847         hd, // ID
66848         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
66849         {  // CONFIG
66850             dragElId : Roo.id(this.proxy.dom),
66851             resizeFrame:false
66852         }
66853     );
66854     
66855     this.setHandleElId(Roo.id(hd));
66856     if (hd2 !== false) {
66857         this.setOuterHandleElId(Roo.id(hd2));
66858     }
66859     
66860     this.scroll = false;
66861 };
66862 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
66863     fly: Roo.Element.fly,
66864
66865     b4StartDrag : function(x, y){
66866         this.view.headersDisabled = true;
66867         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
66868                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
66869         );
66870         this.proxy.setHeight(h);
66871         
66872         // for old system colWidth really stored the actual width?
66873         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
66874         // which in reality did not work.. - it worked only for fixed sizes
66875         // for resizable we need to use actual sizes.
66876         var w = this.cm.getColumnWidth(this.cellIndex);
66877         if (!this.view.mainWrap) {
66878             // bootstrap.
66879             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
66880         }
66881         
66882         
66883         
66884         // this was w-this.grid.minColumnWidth;
66885         // doesnt really make sense? - w = thie curren width or the rendered one?
66886         var minw = Math.max(w-this.grid.minColumnWidth, 0);
66887         this.resetConstraints();
66888         this.setXConstraint(minw, 1000);
66889         this.setYConstraint(0, 0);
66890         this.minX = x - minw;
66891         this.maxX = x + 1000;
66892         this.startPos = x;
66893         if (!this.view.mainWrap) { // this is Bootstrap code..
66894             this.getDragEl().style.display='block';
66895         }
66896         
66897         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
66898     },
66899
66900
66901     handleMouseDown : function(e){
66902         ev = Roo.EventObject.setEvent(e);
66903         var t = this.fly(ev.getTarget());
66904         if(t.hasClass("x-grid-split")){
66905             this.cellIndex = this.view.getCellIndex(t.dom);
66906             this.split = t.dom;
66907             this.cm = this.grid.colModel;
66908             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
66909                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
66910             }
66911         }
66912     },
66913
66914     endDrag : function(e){
66915         this.view.headersDisabled = false;
66916         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
66917         var diff = endX - this.startPos;
66918         // 
66919         var w = this.cm.getColumnWidth(this.cellIndex);
66920         if (!this.view.mainWrap) {
66921             w = 0;
66922         }
66923         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
66924     },
66925
66926     autoOffset : function(){
66927         this.setDelta(0,0);
66928     }
66929 });/*
66930  * Based on:
66931  * Ext JS Library 1.1.1
66932  * Copyright(c) 2006-2007, Ext JS, LLC.
66933  *
66934  * Originally Released Under LGPL - original licence link has changed is not relivant.
66935  *
66936  * Fork - LGPL
66937  * <script type="text/javascript">
66938  */
66939  
66940 // private
66941 // This is a support class used internally by the Grid components
66942 Roo.grid.GridDragZone = function(grid, config){
66943     this.view = grid.getView();
66944     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
66945     if(this.view.lockedBody){
66946         this.setHandleElId(Roo.id(this.view.mainBody.dom));
66947         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
66948     }
66949     this.scroll = false;
66950     this.grid = grid;
66951     this.ddel = document.createElement('div');
66952     this.ddel.className = 'x-grid-dd-wrap';
66953 };
66954
66955 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
66956     ddGroup : "GridDD",
66957
66958     getDragData : function(e){
66959         var t = Roo.lib.Event.getTarget(e);
66960         var rowIndex = this.view.findRowIndex(t);
66961         var sm = this.grid.selModel;
66962             
66963         //Roo.log(rowIndex);
66964         
66965         if (sm.getSelectedCell) {
66966             // cell selection..
66967             if (!sm.getSelectedCell()) {
66968                 return false;
66969             }
66970             if (rowIndex != sm.getSelectedCell()[0]) {
66971                 return false;
66972             }
66973         
66974         }
66975         if (sm.getSelections && sm.getSelections().length < 1) {
66976             return false;
66977         }
66978         
66979         
66980         // before it used to all dragging of unseleted... - now we dont do that.
66981         if(rowIndex !== false){
66982             
66983             // if editorgrid.. 
66984             
66985             
66986             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
66987                
66988             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
66989               //  
66990             //}
66991             if (e.hasModifier()){
66992                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
66993             }
66994             
66995             Roo.log("getDragData");
66996             
66997             return {
66998                 grid: this.grid,
66999                 ddel: this.ddel,
67000                 rowIndex: rowIndex,
67001                 selections: sm.getSelections ? sm.getSelections() : (
67002                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
67003             };
67004         }
67005         return false;
67006     },
67007     
67008     
67009     onInitDrag : function(e){
67010         var data = this.dragData;
67011         this.ddel.innerHTML = this.grid.getDragDropText();
67012         this.proxy.update(this.ddel);
67013         // fire start drag?
67014     },
67015
67016     afterRepair : function(){
67017         this.dragging = false;
67018     },
67019
67020     getRepairXY : function(e, data){
67021         return false;
67022     },
67023
67024     onEndDrag : function(data, e){
67025         // fire end drag?
67026     },
67027
67028     onValidDrop : function(dd, e, id){
67029         // fire drag drop?
67030         this.hideProxy();
67031     },
67032
67033     beforeInvalidDrop : function(e, id){
67034
67035     }
67036 });/*
67037  * Based on:
67038  * Ext JS Library 1.1.1
67039  * Copyright(c) 2006-2007, Ext JS, LLC.
67040  *
67041  * Originally Released Under LGPL - original licence link has changed is not relivant.
67042  *
67043  * Fork - LGPL
67044  * <script type="text/javascript">
67045  */
67046  
67047
67048 /**
67049  * @class Roo.grid.ColumnModel
67050  * @extends Roo.util.Observable
67051  * This is the default implementation of a ColumnModel used by the Grid. It defines
67052  * the columns in the grid.
67053  * <br>Usage:<br>
67054  <pre><code>
67055  var colModel = new Roo.grid.ColumnModel([
67056         {header: "Ticker", width: 60, sortable: true, locked: true},
67057         {header: "Company Name", width: 150, sortable: true},
67058         {header: "Market Cap.", width: 100, sortable: true},
67059         {header: "$ Sales", width: 100, sortable: true, renderer: money},
67060         {header: "Employees", width: 100, sortable: true, resizable: false}
67061  ]);
67062  </code></pre>
67063  * <p>
67064  
67065  * The config options listed for this class are options which may appear in each
67066  * individual column definition.
67067  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
67068  * @constructor
67069  * @param {Object} config An Array of column config objects. See this class's
67070  * config objects for details.
67071 */
67072 Roo.grid.ColumnModel = function(config){
67073         /**
67074      * The config passed into the constructor
67075      */
67076     this.config = []; //config;
67077     this.lookup = {};
67078
67079     // if no id, create one
67080     // if the column does not have a dataIndex mapping,
67081     // map it to the order it is in the config
67082     for(var i = 0, len = config.length; i < len; i++){
67083         this.addColumn(config[i]);
67084         
67085     }
67086
67087     /**
67088      * The width of columns which have no width specified (defaults to 100)
67089      * @type Number
67090      */
67091     this.defaultWidth = 100;
67092
67093     /**
67094      * Default sortable of columns which have no sortable specified (defaults to false)
67095      * @type Boolean
67096      */
67097     this.defaultSortable = false;
67098
67099     this.addEvents({
67100         /**
67101              * @event widthchange
67102              * Fires when the width of a column changes.
67103              * @param {ColumnModel} this
67104              * @param {Number} columnIndex The column index
67105              * @param {Number} newWidth The new width
67106              */
67107             "widthchange": true,
67108         /**
67109              * @event headerchange
67110              * Fires when the text of a header changes.
67111              * @param {ColumnModel} this
67112              * @param {Number} columnIndex The column index
67113              * @param {Number} newText The new header text
67114              */
67115             "headerchange": true,
67116         /**
67117              * @event hiddenchange
67118              * Fires when a column is hidden or "unhidden".
67119              * @param {ColumnModel} this
67120              * @param {Number} columnIndex The column index
67121              * @param {Boolean} hidden true if hidden, false otherwise
67122              */
67123             "hiddenchange": true,
67124             /**
67125          * @event columnmoved
67126          * Fires when a column is moved.
67127          * @param {ColumnModel} this
67128          * @param {Number} oldIndex
67129          * @param {Number} newIndex
67130          */
67131         "columnmoved" : true,
67132         /**
67133          * @event columlockchange
67134          * Fires when a column's locked state is changed
67135          * @param {ColumnModel} this
67136          * @param {Number} colIndex
67137          * @param {Boolean} locked true if locked
67138          */
67139         "columnlockchange" : true
67140     });
67141     Roo.grid.ColumnModel.superclass.constructor.call(this);
67142 };
67143 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
67144     /**
67145      * @cfg {String} header [required] The header text to display in the Grid view.
67146      */
67147         /**
67148      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
67149      */
67150         /**
67151      * @cfg {String} smHeader Header at Bootsrap Small width
67152      */
67153         /**
67154      * @cfg {String} mdHeader Header at Bootsrap Medium width
67155      */
67156         /**
67157      * @cfg {String} lgHeader Header at Bootsrap Large width
67158      */
67159         /**
67160      * @cfg {String} xlHeader Header at Bootsrap extra Large width
67161      */
67162     /**
67163      * @cfg {String} dataIndex  The name of the field in the grid's {@link Roo.data.Store}'s
67164      * {@link Roo.data.Record} definition from which to draw the column's value. If not
67165      * specified, the column's index is used as an index into the Record's data Array.
67166      */
67167     /**
67168      * @cfg {Number} width  The initial width in pixels of the column. Using this
67169      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
67170      */
67171     /**
67172      * @cfg {Boolean} sortable True if sorting is to be allowed on this column.
67173      * Defaults to the value of the {@link #defaultSortable} property.
67174      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
67175      */
67176     /**
67177      * @cfg {Boolean} locked  True to lock the column in place while scrolling the Grid.  Defaults to false.
67178      */
67179     /**
67180      * @cfg {Boolean} fixed  True if the column width cannot be changed.  Defaults to false.
67181      */
67182     /**
67183      * @cfg {Boolean} resizable  False to disable column resizing. Defaults to true.
67184      */
67185     /**
67186      * @cfg {Boolean} hidden  True to hide the column. Defaults to false.
67187      */
67188     /**
67189      * @cfg {Function} renderer A function used to generate HTML markup for a cell
67190      * given the cell's data value. See {@link #setRenderer}. If not specified, the
67191      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
67192      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
67193      */
67194        /**
67195      * @cfg {Roo.grid.GridEditor} editor  For grid editors - returns the grid editor 
67196      */
67197     /**
67198      * @cfg {String} align (left|right) Set the CSS text-align property of the column.  Defaults to undefined (left).
67199      */
67200     /**
67201      * @cfg {String} valign (top|bottom|middle) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined (middle)
67202      */
67203     /**
67204      * @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)
67205      */
67206     /**
67207      * @cfg {String} tooltip mouse over tooltip text
67208      */
67209     /**
67210      * @cfg {Number} xs  can be '0' for hidden at this size (number less than 12)
67211      */
67212     /**
67213      * @cfg {Number} sm can be '0' for hidden at this size (number less than 12)
67214      */
67215     /**
67216      * @cfg {Number} md can be '0' for hidden at this size (number less than 12)
67217      */
67218     /**
67219      * @cfg {Number} lg   can be '0' for hidden at this size (number less than 12)
67220      */
67221         /**
67222      * @cfg {Number} xl   can be '0' for hidden at this size (number less than 12)
67223      */
67224     /**
67225      * Returns the id of the column at the specified index.
67226      * @param {Number} index The column index
67227      * @return {String} the id
67228      */
67229     getColumnId : function(index){
67230         return this.config[index].id;
67231     },
67232
67233     /**
67234      * Returns the column for a specified id.
67235      * @param {String} id The column id
67236      * @return {Object} the column
67237      */
67238     getColumnById : function(id){
67239         return this.lookup[id];
67240     },
67241
67242     
67243     /**
67244      * Returns the column Object for a specified dataIndex.
67245      * @param {String} dataIndex The column dataIndex
67246      * @return {Object|Boolean} the column or false if not found
67247      */
67248     getColumnByDataIndex: function(dataIndex){
67249         var index = this.findColumnIndex(dataIndex);
67250         return index > -1 ? this.config[index] : false;
67251     },
67252     
67253     /**
67254      * Returns the index for a specified column id.
67255      * @param {String} id The column id
67256      * @return {Number} the index, or -1 if not found
67257      */
67258     getIndexById : function(id){
67259         for(var i = 0, len = this.config.length; i < len; i++){
67260             if(this.config[i].id == id){
67261                 return i;
67262             }
67263         }
67264         return -1;
67265     },
67266     
67267     /**
67268      * Returns the index for a specified column dataIndex.
67269      * @param {String} dataIndex The column dataIndex
67270      * @return {Number} the index, or -1 if not found
67271      */
67272     
67273     findColumnIndex : function(dataIndex){
67274         for(var i = 0, len = this.config.length; i < len; i++){
67275             if(this.config[i].dataIndex == dataIndex){
67276                 return i;
67277             }
67278         }
67279         return -1;
67280     },
67281     
67282     
67283     moveColumn : function(oldIndex, newIndex){
67284         var c = this.config[oldIndex];
67285         this.config.splice(oldIndex, 1);
67286         this.config.splice(newIndex, 0, c);
67287         this.dataMap = null;
67288         this.fireEvent("columnmoved", this, oldIndex, newIndex);
67289     },
67290
67291     isLocked : function(colIndex){
67292         return this.config[colIndex].locked === true;
67293     },
67294
67295     setLocked : function(colIndex, value, suppressEvent){
67296         if(this.isLocked(colIndex) == value){
67297             return;
67298         }
67299         this.config[colIndex].locked = value;
67300         if(!suppressEvent){
67301             this.fireEvent("columnlockchange", this, colIndex, value);
67302         }
67303     },
67304
67305     getTotalLockedWidth : function(){
67306         var totalWidth = 0;
67307         for(var i = 0; i < this.config.length; i++){
67308             if(this.isLocked(i) && !this.isHidden(i)){
67309                 this.totalWidth += this.getColumnWidth(i);
67310             }
67311         }
67312         return totalWidth;
67313     },
67314
67315     getLockedCount : function(){
67316         for(var i = 0, len = this.config.length; i < len; i++){
67317             if(!this.isLocked(i)){
67318                 return i;
67319             }
67320         }
67321         
67322         return this.config.length;
67323     },
67324
67325     /**
67326      * Returns the number of columns.
67327      * @return {Number}
67328      */
67329     getColumnCount : function(visibleOnly){
67330         if(visibleOnly === true){
67331             var c = 0;
67332             for(var i = 0, len = this.config.length; i < len; i++){
67333                 if(!this.isHidden(i)){
67334                     c++;
67335                 }
67336             }
67337             return c;
67338         }
67339         return this.config.length;
67340     },
67341
67342     /**
67343      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
67344      * @param {Function} fn
67345      * @param {Object} scope (optional)
67346      * @return {Array} result
67347      */
67348     getColumnsBy : function(fn, scope){
67349         var r = [];
67350         for(var i = 0, len = this.config.length; i < len; i++){
67351             var c = this.config[i];
67352             if(fn.call(scope||this, c, i) === true){
67353                 r[r.length] = c;
67354             }
67355         }
67356         return r;
67357     },
67358
67359     /**
67360      * Returns true if the specified column is sortable.
67361      * @param {Number} col The column index
67362      * @return {Boolean}
67363      */
67364     isSortable : function(col){
67365         if(typeof this.config[col].sortable == "undefined"){
67366             return this.defaultSortable;
67367         }
67368         return this.config[col].sortable;
67369     },
67370
67371     /**
67372      * Returns the rendering (formatting) function defined for the column.
67373      * @param {Number} col The column index.
67374      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
67375      */
67376     getRenderer : function(col){
67377         if(!this.config[col].renderer){
67378             return Roo.grid.ColumnModel.defaultRenderer;
67379         }
67380         return this.config[col].renderer;
67381     },
67382
67383     /**
67384      * Sets the rendering (formatting) function for a column.
67385      * @param {Number} col The column index
67386      * @param {Function} fn The function to use to process the cell's raw data
67387      * to return HTML markup for the grid view. The render function is called with
67388      * the following parameters:<ul>
67389      * <li>Data value.</li>
67390      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
67391      * <li>css A CSS style string to apply to the table cell.</li>
67392      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
67393      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
67394      * <li>Row index</li>
67395      * <li>Column index</li>
67396      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
67397      */
67398     setRenderer : function(col, fn){
67399         this.config[col].renderer = fn;
67400     },
67401
67402     /**
67403      * Returns the width for the specified column.
67404      * @param {Number} col The column index
67405      * @param (optional) {String} gridSize bootstrap width size.
67406      * @return {Number}
67407      */
67408     getColumnWidth : function(col, gridSize)
67409         {
67410                 var cfg = this.config[col];
67411                 
67412                 if (typeof(gridSize) == 'undefined') {
67413                         return cfg.width * 1 || this.defaultWidth;
67414                 }
67415                 if (gridSize === false) { // if we set it..
67416                         return cfg.width || false;
67417                 }
67418                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
67419                 
67420                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
67421                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
67422                                 continue;
67423                         }
67424                         return cfg[ sizes[i] ];
67425                 }
67426                 return 1;
67427                 
67428     },
67429
67430     /**
67431      * Sets the width for a column.
67432      * @param {Number} col The column index
67433      * @param {Number} width The new width
67434      */
67435     setColumnWidth : function(col, width, suppressEvent){
67436         this.config[col].width = width;
67437         this.totalWidth = null;
67438         if(!suppressEvent){
67439              this.fireEvent("widthchange", this, col, width);
67440         }
67441     },
67442
67443     /**
67444      * Returns the total width of all columns.
67445      * @param {Boolean} includeHidden True to include hidden column widths
67446      * @return {Number}
67447      */
67448     getTotalWidth : function(includeHidden){
67449         if(!this.totalWidth){
67450             this.totalWidth = 0;
67451             for(var i = 0, len = this.config.length; i < len; i++){
67452                 if(includeHidden || !this.isHidden(i)){
67453                     this.totalWidth += this.getColumnWidth(i);
67454                 }
67455             }
67456         }
67457         return this.totalWidth;
67458     },
67459
67460     /**
67461      * Returns the header for the specified column.
67462      * @param {Number} col The column index
67463      * @return {String}
67464      */
67465     getColumnHeader : function(col){
67466         return this.config[col].header;
67467     },
67468
67469     /**
67470      * Sets the header for a column.
67471      * @param {Number} col The column index
67472      * @param {String} header The new header
67473      */
67474     setColumnHeader : function(col, header){
67475         this.config[col].header = header;
67476         this.fireEvent("headerchange", this, col, header);
67477     },
67478
67479     /**
67480      * Returns the tooltip for the specified column.
67481      * @param {Number} col The column index
67482      * @return {String}
67483      */
67484     getColumnTooltip : function(col){
67485             return this.config[col].tooltip;
67486     },
67487     /**
67488      * Sets the tooltip for a column.
67489      * @param {Number} col The column index
67490      * @param {String} tooltip The new tooltip
67491      */
67492     setColumnTooltip : function(col, tooltip){
67493             this.config[col].tooltip = tooltip;
67494     },
67495
67496     /**
67497      * Returns the dataIndex for the specified column.
67498      * @param {Number} col The column index
67499      * @return {Number}
67500      */
67501     getDataIndex : function(col){
67502         return this.config[col].dataIndex;
67503     },
67504
67505     /**
67506      * Sets the dataIndex for a column.
67507      * @param {Number} col The column index
67508      * @param {Number} dataIndex The new dataIndex
67509      */
67510     setDataIndex : function(col, dataIndex){
67511         this.config[col].dataIndex = dataIndex;
67512     },
67513
67514     
67515     
67516     /**
67517      * Returns true if the cell is editable.
67518      * @param {Number} colIndex The column index
67519      * @param {Number} rowIndex The row index - this is nto actually used..?
67520      * @return {Boolean}
67521      */
67522     isCellEditable : function(colIndex, rowIndex){
67523         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
67524     },
67525
67526     /**
67527      * Returns the editor defined for the cell/column.
67528      * return false or null to disable editing.
67529      * @param {Number} colIndex The column index
67530      * @param {Number} rowIndex The row index
67531      * @return {Object}
67532      */
67533     getCellEditor : function(colIndex, rowIndex){
67534         return this.config[colIndex].editor;
67535     },
67536
67537     /**
67538      * Sets if a column is editable.
67539      * @param {Number} col The column index
67540      * @param {Boolean} editable True if the column is editable
67541      */
67542     setEditable : function(col, editable){
67543         this.config[col].editable = editable;
67544     },
67545
67546
67547     /**
67548      * Returns true if the column is hidden.
67549      * @param {Number} colIndex The column index
67550      * @return {Boolean}
67551      */
67552     isHidden : function(colIndex){
67553         return this.config[colIndex].hidden;
67554     },
67555
67556
67557     /**
67558      * Returns true if the column width cannot be changed
67559      */
67560     isFixed : function(colIndex){
67561         return this.config[colIndex].fixed;
67562     },
67563
67564     /**
67565      * Returns true if the column can be resized
67566      * @return {Boolean}
67567      */
67568     isResizable : function(colIndex){
67569         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
67570     },
67571     /**
67572      * Sets if a column is hidden.
67573      * @param {Number} colIndex The column index
67574      * @param {Boolean} hidden True if the column is hidden
67575      */
67576     setHidden : function(colIndex, hidden){
67577         this.config[colIndex].hidden = hidden;
67578         this.totalWidth = null;
67579         this.fireEvent("hiddenchange", this, colIndex, hidden);
67580     },
67581
67582     /**
67583      * Sets the editor for a column.
67584      * @param {Number} col The column index
67585      * @param {Object} editor The editor object
67586      */
67587     setEditor : function(col, editor){
67588         this.config[col].editor = editor;
67589     },
67590     /**
67591      * Add a column (experimental...) - defaults to adding to the end..
67592      * @param {Object} config 
67593     */
67594     addColumn : function(c)
67595     {
67596     
67597         var i = this.config.length;
67598         this.config[i] = c;
67599         
67600         if(typeof c.dataIndex == "undefined"){
67601             c.dataIndex = i;
67602         }
67603         if(typeof c.renderer == "string"){
67604             c.renderer = Roo.util.Format[c.renderer];
67605         }
67606         if(typeof c.id == "undefined"){
67607             c.id = Roo.id();
67608         }
67609         if(c.editor && c.editor.xtype){
67610             c.editor  = Roo.factory(c.editor, Roo.grid);
67611         }
67612         if(c.editor && c.editor.isFormField){
67613             c.editor = new Roo.grid.GridEditor(c.editor);
67614         }
67615         this.lookup[c.id] = c;
67616     }
67617     
67618 });
67619
67620 Roo.grid.ColumnModel.defaultRenderer = function(value)
67621 {
67622     if(typeof value == "object") {
67623         return value;
67624     }
67625         if(typeof value == "string" && value.length < 1){
67626             return "&#160;";
67627         }
67628     
67629         return String.format("{0}", value);
67630 };
67631
67632 // Alias for backwards compatibility
67633 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
67634 /*
67635  * Based on:
67636  * Ext JS Library 1.1.1
67637  * Copyright(c) 2006-2007, Ext JS, LLC.
67638  *
67639  * Originally Released Under LGPL - original licence link has changed is not relivant.
67640  *
67641  * Fork - LGPL
67642  * <script type="text/javascript">
67643  */
67644
67645 /**
67646  * @class Roo.grid.AbstractSelectionModel
67647  * @extends Roo.util.Observable
67648  * @abstract
67649  * Abstract base class for grid SelectionModels.  It provides the interface that should be
67650  * implemented by descendant classes.  This class should not be directly instantiated.
67651  * @constructor
67652  */
67653 Roo.grid.AbstractSelectionModel = function(){
67654     this.locked = false;
67655     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
67656 };
67657
67658 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
67659     /** @ignore Called by the grid automatically. Do not call directly. */
67660     init : function(grid){
67661         this.grid = grid;
67662         this.initEvents();
67663     },
67664
67665     /**
67666      * Locks the selections.
67667      */
67668     lock : function(){
67669         this.locked = true;
67670     },
67671
67672     /**
67673      * Unlocks the selections.
67674      */
67675     unlock : function(){
67676         this.locked = false;
67677     },
67678
67679     /**
67680      * Returns true if the selections are locked.
67681      * @return {Boolean}
67682      */
67683     isLocked : function(){
67684         return this.locked;
67685     }
67686 });/*
67687  * Based on:
67688  * Ext JS Library 1.1.1
67689  * Copyright(c) 2006-2007, Ext JS, LLC.
67690  *
67691  * Originally Released Under LGPL - original licence link has changed is not relivant.
67692  *
67693  * Fork - LGPL
67694  * <script type="text/javascript">
67695  */
67696 /**
67697  * @extends Roo.grid.AbstractSelectionModel
67698  * @class Roo.grid.RowSelectionModel
67699  * The default SelectionModel used by {@link Roo.grid.Grid}.
67700  * It supports multiple selections and keyboard selection/navigation. 
67701  * @constructor
67702  * @param {Object} config
67703  */
67704 Roo.grid.RowSelectionModel = function(config){
67705     Roo.apply(this, config);
67706     this.selections = new Roo.util.MixedCollection(false, function(o){
67707         return o.id;
67708     });
67709
67710     this.last = false;
67711     this.lastActive = false;
67712
67713     this.addEvents({
67714         /**
67715         * @event selectionchange
67716         * Fires when the selection changes
67717         * @param {SelectionModel} this
67718         */
67719        "selectionchange" : true,
67720        /**
67721         * @event afterselectionchange
67722         * Fires after the selection changes (eg. by key press or clicking)
67723         * @param {SelectionModel} this
67724         */
67725        "afterselectionchange" : true,
67726        /**
67727         * @event beforerowselect
67728         * Fires when a row is selected being selected, return false to cancel.
67729         * @param {SelectionModel} this
67730         * @param {Number} rowIndex The selected index
67731         * @param {Boolean} keepExisting False if other selections will be cleared
67732         */
67733        "beforerowselect" : true,
67734        /**
67735         * @event rowselect
67736         * Fires when a row is selected.
67737         * @param {SelectionModel} this
67738         * @param {Number} rowIndex The selected index
67739         * @param {Roo.data.Record} r The record
67740         */
67741        "rowselect" : true,
67742        /**
67743         * @event rowdeselect
67744         * Fires when a row is deselected.
67745         * @param {SelectionModel} this
67746         * @param {Number} rowIndex The selected index
67747         */
67748         "rowdeselect" : true
67749     });
67750     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
67751     this.locked = false;
67752 };
67753
67754 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
67755     /**
67756      * @cfg {Boolean} singleSelect
67757      * True to allow selection of only one row at a time (defaults to false)
67758      */
67759     singleSelect : false,
67760
67761     // private
67762     initEvents : function(){
67763
67764         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
67765             this.grid.on("mousedown", this.handleMouseDown, this);
67766         }else{ // allow click to work like normal
67767             this.grid.on("rowclick", this.handleDragableRowClick, this);
67768         }
67769         // bootstrap does not have a view..
67770         var view = this.grid.view ? this.grid.view : this.grid;
67771         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
67772             "up" : function(e){
67773                 if(!e.shiftKey){
67774                     this.selectPrevious(e.shiftKey);
67775                 }else if(this.last !== false && this.lastActive !== false){
67776                     var last = this.last;
67777                     this.selectRange(this.last,  this.lastActive-1);
67778                     view.focusRow(this.lastActive);
67779                     if(last !== false){
67780                         this.last = last;
67781                     }
67782                 }else{
67783                     this.selectFirstRow();
67784                 }
67785                 this.fireEvent("afterselectionchange", this);
67786             },
67787             "down" : function(e){
67788                 if(!e.shiftKey){
67789                     this.selectNext(e.shiftKey);
67790                 }else if(this.last !== false && this.lastActive !== false){
67791                     var last = this.last;
67792                     this.selectRange(this.last,  this.lastActive+1);
67793                     view.focusRow(this.lastActive);
67794                     if(last !== false){
67795                         this.last = last;
67796                     }
67797                 }else{
67798                     this.selectFirstRow();
67799                 }
67800                 this.fireEvent("afterselectionchange", this);
67801             },
67802             scope: this
67803         });
67804
67805          
67806         view.on("refresh", this.onRefresh, this);
67807         view.on("rowupdated", this.onRowUpdated, this);
67808         view.on("rowremoved", this.onRemove, this);
67809     },
67810
67811     // private
67812     onRefresh : function(){
67813         var ds = this.grid.ds, i, v = this.grid.view;
67814         var s = this.selections;
67815         s.each(function(r){
67816             if((i = ds.indexOfId(r.id)) != -1){
67817                 v.onRowSelect(i);
67818                 s.add(ds.getAt(i)); // updating the selection relate data
67819             }else{
67820                 s.remove(r);
67821             }
67822         });
67823     },
67824
67825     // private
67826     onRemove : function(v, index, r){
67827         this.selections.remove(r);
67828     },
67829
67830     // private
67831     onRowUpdated : function(v, index, r){
67832         if(this.isSelected(r)){
67833             v.onRowSelect(index);
67834         }
67835     },
67836
67837     /**
67838      * Select records.
67839      * @param {Array} records The records to select
67840      * @param {Boolean} keepExisting (optional) True to keep existing selections
67841      */
67842     selectRecords : function(records, keepExisting){
67843         if(!keepExisting){
67844             this.clearSelections();
67845         }
67846         var ds = this.grid.ds;
67847         for(var i = 0, len = records.length; i < len; i++){
67848             this.selectRow(ds.indexOf(records[i]), true);
67849         }
67850     },
67851
67852     /**
67853      * Gets the number of selected rows.
67854      * @return {Number}
67855      */
67856     getCount : function(){
67857         return this.selections.length;
67858     },
67859
67860     /**
67861      * Selects the first row in the grid.
67862      */
67863     selectFirstRow : function(){
67864         this.selectRow(0);
67865     },
67866
67867     /**
67868      * Select the last row.
67869      * @param {Boolean} keepExisting (optional) True to keep existing selections
67870      */
67871     selectLastRow : function(keepExisting){
67872         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
67873     },
67874
67875     /**
67876      * Selects the row immediately following the last selected row.
67877      * @param {Boolean} keepExisting (optional) True to keep existing selections
67878      */
67879     selectNext : function(keepExisting){
67880         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
67881             this.selectRow(this.last+1, keepExisting);
67882             var view = this.grid.view ? this.grid.view : this.grid;
67883             view.focusRow(this.last);
67884         }
67885     },
67886
67887     /**
67888      * Selects the row that precedes the last selected row.
67889      * @param {Boolean} keepExisting (optional) True to keep existing selections
67890      */
67891     selectPrevious : function(keepExisting){
67892         if(this.last){
67893             this.selectRow(this.last-1, keepExisting);
67894             var view = this.grid.view ? this.grid.view : this.grid;
67895             view.focusRow(this.last);
67896         }
67897     },
67898
67899     /**
67900      * Returns the selected records
67901      * @return {Array} Array of selected records
67902      */
67903     getSelections : function(){
67904         return [].concat(this.selections.items);
67905     },
67906
67907     /**
67908      * Returns the first selected record.
67909      * @return {Record}
67910      */
67911     getSelected : function(){
67912         return this.selections.itemAt(0);
67913     },
67914
67915
67916     /**
67917      * Clears all selections.
67918      */
67919     clearSelections : function(fast){
67920         if(this.locked) {
67921             return;
67922         }
67923         if(fast !== true){
67924             var ds = this.grid.ds;
67925             var s = this.selections;
67926             s.each(function(r){
67927                 this.deselectRow(ds.indexOfId(r.id));
67928             }, this);
67929             s.clear();
67930         }else{
67931             this.selections.clear();
67932         }
67933         this.last = false;
67934     },
67935
67936
67937     /**
67938      * Selects all rows.
67939      */
67940     selectAll : function(){
67941         if(this.locked) {
67942             return;
67943         }
67944         this.selections.clear();
67945         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
67946             this.selectRow(i, true);
67947         }
67948     },
67949
67950     /**
67951      * Returns True if there is a selection.
67952      * @return {Boolean}
67953      */
67954     hasSelection : function(){
67955         return this.selections.length > 0;
67956     },
67957
67958     /**
67959      * Returns True if the specified row is selected.
67960      * @param {Number/Record} record The record or index of the record to check
67961      * @return {Boolean}
67962      */
67963     isSelected : function(index){
67964         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
67965         return (r && this.selections.key(r.id) ? true : false);
67966     },
67967
67968     /**
67969      * Returns True if the specified record id is selected.
67970      * @param {String} id The id of record to check
67971      * @return {Boolean}
67972      */
67973     isIdSelected : function(id){
67974         return (this.selections.key(id) ? true : false);
67975     },
67976
67977     // private
67978     handleMouseDown : function(e, t)
67979     {
67980         var view = this.grid.view ? this.grid.view : this.grid;
67981         var rowIndex;
67982         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
67983             return;
67984         };
67985         if(e.shiftKey && this.last !== false){
67986             var last = this.last;
67987             this.selectRange(last, rowIndex, e.ctrlKey);
67988             this.last = last; // reset the last
67989             view.focusRow(rowIndex);
67990         }else{
67991             var isSelected = this.isSelected(rowIndex);
67992             if(e.button !== 0 && isSelected){
67993                 view.focusRow(rowIndex);
67994             }else if(e.ctrlKey && isSelected){
67995                 this.deselectRow(rowIndex);
67996             }else if(!isSelected){
67997                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
67998                 view.focusRow(rowIndex);
67999             }
68000         }
68001         this.fireEvent("afterselectionchange", this);
68002     },
68003     // private
68004     handleDragableRowClick :  function(grid, rowIndex, e) 
68005     {
68006         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
68007             this.selectRow(rowIndex, false);
68008             var view = this.grid.view ? this.grid.view : this.grid;
68009             view.focusRow(rowIndex);
68010              this.fireEvent("afterselectionchange", this);
68011         }
68012     },
68013     
68014     /**
68015      * Selects multiple rows.
68016      * @param {Array} rows Array of the indexes of the row to select
68017      * @param {Boolean} keepExisting (optional) True to keep existing selections
68018      */
68019     selectRows : function(rows, keepExisting){
68020         if(!keepExisting){
68021             this.clearSelections();
68022         }
68023         for(var i = 0, len = rows.length; i < len; i++){
68024             this.selectRow(rows[i], true);
68025         }
68026     },
68027
68028     /**
68029      * Selects a range of rows. All rows in between startRow and endRow are also selected.
68030      * @param {Number} startRow The index of the first row in the range
68031      * @param {Number} endRow The index of the last row in the range
68032      * @param {Boolean} keepExisting (optional) True to retain existing selections
68033      */
68034     selectRange : function(startRow, endRow, keepExisting){
68035         if(this.locked) {
68036             return;
68037         }
68038         if(!keepExisting){
68039             this.clearSelections();
68040         }
68041         if(startRow <= endRow){
68042             for(var i = startRow; i <= endRow; i++){
68043                 this.selectRow(i, true);
68044             }
68045         }else{
68046             for(var i = startRow; i >= endRow; i--){
68047                 this.selectRow(i, true);
68048             }
68049         }
68050     },
68051
68052     /**
68053      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
68054      * @param {Number} startRow The index of the first row in the range
68055      * @param {Number} endRow The index of the last row in the range
68056      */
68057     deselectRange : function(startRow, endRow, preventViewNotify){
68058         if(this.locked) {
68059             return;
68060         }
68061         for(var i = startRow; i <= endRow; i++){
68062             this.deselectRow(i, preventViewNotify);
68063         }
68064     },
68065
68066     /**
68067      * Selects a row.
68068      * @param {Number} row The index of the row to select
68069      * @param {Boolean} keepExisting (optional) True to keep existing selections
68070      */
68071     selectRow : function(index, keepExisting, preventViewNotify){
68072         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
68073             return;
68074         }
68075         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
68076             if(!keepExisting || this.singleSelect){
68077                 this.clearSelections();
68078             }
68079             var r = this.grid.ds.getAt(index);
68080             this.selections.add(r);
68081             this.last = this.lastActive = index;
68082             if(!preventViewNotify){
68083                 var view = this.grid.view ? this.grid.view : this.grid;
68084                 view.onRowSelect(index);
68085             }
68086             this.fireEvent("rowselect", this, index, r);
68087             this.fireEvent("selectionchange", this);
68088         }
68089     },
68090
68091     /**
68092      * Deselects a row.
68093      * @param {Number} row The index of the row to deselect
68094      */
68095     deselectRow : function(index, preventViewNotify){
68096         if(this.locked) {
68097             return;
68098         }
68099         if(this.last == index){
68100             this.last = false;
68101         }
68102         if(this.lastActive == index){
68103             this.lastActive = false;
68104         }
68105         var r = this.grid.ds.getAt(index);
68106         this.selections.remove(r);
68107         if(!preventViewNotify){
68108             var view = this.grid.view ? this.grid.view : this.grid;
68109             view.onRowDeselect(index);
68110         }
68111         this.fireEvent("rowdeselect", this, index);
68112         this.fireEvent("selectionchange", this);
68113     },
68114
68115     // private
68116     restoreLast : function(){
68117         if(this._last){
68118             this.last = this._last;
68119         }
68120     },
68121
68122     // private
68123     acceptsNav : function(row, col, cm){
68124         return !cm.isHidden(col) && cm.isCellEditable(col, row);
68125     },
68126
68127     // private
68128     onEditorKey : function(field, e){
68129         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
68130         if(k == e.TAB){
68131             e.stopEvent();
68132             ed.completeEdit();
68133             if(e.shiftKey){
68134                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
68135             }else{
68136                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
68137             }
68138         }else if(k == e.ENTER && !e.ctrlKey){
68139             e.stopEvent();
68140             ed.completeEdit();
68141             if(e.shiftKey){
68142                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
68143             }else{
68144                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
68145             }
68146         }else if(k == e.ESC){
68147             ed.cancelEdit();
68148         }
68149         if(newCell){
68150             g.startEditing(newCell[0], newCell[1]);
68151         }
68152     }
68153 });/*
68154  * Based on:
68155  * Ext JS Library 1.1.1
68156  * Copyright(c) 2006-2007, Ext JS, LLC.
68157  *
68158  * Originally Released Under LGPL - original licence link has changed is not relivant.
68159  *
68160  * Fork - LGPL
68161  * <script type="text/javascript">
68162  */
68163 /**
68164  * @class Roo.grid.CellSelectionModel
68165  * @extends Roo.grid.AbstractSelectionModel
68166  * This class provides the basic implementation for cell selection in a grid.
68167  * @constructor
68168  * @param {Object} config The object containing the configuration of this model.
68169  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
68170  */
68171 Roo.grid.CellSelectionModel = function(config){
68172     Roo.apply(this, config);
68173
68174     this.selection = null;
68175
68176     this.addEvents({
68177         /**
68178              * @event beforerowselect
68179              * Fires before a cell is selected.
68180              * @param {SelectionModel} this
68181              * @param {Number} rowIndex The selected row index
68182              * @param {Number} colIndex The selected cell index
68183              */
68184             "beforecellselect" : true,
68185         /**
68186              * @event cellselect
68187              * Fires when a cell is selected.
68188              * @param {SelectionModel} this
68189              * @param {Number} rowIndex The selected row index
68190              * @param {Number} colIndex The selected cell index
68191              */
68192             "cellselect" : true,
68193         /**
68194              * @event selectionchange
68195              * Fires when the active selection changes.
68196              * @param {SelectionModel} this
68197              * @param {Object} selection null for no selection or an object (o) with two properties
68198                 <ul>
68199                 <li>o.record: the record object for the row the selection is in</li>
68200                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
68201                 </ul>
68202              */
68203             "selectionchange" : true,
68204         /**
68205              * @event tabend
68206              * Fires when the tab (or enter) was pressed on the last editable cell
68207              * You can use this to trigger add new row.
68208              * @param {SelectionModel} this
68209              */
68210             "tabend" : true,
68211          /**
68212              * @event beforeeditnext
68213              * Fires before the next editable sell is made active
68214              * You can use this to skip to another cell or fire the tabend
68215              *    if you set cell to false
68216              * @param {Object} eventdata object : { cell : [ row, col ] } 
68217              */
68218             "beforeeditnext" : true
68219     });
68220     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
68221 };
68222
68223 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
68224     
68225     enter_is_tab: false,
68226
68227     /** @ignore */
68228     initEvents : function(){
68229         this.grid.on("mousedown", this.handleMouseDown, this);
68230         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
68231         var view = this.grid.view;
68232         view.on("refresh", this.onViewChange, this);
68233         view.on("rowupdated", this.onRowUpdated, this);
68234         view.on("beforerowremoved", this.clearSelections, this);
68235         view.on("beforerowsinserted", this.clearSelections, this);
68236         if(this.grid.isEditor){
68237             this.grid.on("beforeedit", this.beforeEdit,  this);
68238         }
68239     },
68240
68241         //private
68242     beforeEdit : function(e){
68243         this.select(e.row, e.column, false, true, e.record);
68244     },
68245
68246         //private
68247     onRowUpdated : function(v, index, r){
68248         if(this.selection && this.selection.record == r){
68249             v.onCellSelect(index, this.selection.cell[1]);
68250         }
68251     },
68252
68253         //private
68254     onViewChange : function(){
68255         this.clearSelections(true);
68256     },
68257
68258         /**
68259          * Returns the currently selected cell,.
68260          * @return {Array} The selected cell (row, column) or null if none selected.
68261          */
68262     getSelectedCell : function(){
68263         return this.selection ? this.selection.cell : null;
68264     },
68265
68266     /**
68267      * Clears all selections.
68268      * @param {Boolean} true to prevent the gridview from being notified about the change.
68269      */
68270     clearSelections : function(preventNotify){
68271         var s = this.selection;
68272         if(s){
68273             if(preventNotify !== true){
68274                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
68275             }
68276             this.selection = null;
68277             this.fireEvent("selectionchange", this, null);
68278         }
68279     },
68280
68281     /**
68282      * Returns true if there is a selection.
68283      * @return {Boolean}
68284      */
68285     hasSelection : function(){
68286         return this.selection ? true : false;
68287     },
68288
68289     /** @ignore */
68290     handleMouseDown : function(e, t){
68291         var v = this.grid.getView();
68292         if(this.isLocked()){
68293             return;
68294         };
68295         var row = v.findRowIndex(t);
68296         var cell = v.findCellIndex(t);
68297         if(row !== false && cell !== false){
68298             this.select(row, cell);
68299         }
68300     },
68301
68302     /**
68303      * Selects a cell.
68304      * @param {Number} rowIndex
68305      * @param {Number} collIndex
68306      */
68307     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
68308         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
68309             this.clearSelections();
68310             r = r || this.grid.dataSource.getAt(rowIndex);
68311             this.selection = {
68312                 record : r,
68313                 cell : [rowIndex, colIndex]
68314             };
68315             if(!preventViewNotify){
68316                 var v = this.grid.getView();
68317                 v.onCellSelect(rowIndex, colIndex);
68318                 if(preventFocus !== true){
68319                     v.focusCell(rowIndex, colIndex);
68320                 }
68321             }
68322             this.fireEvent("cellselect", this, rowIndex, colIndex);
68323             this.fireEvent("selectionchange", this, this.selection);
68324         }
68325     },
68326
68327         //private
68328     isSelectable : function(rowIndex, colIndex, cm){
68329         return !cm.isHidden(colIndex);
68330     },
68331
68332     /** @ignore */
68333     handleKeyDown : function(e){
68334         //Roo.log('Cell Sel Model handleKeyDown');
68335         if(!e.isNavKeyPress()){
68336             return;
68337         }
68338         var g = this.grid, s = this.selection;
68339         if(!s){
68340             e.stopEvent();
68341             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
68342             if(cell){
68343                 this.select(cell[0], cell[1]);
68344             }
68345             return;
68346         }
68347         var sm = this;
68348         var walk = function(row, col, step){
68349             return g.walkCells(row, col, step, sm.isSelectable,  sm);
68350         };
68351         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
68352         var newCell;
68353
68354       
68355
68356         switch(k){
68357             case e.TAB:
68358                 // handled by onEditorKey
68359                 if (g.isEditor && g.editing) {
68360                     return;
68361                 }
68362                 if(e.shiftKey) {
68363                     newCell = walk(r, c-1, -1);
68364                 } else {
68365                     newCell = walk(r, c+1, 1);
68366                 }
68367                 break;
68368             
68369             case e.DOWN:
68370                newCell = walk(r+1, c, 1);
68371                 break;
68372             
68373             case e.UP:
68374                 newCell = walk(r-1, c, -1);
68375                 break;
68376             
68377             case e.RIGHT:
68378                 newCell = walk(r, c+1, 1);
68379                 break;
68380             
68381             case e.LEFT:
68382                 newCell = walk(r, c-1, -1);
68383                 break;
68384             
68385             case e.ENTER:
68386                 
68387                 if(g.isEditor && !g.editing){
68388                    g.startEditing(r, c);
68389                    e.stopEvent();
68390                    return;
68391                 }
68392                 
68393                 
68394              break;
68395         };
68396         if(newCell){
68397             this.select(newCell[0], newCell[1]);
68398             e.stopEvent();
68399             
68400         }
68401     },
68402
68403     acceptsNav : function(row, col, cm){
68404         return !cm.isHidden(col) && cm.isCellEditable(col, row);
68405     },
68406     /**
68407      * Selects a cell.
68408      * @param {Number} field (not used) - as it's normally used as a listener
68409      * @param {Number} e - event - fake it by using
68410      *
68411      * var e = Roo.EventObjectImpl.prototype;
68412      * e.keyCode = e.TAB
68413      *
68414      * 
68415      */
68416     onEditorKey : function(field, e){
68417         
68418         var k = e.getKey(),
68419             newCell,
68420             g = this.grid,
68421             ed = g.activeEditor,
68422             forward = false;
68423         ///Roo.log('onEditorKey' + k);
68424         
68425         
68426         if (this.enter_is_tab && k == e.ENTER) {
68427             k = e.TAB;
68428         }
68429         
68430         if(k == e.TAB){
68431             if(e.shiftKey){
68432                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
68433             }else{
68434                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
68435                 forward = true;
68436             }
68437             
68438             e.stopEvent();
68439             
68440         } else if(k == e.ENTER &&  !e.ctrlKey){
68441             ed.completeEdit();
68442             e.stopEvent();
68443             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
68444         
68445                 } else if(k == e.ESC){
68446             ed.cancelEdit();
68447         }
68448                 
68449         if (newCell) {
68450             var ecall = { cell : newCell, forward : forward };
68451             this.fireEvent('beforeeditnext', ecall );
68452             newCell = ecall.cell;
68453                         forward = ecall.forward;
68454         }
68455                 
68456         if(newCell){
68457             //Roo.log('next cell after edit');
68458             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
68459         } else if (forward) {
68460             // tabbed past last
68461             this.fireEvent.defer(100, this, ['tabend',this]);
68462         }
68463     }
68464 });/*
68465  * Based on:
68466  * Ext JS Library 1.1.1
68467  * Copyright(c) 2006-2007, Ext JS, LLC.
68468  *
68469  * Originally Released Under LGPL - original licence link has changed is not relivant.
68470  *
68471  * Fork - LGPL
68472  * <script type="text/javascript">
68473  */
68474  
68475 /**
68476  * @class Roo.grid.EditorGrid
68477  * @extends Roo.grid.Grid
68478  * Class for creating and editable grid.
68479  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
68480  * The container MUST have some type of size defined for the grid to fill. The container will be 
68481  * automatically set to position relative if it isn't already.
68482  * @param {Object} dataSource The data model to bind to
68483  * @param {Object} colModel The column model with info about this grid's columns
68484  */
68485 Roo.grid.EditorGrid = function(container, config){
68486     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
68487     this.getGridEl().addClass("xedit-grid");
68488
68489     if(!this.selModel){
68490         this.selModel = new Roo.grid.CellSelectionModel();
68491     }
68492
68493     this.activeEditor = null;
68494
68495         this.addEvents({
68496             /**
68497              * @event beforeedit
68498              * Fires before cell editing is triggered. The edit event object has the following properties <br />
68499              * <ul style="padding:5px;padding-left:16px;">
68500              * <li>grid - This grid</li>
68501              * <li>record - The record being edited</li>
68502              * <li>field - The field name being edited</li>
68503              * <li>value - The value for the field being edited.</li>
68504              * <li>row - The grid row index</li>
68505              * <li>column - The grid column index</li>
68506              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
68507              * </ul>
68508              * @param {Object} e An edit event (see above for description)
68509              */
68510             "beforeedit" : true,
68511             /**
68512              * @event afteredit
68513              * Fires after a cell is edited. <br />
68514              * <ul style="padding:5px;padding-left:16px;">
68515              * <li>grid - This grid</li>
68516              * <li>record - The record being edited</li>
68517              * <li>field - The field name being edited</li>
68518              * <li>value - The value being set</li>
68519              * <li>originalValue - The original value for the field, before the edit.</li>
68520              * <li>row - The grid row index</li>
68521              * <li>column - The grid column index</li>
68522              * </ul>
68523              * @param {Object} e An edit event (see above for description)
68524              */
68525             "afteredit" : true,
68526             /**
68527              * @event validateedit
68528              * Fires after a cell is edited, but before the value is set in the record. 
68529          * You can use this to modify the value being set in the field, Return false
68530              * to cancel the change. The edit event object has the following properties <br />
68531              * <ul style="padding:5px;padding-left:16px;">
68532          * <li>editor - This editor</li>
68533              * <li>grid - This grid</li>
68534              * <li>record - The record being edited</li>
68535              * <li>field - The field name being edited</li>
68536              * <li>value - The value being set</li>
68537              * <li>originalValue - The original value for the field, before the edit.</li>
68538              * <li>row - The grid row index</li>
68539              * <li>column - The grid column index</li>
68540              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
68541              * </ul>
68542              * @param {Object} e An edit event (see above for description)
68543              */
68544             "validateedit" : true
68545         });
68546     this.on("bodyscroll", this.stopEditing,  this);
68547     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
68548 };
68549
68550 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
68551     /**
68552      * @cfg {Number} clicksToEdit
68553      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
68554      */
68555     clicksToEdit: 2,
68556
68557     // private
68558     isEditor : true,
68559     // private
68560     trackMouseOver: false, // causes very odd FF errors
68561
68562     onCellDblClick : function(g, row, col){
68563         this.startEditing(row, col);
68564     },
68565
68566     onEditComplete : function(ed, value, startValue){
68567         this.editing = false;
68568         this.activeEditor = null;
68569         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
68570         var r = ed.record;
68571         var field = this.colModel.getDataIndex(ed.col);
68572         var e = {
68573             grid: this,
68574             record: r,
68575             field: field,
68576             originalValue: startValue,
68577             value: value,
68578             row: ed.row,
68579             column: ed.col,
68580             cancel:false,
68581             editor: ed
68582         };
68583         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
68584         cell.show();
68585           
68586         if(String(value) !== String(startValue)){
68587             
68588             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
68589                 r.set(field, e.value);
68590                 // if we are dealing with a combo box..
68591                 // then we also set the 'name' colum to be the displayField
68592                 if (ed.field.displayField && ed.field.name) {
68593                     r.set(ed.field.name, ed.field.el.dom.value);
68594                 }
68595                 
68596                 delete e.cancel; //?? why!!!
68597                 this.fireEvent("afteredit", e);
68598             }
68599         } else {
68600             this.fireEvent("afteredit", e); // always fire it!
68601         }
68602         this.view.focusCell(ed.row, ed.col);
68603     },
68604
68605     /**
68606      * Starts editing the specified for the specified row/column
68607      * @param {Number} rowIndex
68608      * @param {Number} colIndex
68609      */
68610     startEditing : function(row, col){
68611         this.stopEditing();
68612         if(this.colModel.isCellEditable(col, row)){
68613             this.view.ensureVisible(row, col, true);
68614           
68615             var r = this.dataSource.getAt(row);
68616             var field = this.colModel.getDataIndex(col);
68617             var cell = Roo.get(this.view.getCell(row,col));
68618             var e = {
68619                 grid: this,
68620                 record: r,
68621                 field: field,
68622                 value: r.data[field],
68623                 row: row,
68624                 column: col,
68625                 cancel:false 
68626             };
68627             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
68628                 this.editing = true;
68629                 var ed = this.colModel.getCellEditor(col, row);
68630                 
68631                 if (!ed) {
68632                     return;
68633                 }
68634                 if(!ed.rendered){
68635                     ed.render(ed.parentEl || document.body);
68636                 }
68637                 ed.field.reset();
68638                
68639                 cell.hide();
68640                 
68641                 (function(){ // complex but required for focus issues in safari, ie and opera
68642                     ed.row = row;
68643                     ed.col = col;
68644                     ed.record = r;
68645                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
68646                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
68647                     this.activeEditor = ed;
68648                     var v = r.data[field];
68649                     ed.startEdit(this.view.getCell(row, col), v);
68650                     // combo's with 'displayField and name set
68651                     if (ed.field.displayField && ed.field.name) {
68652                         ed.field.el.dom.value = r.data[ed.field.name];
68653                     }
68654                     
68655                     
68656                 }).defer(50, this);
68657             }
68658         }
68659     },
68660         
68661     /**
68662      * Stops any active editing
68663      */
68664     stopEditing : function(){
68665         if(this.activeEditor){
68666             this.activeEditor.completeEdit();
68667         }
68668         this.activeEditor = null;
68669     },
68670         
68671          /**
68672      * Called to get grid's drag proxy text, by default returns this.ddText.
68673      * @return {String}
68674      */
68675     getDragDropText : function(){
68676         var count = this.selModel.getSelectedCell() ? 1 : 0;
68677         return String.format(this.ddText, count, count == 1 ? '' : 's');
68678     }
68679         
68680 });/*
68681  * Based on:
68682  * Ext JS Library 1.1.1
68683  * Copyright(c) 2006-2007, Ext JS, LLC.
68684  *
68685  * Originally Released Under LGPL - original licence link has changed is not relivant.
68686  *
68687  * Fork - LGPL
68688  * <script type="text/javascript">
68689  */
68690
68691 // private - not really -- you end up using it !
68692 // This is a support class used internally by the Grid components
68693
68694 /**
68695  * @class Roo.grid.GridEditor
68696  * @extends Roo.Editor
68697  * Class for creating and editable grid elements.
68698  * @param {Object} config any settings (must include field)
68699  */
68700 Roo.grid.GridEditor = function(field, config){
68701     if (!config && field.field) {
68702         config = field;
68703         field = Roo.factory(config.field, Roo.form);
68704     }
68705     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
68706     field.monitorTab = false;
68707 };
68708
68709 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
68710     
68711     /**
68712      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
68713      */
68714     
68715     alignment: "tl-tl",
68716     autoSize: "width",
68717     hideEl : false,
68718     cls: "x-small-editor x-grid-editor",
68719     shim:false,
68720     shadow:"frame"
68721 });/*
68722  * Based on:
68723  * Ext JS Library 1.1.1
68724  * Copyright(c) 2006-2007, Ext JS, LLC.
68725  *
68726  * Originally Released Under LGPL - original licence link has changed is not relivant.
68727  *
68728  * Fork - LGPL
68729  * <script type="text/javascript">
68730  */
68731   
68732
68733   
68734 Roo.grid.PropertyRecord = Roo.data.Record.create([
68735     {name:'name',type:'string'},  'value'
68736 ]);
68737
68738
68739 Roo.grid.PropertyStore = function(grid, source){
68740     this.grid = grid;
68741     this.store = new Roo.data.Store({
68742         recordType : Roo.grid.PropertyRecord
68743     });
68744     this.store.on('update', this.onUpdate,  this);
68745     if(source){
68746         this.setSource(source);
68747     }
68748     Roo.grid.PropertyStore.superclass.constructor.call(this);
68749 };
68750
68751
68752
68753 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
68754     setSource : function(o){
68755         this.source = o;
68756         this.store.removeAll();
68757         var data = [];
68758         for(var k in o){
68759             if(this.isEditableValue(o[k])){
68760                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
68761             }
68762         }
68763         this.store.loadRecords({records: data}, {}, true);
68764     },
68765
68766     onUpdate : function(ds, record, type){
68767         if(type == Roo.data.Record.EDIT){
68768             var v = record.data['value'];
68769             var oldValue = record.modified['value'];
68770             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
68771                 this.source[record.id] = v;
68772                 record.commit();
68773                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
68774             }else{
68775                 record.reject();
68776             }
68777         }
68778     },
68779
68780     getProperty : function(row){
68781        return this.store.getAt(row);
68782     },
68783
68784     isEditableValue: function(val){
68785         if(val && val instanceof Date){
68786             return true;
68787         }else if(typeof val == 'object' || typeof val == 'function'){
68788             return false;
68789         }
68790         return true;
68791     },
68792
68793     setValue : function(prop, value){
68794         this.source[prop] = value;
68795         this.store.getById(prop).set('value', value);
68796     },
68797
68798     getSource : function(){
68799         return this.source;
68800     }
68801 });
68802
68803 Roo.grid.PropertyColumnModel = function(grid, store){
68804     this.grid = grid;
68805     var g = Roo.grid;
68806     g.PropertyColumnModel.superclass.constructor.call(this, [
68807         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
68808         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
68809     ]);
68810     this.store = store;
68811     this.bselect = Roo.DomHelper.append(document.body, {
68812         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
68813             {tag: 'option', value: 'true', html: 'true'},
68814             {tag: 'option', value: 'false', html: 'false'}
68815         ]
68816     });
68817     Roo.id(this.bselect);
68818     var f = Roo.form;
68819     this.editors = {
68820         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
68821         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
68822         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
68823         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
68824         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
68825     };
68826     this.renderCellDelegate = this.renderCell.createDelegate(this);
68827     this.renderPropDelegate = this.renderProp.createDelegate(this);
68828 };
68829
68830 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
68831     
68832     
68833     nameText : 'Name',
68834     valueText : 'Value',
68835     
68836     dateFormat : 'm/j/Y',
68837     
68838     
68839     renderDate : function(dateVal){
68840         return dateVal.dateFormat(this.dateFormat);
68841     },
68842
68843     renderBool : function(bVal){
68844         return bVal ? 'true' : 'false';
68845     },
68846
68847     isCellEditable : function(colIndex, rowIndex){
68848         return colIndex == 1;
68849     },
68850
68851     getRenderer : function(col){
68852         return col == 1 ?
68853             this.renderCellDelegate : this.renderPropDelegate;
68854     },
68855
68856     renderProp : function(v){
68857         return this.getPropertyName(v);
68858     },
68859
68860     renderCell : function(val){
68861         var rv = val;
68862         if(val instanceof Date){
68863             rv = this.renderDate(val);
68864         }else if(typeof val == 'boolean'){
68865             rv = this.renderBool(val);
68866         }
68867         return Roo.util.Format.htmlEncode(rv);
68868     },
68869
68870     getPropertyName : function(name){
68871         var pn = this.grid.propertyNames;
68872         return pn && pn[name] ? pn[name] : name;
68873     },
68874
68875     getCellEditor : function(colIndex, rowIndex){
68876         var p = this.store.getProperty(rowIndex);
68877         var n = p.data['name'], val = p.data['value'];
68878         
68879         if(typeof(this.grid.customEditors[n]) == 'string'){
68880             return this.editors[this.grid.customEditors[n]];
68881         }
68882         if(typeof(this.grid.customEditors[n]) != 'undefined'){
68883             return this.grid.customEditors[n];
68884         }
68885         if(val instanceof Date){
68886             return this.editors['date'];
68887         }else if(typeof val == 'number'){
68888             return this.editors['number'];
68889         }else if(typeof val == 'boolean'){
68890             return this.editors['boolean'];
68891         }else{
68892             return this.editors['string'];
68893         }
68894     }
68895 });
68896
68897 /**
68898  * @class Roo.grid.PropertyGrid
68899  * @extends Roo.grid.EditorGrid
68900  * This class represents the  interface of a component based property grid control.
68901  * <br><br>Usage:<pre><code>
68902  var grid = new Roo.grid.PropertyGrid("my-container-id", {
68903       
68904  });
68905  // set any options
68906  grid.render();
68907  * </code></pre>
68908   
68909  * @constructor
68910  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
68911  * The container MUST have some type of size defined for the grid to fill. The container will be
68912  * automatically set to position relative if it isn't already.
68913  * @param {Object} config A config object that sets properties on this grid.
68914  */
68915 Roo.grid.PropertyGrid = function(container, config){
68916     config = config || {};
68917     var store = new Roo.grid.PropertyStore(this);
68918     this.store = store;
68919     var cm = new Roo.grid.PropertyColumnModel(this, store);
68920     store.store.sort('name', 'ASC');
68921     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
68922         ds: store.store,
68923         cm: cm,
68924         enableColLock:false,
68925         enableColumnMove:false,
68926         stripeRows:false,
68927         trackMouseOver: false,
68928         clicksToEdit:1
68929     }, config));
68930     this.getGridEl().addClass('x-props-grid');
68931     this.lastEditRow = null;
68932     this.on('columnresize', this.onColumnResize, this);
68933     this.addEvents({
68934          /**
68935              * @event beforepropertychange
68936              * Fires before a property changes (return false to stop?)
68937              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
68938              * @param {String} id Record Id
68939              * @param {String} newval New Value
68940          * @param {String} oldval Old Value
68941              */
68942         "beforepropertychange": true,
68943         /**
68944              * @event propertychange
68945              * Fires after a property changes
68946              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
68947              * @param {String} id Record Id
68948              * @param {String} newval New Value
68949          * @param {String} oldval Old Value
68950              */
68951         "propertychange": true
68952     });
68953     this.customEditors = this.customEditors || {};
68954 };
68955 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
68956     
68957      /**
68958      * @cfg {Object} customEditors map of colnames=> custom editors.
68959      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
68960      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
68961      * false disables editing of the field.
68962          */
68963     
68964       /**
68965      * @cfg {Object} propertyNames map of property Names to their displayed value
68966          */
68967     
68968     render : function(){
68969         Roo.grid.PropertyGrid.superclass.render.call(this);
68970         this.autoSize.defer(100, this);
68971     },
68972
68973     autoSize : function(){
68974         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
68975         if(this.view){
68976             this.view.fitColumns();
68977         }
68978     },
68979
68980     onColumnResize : function(){
68981         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
68982         this.autoSize();
68983     },
68984     /**
68985      * Sets the data for the Grid
68986      * accepts a Key => Value object of all the elements avaiable.
68987      * @param {Object} data  to appear in grid.
68988      */
68989     setSource : function(source){
68990         this.store.setSource(source);
68991         //this.autoSize();
68992     },
68993     /**
68994      * Gets all the data from the grid.
68995      * @return {Object} data  data stored in grid
68996      */
68997     getSource : function(){
68998         return this.store.getSource();
68999     }
69000 });/*
69001   
69002  * Licence LGPL
69003  
69004  */
69005  
69006 /**
69007  * @class Roo.grid.Calendar
69008  * @extends Roo.grid.Grid
69009  * This class extends the Grid to provide a calendar widget
69010  * <br><br>Usage:<pre><code>
69011  var grid = new Roo.grid.Calendar("my-container-id", {
69012      ds: myDataStore,
69013      cm: myColModel,
69014      selModel: mySelectionModel,
69015      autoSizeColumns: true,
69016      monitorWindowResize: false,
69017      trackMouseOver: true
69018      eventstore : real data store..
69019  });
69020  // set any options
69021  grid.render();
69022   
69023   * @constructor
69024  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
69025  * The container MUST have some type of size defined for the grid to fill. The container will be
69026  * automatically set to position relative if it isn't already.
69027  * @param {Object} config A config object that sets properties on this grid.
69028  */
69029 Roo.grid.Calendar = function(container, config){
69030         // initialize the container
69031         this.container = Roo.get(container);
69032         this.container.update("");
69033         this.container.setStyle("overflow", "hidden");
69034     this.container.addClass('x-grid-container');
69035
69036     this.id = this.container.id;
69037
69038     Roo.apply(this, config);
69039     // check and correct shorthanded configs
69040     
69041     var rows = [];
69042     var d =1;
69043     for (var r = 0;r < 6;r++) {
69044         
69045         rows[r]=[];
69046         for (var c =0;c < 7;c++) {
69047             rows[r][c]= '';
69048         }
69049     }
69050     if (this.eventStore) {
69051         this.eventStore= Roo.factory(this.eventStore, Roo.data);
69052         this.eventStore.on('load',this.onLoad, this);
69053         this.eventStore.on('beforeload',this.clearEvents, this);
69054          
69055     }
69056     
69057     this.dataSource = new Roo.data.Store({
69058             proxy: new Roo.data.MemoryProxy(rows),
69059             reader: new Roo.data.ArrayReader({}, [
69060                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
69061     });
69062
69063     this.dataSource.load();
69064     this.ds = this.dataSource;
69065     this.ds.xmodule = this.xmodule || false;
69066     
69067     
69068     var cellRender = function(v,x,r)
69069     {
69070         return String.format(
69071             '<div class="fc-day  fc-widget-content"><div>' +
69072                 '<div class="fc-event-container"></div>' +
69073                 '<div class="fc-day-number">{0}</div>'+
69074                 
69075                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
69076             '</div></div>', v);
69077     
69078     }
69079     
69080     
69081     this.colModel = new Roo.grid.ColumnModel( [
69082         {
69083             xtype: 'ColumnModel',
69084             xns: Roo.grid,
69085             dataIndex : 'weekday0',
69086             header : 'Sunday',
69087             renderer : cellRender
69088         },
69089         {
69090             xtype: 'ColumnModel',
69091             xns: Roo.grid,
69092             dataIndex : 'weekday1',
69093             header : 'Monday',
69094             renderer : cellRender
69095         },
69096         {
69097             xtype: 'ColumnModel',
69098             xns: Roo.grid,
69099             dataIndex : 'weekday2',
69100             header : 'Tuesday',
69101             renderer : cellRender
69102         },
69103         {
69104             xtype: 'ColumnModel',
69105             xns: Roo.grid,
69106             dataIndex : 'weekday3',
69107             header : 'Wednesday',
69108             renderer : cellRender
69109         },
69110         {
69111             xtype: 'ColumnModel',
69112             xns: Roo.grid,
69113             dataIndex : 'weekday4',
69114             header : 'Thursday',
69115             renderer : cellRender
69116         },
69117         {
69118             xtype: 'ColumnModel',
69119             xns: Roo.grid,
69120             dataIndex : 'weekday5',
69121             header : 'Friday',
69122             renderer : cellRender
69123         },
69124         {
69125             xtype: 'ColumnModel',
69126             xns: Roo.grid,
69127             dataIndex : 'weekday6',
69128             header : 'Saturday',
69129             renderer : cellRender
69130         }
69131     ]);
69132     this.cm = this.colModel;
69133     this.cm.xmodule = this.xmodule || false;
69134  
69135         
69136           
69137     //this.selModel = new Roo.grid.CellSelectionModel();
69138     //this.sm = this.selModel;
69139     //this.selModel.init(this);
69140     
69141     
69142     if(this.width){
69143         this.container.setWidth(this.width);
69144     }
69145
69146     if(this.height){
69147         this.container.setHeight(this.height);
69148     }
69149     /** @private */
69150         this.addEvents({
69151         // raw events
69152         /**
69153          * @event click
69154          * The raw click event for the entire grid.
69155          * @param {Roo.EventObject} e
69156          */
69157         "click" : true,
69158         /**
69159          * @event dblclick
69160          * The raw dblclick event for the entire grid.
69161          * @param {Roo.EventObject} e
69162          */
69163         "dblclick" : true,
69164         /**
69165          * @event contextmenu
69166          * The raw contextmenu event for the entire grid.
69167          * @param {Roo.EventObject} e
69168          */
69169         "contextmenu" : true,
69170         /**
69171          * @event mousedown
69172          * The raw mousedown event for the entire grid.
69173          * @param {Roo.EventObject} e
69174          */
69175         "mousedown" : true,
69176         /**
69177          * @event mouseup
69178          * The raw mouseup event for the entire grid.
69179          * @param {Roo.EventObject} e
69180          */
69181         "mouseup" : true,
69182         /**
69183          * @event mouseover
69184          * The raw mouseover event for the entire grid.
69185          * @param {Roo.EventObject} e
69186          */
69187         "mouseover" : true,
69188         /**
69189          * @event mouseout
69190          * The raw mouseout event for the entire grid.
69191          * @param {Roo.EventObject} e
69192          */
69193         "mouseout" : true,
69194         /**
69195          * @event keypress
69196          * The raw keypress event for the entire grid.
69197          * @param {Roo.EventObject} e
69198          */
69199         "keypress" : true,
69200         /**
69201          * @event keydown
69202          * The raw keydown event for the entire grid.
69203          * @param {Roo.EventObject} e
69204          */
69205         "keydown" : true,
69206
69207         // custom events
69208
69209         /**
69210          * @event cellclick
69211          * Fires when a cell is clicked
69212          * @param {Grid} this
69213          * @param {Number} rowIndex
69214          * @param {Number} columnIndex
69215          * @param {Roo.EventObject} e
69216          */
69217         "cellclick" : true,
69218         /**
69219          * @event celldblclick
69220          * Fires when a cell is double clicked
69221          * @param {Grid} this
69222          * @param {Number} rowIndex
69223          * @param {Number} columnIndex
69224          * @param {Roo.EventObject} e
69225          */
69226         "celldblclick" : true,
69227         /**
69228          * @event rowclick
69229          * Fires when a row is clicked
69230          * @param {Grid} this
69231          * @param {Number} rowIndex
69232          * @param {Roo.EventObject} e
69233          */
69234         "rowclick" : true,
69235         /**
69236          * @event rowdblclick
69237          * Fires when a row is double clicked
69238          * @param {Grid} this
69239          * @param {Number} rowIndex
69240          * @param {Roo.EventObject} e
69241          */
69242         "rowdblclick" : true,
69243         /**
69244          * @event headerclick
69245          * Fires when a header is clicked
69246          * @param {Grid} this
69247          * @param {Number} columnIndex
69248          * @param {Roo.EventObject} e
69249          */
69250         "headerclick" : true,
69251         /**
69252          * @event headerdblclick
69253          * Fires when a header cell is double clicked
69254          * @param {Grid} this
69255          * @param {Number} columnIndex
69256          * @param {Roo.EventObject} e
69257          */
69258         "headerdblclick" : true,
69259         /**
69260          * @event rowcontextmenu
69261          * Fires when a row is right clicked
69262          * @param {Grid} this
69263          * @param {Number} rowIndex
69264          * @param {Roo.EventObject} e
69265          */
69266         "rowcontextmenu" : true,
69267         /**
69268          * @event cellcontextmenu
69269          * Fires when a cell is right clicked
69270          * @param {Grid} this
69271          * @param {Number} rowIndex
69272          * @param {Number} cellIndex
69273          * @param {Roo.EventObject} e
69274          */
69275          "cellcontextmenu" : true,
69276         /**
69277          * @event headercontextmenu
69278          * Fires when a header is right clicked
69279          * @param {Grid} this
69280          * @param {Number} columnIndex
69281          * @param {Roo.EventObject} e
69282          */
69283         "headercontextmenu" : true,
69284         /**
69285          * @event bodyscroll
69286          * Fires when the body element is scrolled
69287          * @param {Number} scrollLeft
69288          * @param {Number} scrollTop
69289          */
69290         "bodyscroll" : true,
69291         /**
69292          * @event columnresize
69293          * Fires when the user resizes a column
69294          * @param {Number} columnIndex
69295          * @param {Number} newSize
69296          */
69297         "columnresize" : true,
69298         /**
69299          * @event columnmove
69300          * Fires when the user moves a column
69301          * @param {Number} oldIndex
69302          * @param {Number} newIndex
69303          */
69304         "columnmove" : true,
69305         /**
69306          * @event startdrag
69307          * Fires when row(s) start being dragged
69308          * @param {Grid} this
69309          * @param {Roo.GridDD} dd The drag drop object
69310          * @param {event} e The raw browser event
69311          */
69312         "startdrag" : true,
69313         /**
69314          * @event enddrag
69315          * Fires when a drag operation is complete
69316          * @param {Grid} this
69317          * @param {Roo.GridDD} dd The drag drop object
69318          * @param {event} e The raw browser event
69319          */
69320         "enddrag" : true,
69321         /**
69322          * @event dragdrop
69323          * Fires when dragged row(s) are dropped on a valid DD target
69324          * @param {Grid} this
69325          * @param {Roo.GridDD} dd The drag drop object
69326          * @param {String} targetId The target drag drop object
69327          * @param {event} e The raw browser event
69328          */
69329         "dragdrop" : true,
69330         /**
69331          * @event dragover
69332          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
69333          * @param {Grid} this
69334          * @param {Roo.GridDD} dd The drag drop object
69335          * @param {String} targetId The target drag drop object
69336          * @param {event} e The raw browser event
69337          */
69338         "dragover" : true,
69339         /**
69340          * @event dragenter
69341          *  Fires when the dragged row(s) first cross another DD target while being dragged
69342          * @param {Grid} this
69343          * @param {Roo.GridDD} dd The drag drop object
69344          * @param {String} targetId The target drag drop object
69345          * @param {event} e The raw browser event
69346          */
69347         "dragenter" : true,
69348         /**
69349          * @event dragout
69350          * Fires when the dragged row(s) leave another DD target while being dragged
69351          * @param {Grid} this
69352          * @param {Roo.GridDD} dd The drag drop object
69353          * @param {String} targetId The target drag drop object
69354          * @param {event} e The raw browser event
69355          */
69356         "dragout" : true,
69357         /**
69358          * @event rowclass
69359          * Fires when a row is rendered, so you can change add a style to it.
69360          * @param {GridView} gridview   The grid view
69361          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
69362          */
69363         'rowclass' : true,
69364
69365         /**
69366          * @event render
69367          * Fires when the grid is rendered
69368          * @param {Grid} grid
69369          */
69370         'render' : true,
69371             /**
69372              * @event select
69373              * Fires when a date is selected
69374              * @param {DatePicker} this
69375              * @param {Date} date The selected date
69376              */
69377         'select': true,
69378         /**
69379              * @event monthchange
69380              * Fires when the displayed month changes 
69381              * @param {DatePicker} this
69382              * @param {Date} date The selected month
69383              */
69384         'monthchange': true,
69385         /**
69386              * @event evententer
69387              * Fires when mouse over an event
69388              * @param {Calendar} this
69389              * @param {event} Event
69390              */
69391         'evententer': true,
69392         /**
69393              * @event eventleave
69394              * Fires when the mouse leaves an
69395              * @param {Calendar} this
69396              * @param {event}
69397              */
69398         'eventleave': true,
69399         /**
69400              * @event eventclick
69401              * Fires when the mouse click an
69402              * @param {Calendar} this
69403              * @param {event}
69404              */
69405         'eventclick': true,
69406         /**
69407              * @event eventrender
69408              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
69409              * @param {Calendar} this
69410              * @param {data} data to be modified
69411              */
69412         'eventrender': true
69413         
69414     });
69415
69416     Roo.grid.Grid.superclass.constructor.call(this);
69417     this.on('render', function() {
69418         this.view.el.addClass('x-grid-cal'); 
69419         
69420         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
69421
69422     },this);
69423     
69424     if (!Roo.grid.Calendar.style) {
69425         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
69426             
69427             
69428             '.x-grid-cal .x-grid-col' :  {
69429                 height: 'auto !important',
69430                 'vertical-align': 'top'
69431             },
69432             '.x-grid-cal  .fc-event-hori' : {
69433                 height: '14px'
69434             }
69435              
69436             
69437         }, Roo.id());
69438     }
69439
69440     
69441     
69442 };
69443 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
69444     /**
69445      * @cfg {Store} eventStore The store that loads events.
69446      */
69447     eventStore : 25,
69448
69449      
69450     activeDate : false,
69451     startDay : 0,
69452     autoWidth : true,
69453     monitorWindowResize : false,
69454
69455     
69456     resizeColumns : function() {
69457         var col = (this.view.el.getWidth() / 7) - 3;
69458         // loop through cols, and setWidth
69459         for(var i =0 ; i < 7 ; i++){
69460             this.cm.setColumnWidth(i, col);
69461         }
69462     },
69463      setDate :function(date) {
69464         
69465         Roo.log('setDate?');
69466         
69467         this.resizeColumns();
69468         var vd = this.activeDate;
69469         this.activeDate = date;
69470 //        if(vd && this.el){
69471 //            var t = date.getTime();
69472 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
69473 //                Roo.log('using add remove');
69474 //                
69475 //                this.fireEvent('monthchange', this, date);
69476 //                
69477 //                this.cells.removeClass("fc-state-highlight");
69478 //                this.cells.each(function(c){
69479 //                   if(c.dateValue == t){
69480 //                       c.addClass("fc-state-highlight");
69481 //                       setTimeout(function(){
69482 //                            try{c.dom.firstChild.focus();}catch(e){}
69483 //                       }, 50);
69484 //                       return false;
69485 //                   }
69486 //                   return true;
69487 //                });
69488 //                return;
69489 //            }
69490 //        }
69491         
69492         var days = date.getDaysInMonth();
69493         
69494         var firstOfMonth = date.getFirstDateOfMonth();
69495         var startingPos = firstOfMonth.getDay()-this.startDay;
69496         
69497         if(startingPos < this.startDay){
69498             startingPos += 7;
69499         }
69500         
69501         var pm = date.add(Date.MONTH, -1);
69502         var prevStart = pm.getDaysInMonth()-startingPos;
69503 //        
69504         
69505         
69506         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
69507         
69508         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
69509         //this.cells.addClassOnOver('fc-state-hover');
69510         
69511         var cells = this.cells.elements;
69512         var textEls = this.textNodes;
69513         
69514         //Roo.each(cells, function(cell){
69515         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
69516         //});
69517         
69518         days += startingPos;
69519
69520         // convert everything to numbers so it's fast
69521         var day = 86400000;
69522         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
69523         //Roo.log(d);
69524         //Roo.log(pm);
69525         //Roo.log(prevStart);
69526         
69527         var today = new Date().clearTime().getTime();
69528         var sel = date.clearTime().getTime();
69529         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
69530         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
69531         var ddMatch = this.disabledDatesRE;
69532         var ddText = this.disabledDatesText;
69533         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
69534         var ddaysText = this.disabledDaysText;
69535         var format = this.format;
69536         
69537         var setCellClass = function(cal, cell){
69538             
69539             //Roo.log('set Cell Class');
69540             cell.title = "";
69541             var t = d.getTime();
69542             
69543             //Roo.log(d);
69544             
69545             
69546             cell.dateValue = t;
69547             if(t == today){
69548                 cell.className += " fc-today";
69549                 cell.className += " fc-state-highlight";
69550                 cell.title = cal.todayText;
69551             }
69552             if(t == sel){
69553                 // disable highlight in other month..
69554                 cell.className += " fc-state-highlight";
69555                 
69556             }
69557             // disabling
69558             if(t < min) {
69559                 //cell.className = " fc-state-disabled";
69560                 cell.title = cal.minText;
69561                 return;
69562             }
69563             if(t > max) {
69564                 //cell.className = " fc-state-disabled";
69565                 cell.title = cal.maxText;
69566                 return;
69567             }
69568             if(ddays){
69569                 if(ddays.indexOf(d.getDay()) != -1){
69570                     // cell.title = ddaysText;
69571                    // cell.className = " fc-state-disabled";
69572                 }
69573             }
69574             if(ddMatch && format){
69575                 var fvalue = d.dateFormat(format);
69576                 if(ddMatch.test(fvalue)){
69577                     cell.title = ddText.replace("%0", fvalue);
69578                    cell.className = " fc-state-disabled";
69579                 }
69580             }
69581             
69582             if (!cell.initialClassName) {
69583                 cell.initialClassName = cell.dom.className;
69584             }
69585             
69586             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
69587         };
69588
69589         var i = 0;
69590         
69591         for(; i < startingPos; i++) {
69592             cells[i].dayName =  (++prevStart);
69593             Roo.log(textEls[i]);
69594             d.setDate(d.getDate()+1);
69595             
69596             //cells[i].className = "fc-past fc-other-month";
69597             setCellClass(this, cells[i]);
69598         }
69599         
69600         var intDay = 0;
69601         
69602         for(; i < days; i++){
69603             intDay = i - startingPos + 1;
69604             cells[i].dayName =  (intDay);
69605             d.setDate(d.getDate()+1);
69606             
69607             cells[i].className = ''; // "x-date-active";
69608             setCellClass(this, cells[i]);
69609         }
69610         var extraDays = 0;
69611         
69612         for(; i < 42; i++) {
69613             //textEls[i].innerHTML = (++extraDays);
69614             
69615             d.setDate(d.getDate()+1);
69616             cells[i].dayName = (++extraDays);
69617             cells[i].className = "fc-future fc-other-month";
69618             setCellClass(this, cells[i]);
69619         }
69620         
69621         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
69622         
69623         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
69624         
69625         // this will cause all the cells to mis
69626         var rows= [];
69627         var i =0;
69628         for (var r = 0;r < 6;r++) {
69629             for (var c =0;c < 7;c++) {
69630                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
69631             }    
69632         }
69633         
69634         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
69635         for(i=0;i<cells.length;i++) {
69636             
69637             this.cells.elements[i].dayName = cells[i].dayName ;
69638             this.cells.elements[i].className = cells[i].className;
69639             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
69640             this.cells.elements[i].title = cells[i].title ;
69641             this.cells.elements[i].dateValue = cells[i].dateValue ;
69642         }
69643         
69644         
69645         
69646         
69647         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
69648         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
69649         
69650         ////if(totalRows != 6){
69651             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
69652            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
69653        // }
69654         
69655         this.fireEvent('monthchange', this, date);
69656         
69657         
69658     },
69659  /**
69660      * Returns the grid's SelectionModel.
69661      * @return {SelectionModel}
69662      */
69663     getSelectionModel : function(){
69664         if(!this.selModel){
69665             this.selModel = new Roo.grid.CellSelectionModel();
69666         }
69667         return this.selModel;
69668     },
69669
69670     load: function() {
69671         this.eventStore.load()
69672         
69673         
69674         
69675     },
69676     
69677     findCell : function(dt) {
69678         dt = dt.clearTime().getTime();
69679         var ret = false;
69680         this.cells.each(function(c){
69681             //Roo.log("check " +c.dateValue + '?=' + dt);
69682             if(c.dateValue == dt){
69683                 ret = c;
69684                 return false;
69685             }
69686             return true;
69687         });
69688         
69689         return ret;
69690     },
69691     
69692     findCells : function(rec) {
69693         var s = rec.data.start_dt.clone().clearTime().getTime();
69694        // Roo.log(s);
69695         var e= rec.data.end_dt.clone().clearTime().getTime();
69696        // Roo.log(e);
69697         var ret = [];
69698         this.cells.each(function(c){
69699              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
69700             
69701             if(c.dateValue > e){
69702                 return ;
69703             }
69704             if(c.dateValue < s){
69705                 return ;
69706             }
69707             ret.push(c);
69708         });
69709         
69710         return ret;    
69711     },
69712     
69713     findBestRow: function(cells)
69714     {
69715         var ret = 0;
69716         
69717         for (var i =0 ; i < cells.length;i++) {
69718             ret  = Math.max(cells[i].rows || 0,ret);
69719         }
69720         return ret;
69721         
69722     },
69723     
69724     
69725     addItem : function(rec)
69726     {
69727         // look for vertical location slot in
69728         var cells = this.findCells(rec);
69729         
69730         rec.row = this.findBestRow(cells);
69731         
69732         // work out the location.
69733         
69734         var crow = false;
69735         var rows = [];
69736         for(var i =0; i < cells.length; i++) {
69737             if (!crow) {
69738                 crow = {
69739                     start : cells[i],
69740                     end :  cells[i]
69741                 };
69742                 continue;
69743             }
69744             if (crow.start.getY() == cells[i].getY()) {
69745                 // on same row.
69746                 crow.end = cells[i];
69747                 continue;
69748             }
69749             // different row.
69750             rows.push(crow);
69751             crow = {
69752                 start: cells[i],
69753                 end : cells[i]
69754             };
69755             
69756         }
69757         
69758         rows.push(crow);
69759         rec.els = [];
69760         rec.rows = rows;
69761         rec.cells = cells;
69762         for (var i = 0; i < cells.length;i++) {
69763             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
69764             
69765         }
69766         
69767         
69768     },
69769     
69770     clearEvents: function() {
69771         
69772         if (!this.eventStore.getCount()) {
69773             return;
69774         }
69775         // reset number of rows in cells.
69776         Roo.each(this.cells.elements, function(c){
69777             c.rows = 0;
69778         });
69779         
69780         this.eventStore.each(function(e) {
69781             this.clearEvent(e);
69782         },this);
69783         
69784     },
69785     
69786     clearEvent : function(ev)
69787     {
69788         if (ev.els) {
69789             Roo.each(ev.els, function(el) {
69790                 el.un('mouseenter' ,this.onEventEnter, this);
69791                 el.un('mouseleave' ,this.onEventLeave, this);
69792                 el.remove();
69793             },this);
69794             ev.els = [];
69795         }
69796     },
69797     
69798     
69799     renderEvent : function(ev,ctr) {
69800         if (!ctr) {
69801              ctr = this.view.el.select('.fc-event-container',true).first();
69802         }
69803         
69804          
69805         this.clearEvent(ev);
69806             //code
69807        
69808         
69809         
69810         ev.els = [];
69811         var cells = ev.cells;
69812         var rows = ev.rows;
69813         this.fireEvent('eventrender', this, ev);
69814         
69815         for(var i =0; i < rows.length; i++) {
69816             
69817             cls = '';
69818             if (i == 0) {
69819                 cls += ' fc-event-start';
69820             }
69821             if ((i+1) == rows.length) {
69822                 cls += ' fc-event-end';
69823             }
69824             
69825             //Roo.log(ev.data);
69826             // how many rows should it span..
69827             var cg = this.eventTmpl.append(ctr,Roo.apply({
69828                 fccls : cls
69829                 
69830             }, ev.data) , true);
69831             
69832             
69833             cg.on('mouseenter' ,this.onEventEnter, this, ev);
69834             cg.on('mouseleave' ,this.onEventLeave, this, ev);
69835             cg.on('click', this.onEventClick, this, ev);
69836             
69837             ev.els.push(cg);
69838             
69839             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
69840             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
69841             //Roo.log(cg);
69842              
69843             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
69844             cg.setWidth(ebox.right - sbox.x -2);
69845         }
69846     },
69847     
69848     renderEvents: function()
69849     {   
69850         // first make sure there is enough space..
69851         
69852         if (!this.eventTmpl) {
69853             this.eventTmpl = new Roo.Template(
69854                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
69855                     '<div class="fc-event-inner">' +
69856                         '<span class="fc-event-time">{time}</span>' +
69857                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
69858                     '</div>' +
69859                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
69860                 '</div>'
69861             );
69862                 
69863         }
69864                
69865         
69866         
69867         this.cells.each(function(c) {
69868             //Roo.log(c.select('.fc-day-content div',true).first());
69869             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
69870         });
69871         
69872         var ctr = this.view.el.select('.fc-event-container',true).first();
69873         
69874         var cls;
69875         this.eventStore.each(function(ev){
69876             
69877             this.renderEvent(ev);
69878              
69879              
69880         }, this);
69881         this.view.layout();
69882         
69883     },
69884     
69885     onEventEnter: function (e, el,event,d) {
69886         this.fireEvent('evententer', this, el, event);
69887     },
69888     
69889     onEventLeave: function (e, el,event,d) {
69890         this.fireEvent('eventleave', this, el, event);
69891     },
69892     
69893     onEventClick: function (e, el,event,d) {
69894         this.fireEvent('eventclick', this, el, event);
69895     },
69896     
69897     onMonthChange: function () {
69898         this.store.load();
69899     },
69900     
69901     onLoad: function () {
69902         
69903         //Roo.log('calendar onload');
69904 //         
69905         if(this.eventStore.getCount() > 0){
69906             
69907            
69908             
69909             this.eventStore.each(function(d){
69910                 
69911                 
69912                 // FIXME..
69913                 var add =   d.data;
69914                 if (typeof(add.end_dt) == 'undefined')  {
69915                     Roo.log("Missing End time in calendar data: ");
69916                     Roo.log(d);
69917                     return;
69918                 }
69919                 if (typeof(add.start_dt) == 'undefined')  {
69920                     Roo.log("Missing Start time in calendar data: ");
69921                     Roo.log(d);
69922                     return;
69923                 }
69924                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
69925                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
69926                 add.id = add.id || d.id;
69927                 add.title = add.title || '??';
69928                 
69929                 this.addItem(d);
69930                 
69931              
69932             },this);
69933         }
69934         
69935         this.renderEvents();
69936     }
69937     
69938
69939 });
69940 /*
69941  grid : {
69942                 xtype: 'Grid',
69943                 xns: Roo.grid,
69944                 listeners : {
69945                     render : function ()
69946                     {
69947                         _this.grid = this;
69948                         
69949                         if (!this.view.el.hasClass('course-timesheet')) {
69950                             this.view.el.addClass('course-timesheet');
69951                         }
69952                         if (this.tsStyle) {
69953                             this.ds.load({});
69954                             return; 
69955                         }
69956                         Roo.log('width');
69957                         Roo.log(_this.grid.view.el.getWidth());
69958                         
69959                         
69960                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
69961                             '.course-timesheet .x-grid-row' : {
69962                                 height: '80px'
69963                             },
69964                             '.x-grid-row td' : {
69965                                 'vertical-align' : 0
69966                             },
69967                             '.course-edit-link' : {
69968                                 'color' : 'blue',
69969                                 'text-overflow' : 'ellipsis',
69970                                 'overflow' : 'hidden',
69971                                 'white-space' : 'nowrap',
69972                                 'cursor' : 'pointer'
69973                             },
69974                             '.sub-link' : {
69975                                 'color' : 'green'
69976                             },
69977                             '.de-act-sup-link' : {
69978                                 'color' : 'purple',
69979                                 'text-decoration' : 'line-through'
69980                             },
69981                             '.de-act-link' : {
69982                                 'color' : 'red',
69983                                 'text-decoration' : 'line-through'
69984                             },
69985                             '.course-timesheet .course-highlight' : {
69986                                 'border-top-style': 'dashed !important',
69987                                 'border-bottom-bottom': 'dashed !important'
69988                             },
69989                             '.course-timesheet .course-item' : {
69990                                 'font-family'   : 'tahoma, arial, helvetica',
69991                                 'font-size'     : '11px',
69992                                 'overflow'      : 'hidden',
69993                                 'padding-left'  : '10px',
69994                                 'padding-right' : '10px',
69995                                 'padding-top' : '10px' 
69996                             }
69997                             
69998                         }, Roo.id());
69999                                 this.ds.load({});
70000                     }
70001                 },
70002                 autoWidth : true,
70003                 monitorWindowResize : false,
70004                 cellrenderer : function(v,x,r)
70005                 {
70006                     return v;
70007                 },
70008                 sm : {
70009                     xtype: 'CellSelectionModel',
70010                     xns: Roo.grid
70011                 },
70012                 dataSource : {
70013                     xtype: 'Store',
70014                     xns: Roo.data,
70015                     listeners : {
70016                         beforeload : function (_self, options)
70017                         {
70018                             options.params = options.params || {};
70019                             options.params._month = _this.monthField.getValue();
70020                             options.params.limit = 9999;
70021                             options.params['sort'] = 'when_dt';    
70022                             options.params['dir'] = 'ASC';    
70023                             this.proxy.loadResponse = this.loadResponse;
70024                             Roo.log("load?");
70025                             //this.addColumns();
70026                         },
70027                         load : function (_self, records, options)
70028                         {
70029                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
70030                                 // if you click on the translation.. you can edit it...
70031                                 var el = Roo.get(this);
70032                                 var id = el.dom.getAttribute('data-id');
70033                                 var d = el.dom.getAttribute('data-date');
70034                                 var t = el.dom.getAttribute('data-time');
70035                                 //var id = this.child('span').dom.textContent;
70036                                 
70037                                 //Roo.log(this);
70038                                 Pman.Dialog.CourseCalendar.show({
70039                                     id : id,
70040                                     when_d : d,
70041                                     when_t : t,
70042                                     productitem_active : id ? 1 : 0
70043                                 }, function() {
70044                                     _this.grid.ds.load({});
70045                                 });
70046                            
70047                            });
70048                            
70049                            _this.panel.fireEvent('resize', [ '', '' ]);
70050                         }
70051                     },
70052                     loadResponse : function(o, success, response){
70053                             // this is overridden on before load..
70054                             
70055                             Roo.log("our code?");       
70056                             //Roo.log(success);
70057                             //Roo.log(response)
70058                             delete this.activeRequest;
70059                             if(!success){
70060                                 this.fireEvent("loadexception", this, o, response);
70061                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
70062                                 return;
70063                             }
70064                             var result;
70065                             try {
70066                                 result = o.reader.read(response);
70067                             }catch(e){
70068                                 Roo.log("load exception?");
70069                                 this.fireEvent("loadexception", this, o, response, e);
70070                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
70071                                 return;
70072                             }
70073                             Roo.log("ready...");        
70074                             // loop through result.records;
70075                             // and set this.tdate[date] = [] << array of records..
70076                             _this.tdata  = {};
70077                             Roo.each(result.records, function(r){
70078                                 //Roo.log(r.data);
70079                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
70080                                     _this.tdata[r.data.when_dt.format('j')] = [];
70081                                 }
70082                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
70083                             });
70084                             
70085                             //Roo.log(_this.tdata);
70086                             
70087                             result.records = [];
70088                             result.totalRecords = 6;
70089                     
70090                             // let's generate some duumy records for the rows.
70091                             //var st = _this.dateField.getValue();
70092                             
70093                             // work out monday..
70094                             //st = st.add(Date.DAY, -1 * st.format('w'));
70095                             
70096                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
70097                             
70098                             var firstOfMonth = date.getFirstDayOfMonth();
70099                             var days = date.getDaysInMonth();
70100                             var d = 1;
70101                             var firstAdded = false;
70102                             for (var i = 0; i < result.totalRecords ; i++) {
70103                                 //var d= st.add(Date.DAY, i);
70104                                 var row = {};
70105                                 var added = 0;
70106                                 for(var w = 0 ; w < 7 ; w++){
70107                                     if(!firstAdded && firstOfMonth != w){
70108                                         continue;
70109                                     }
70110                                     if(d > days){
70111                                         continue;
70112                                     }
70113                                     firstAdded = true;
70114                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
70115                                     row['weekday'+w] = String.format(
70116                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
70117                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
70118                                                     d,
70119                                                     date.format('Y-m-')+dd
70120                                                 );
70121                                     added++;
70122                                     if(typeof(_this.tdata[d]) != 'undefined'){
70123                                         Roo.each(_this.tdata[d], function(r){
70124                                             var is_sub = '';
70125                                             var deactive = '';
70126                                             var id = r.id;
70127                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
70128                                             if(r.parent_id*1>0){
70129                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
70130                                                 id = r.parent_id;
70131                                             }
70132                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
70133                                                 deactive = 'de-act-link';
70134                                             }
70135                                             
70136                                             row['weekday'+w] += String.format(
70137                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
70138                                                     id, //0
70139                                                     r.product_id_name, //1
70140                                                     r.when_dt.format('h:ia'), //2
70141                                                     is_sub, //3
70142                                                     deactive, //4
70143                                                     desc // 5
70144                                             );
70145                                         });
70146                                     }
70147                                     d++;
70148                                 }
70149                                 
70150                                 // only do this if something added..
70151                                 if(added > 0){ 
70152                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
70153                                 }
70154                                 
70155                                 
70156                                 // push it twice. (second one with an hour..
70157                                 
70158                             }
70159                             //Roo.log(result);
70160                             this.fireEvent("load", this, o, o.request.arg);
70161                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
70162                         },
70163                     sortInfo : {field: 'when_dt', direction : 'ASC' },
70164                     proxy : {
70165                         xtype: 'HttpProxy',
70166                         xns: Roo.data,
70167                         method : 'GET',
70168                         url : baseURL + '/Roo/Shop_course.php'
70169                     },
70170                     reader : {
70171                         xtype: 'JsonReader',
70172                         xns: Roo.data,
70173                         id : 'id',
70174                         fields : [
70175                             {
70176                                 'name': 'id',
70177                                 'type': 'int'
70178                             },
70179                             {
70180                                 'name': 'when_dt',
70181                                 'type': 'string'
70182                             },
70183                             {
70184                                 'name': 'end_dt',
70185                                 'type': 'string'
70186                             },
70187                             {
70188                                 'name': 'parent_id',
70189                                 'type': 'int'
70190                             },
70191                             {
70192                                 'name': 'product_id',
70193                                 'type': 'int'
70194                             },
70195                             {
70196                                 'name': 'productitem_id',
70197                                 'type': 'int'
70198                             },
70199                             {
70200                                 'name': 'guid',
70201                                 'type': 'int'
70202                             }
70203                         ]
70204                     }
70205                 },
70206                 toolbar : {
70207                     xtype: 'Toolbar',
70208                     xns: Roo,
70209                     items : [
70210                         {
70211                             xtype: 'Button',
70212                             xns: Roo.Toolbar,
70213                             listeners : {
70214                                 click : function (_self, e)
70215                                 {
70216                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
70217                                     sd.setMonth(sd.getMonth()-1);
70218                                     _this.monthField.setValue(sd.format('Y-m-d'));
70219                                     _this.grid.ds.load({});
70220                                 }
70221                             },
70222                             text : "Back"
70223                         },
70224                         {
70225                             xtype: 'Separator',
70226                             xns: Roo.Toolbar
70227                         },
70228                         {
70229                             xtype: 'MonthField',
70230                             xns: Roo.form,
70231                             listeners : {
70232                                 render : function (_self)
70233                                 {
70234                                     _this.monthField = _self;
70235                                    // _this.monthField.set  today
70236                                 },
70237                                 select : function (combo, date)
70238                                 {
70239                                     _this.grid.ds.load({});
70240                                 }
70241                             },
70242                             value : (function() { return new Date(); })()
70243                         },
70244                         {
70245                             xtype: 'Separator',
70246                             xns: Roo.Toolbar
70247                         },
70248                         {
70249                             xtype: 'TextItem',
70250                             xns: Roo.Toolbar,
70251                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
70252                         },
70253                         {
70254                             xtype: 'Fill',
70255                             xns: Roo.Toolbar
70256                         },
70257                         {
70258                             xtype: 'Button',
70259                             xns: Roo.Toolbar,
70260                             listeners : {
70261                                 click : function (_self, e)
70262                                 {
70263                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
70264                                     sd.setMonth(sd.getMonth()+1);
70265                                     _this.monthField.setValue(sd.format('Y-m-d'));
70266                                     _this.grid.ds.load({});
70267                                 }
70268                             },
70269                             text : "Next"
70270                         }
70271                     ]
70272                 },
70273                  
70274             }
70275         };
70276         
70277         *//*
70278  * Based on:
70279  * Ext JS Library 1.1.1
70280  * Copyright(c) 2006-2007, Ext JS, LLC.
70281  *
70282  * Originally Released Under LGPL - original licence link has changed is not relivant.
70283  *
70284  * Fork - LGPL
70285  * <script type="text/javascript">
70286  */
70287  
70288 /**
70289  * @class Roo.LoadMask
70290  * A simple utility class for generically masking elements while loading data.  If the element being masked has
70291  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
70292  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
70293  * element's UpdateManager load indicator and will be destroyed after the initial load.
70294  * @constructor
70295  * Create a new LoadMask
70296  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
70297  * @param {Object} config The config object
70298  */
70299 Roo.LoadMask = function(el, config){
70300     this.el = Roo.get(el);
70301     Roo.apply(this, config);
70302     if(this.store){
70303         this.store.on('beforeload', this.onBeforeLoad, this);
70304         this.store.on('load', this.onLoad, this);
70305         this.store.on('loadexception', this.onLoadException, this);
70306         this.removeMask = false;
70307     }else{
70308         var um = this.el.getUpdateManager();
70309         um.showLoadIndicator = false; // disable the default indicator
70310         um.on('beforeupdate', this.onBeforeLoad, this);
70311         um.on('update', this.onLoad, this);
70312         um.on('failure', this.onLoad, this);
70313         this.removeMask = true;
70314     }
70315 };
70316
70317 Roo.LoadMask.prototype = {
70318     /**
70319      * @cfg {Boolean} removeMask
70320      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
70321      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
70322      */
70323     removeMask : false,
70324     /**
70325      * @cfg {String} msg
70326      * The text to display in a centered loading message box (defaults to 'Loading...')
70327      */
70328     msg : 'Loading...',
70329     /**
70330      * @cfg {String} msgCls
70331      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
70332      */
70333     msgCls : 'x-mask-loading',
70334
70335     /**
70336      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
70337      * @type Boolean
70338      */
70339     disabled: false,
70340
70341     /**
70342      * Disables the mask to prevent it from being displayed
70343      */
70344     disable : function(){
70345        this.disabled = true;
70346     },
70347
70348     /**
70349      * Enables the mask so that it can be displayed
70350      */
70351     enable : function(){
70352         this.disabled = false;
70353     },
70354     
70355     onLoadException : function()
70356     {
70357         Roo.log(arguments);
70358         
70359         if (typeof(arguments[3]) != 'undefined') {
70360             Roo.MessageBox.alert("Error loading",arguments[3]);
70361         } 
70362         /*
70363         try {
70364             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
70365                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
70366             }   
70367         } catch(e) {
70368             
70369         }
70370         */
70371     
70372         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
70373     },
70374     // private
70375     onLoad : function()
70376     {
70377         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
70378     },
70379
70380     // private
70381     onBeforeLoad : function(){
70382         if(!this.disabled){
70383             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
70384         }
70385     },
70386
70387     // private
70388     destroy : function(){
70389         if(this.store){
70390             this.store.un('beforeload', this.onBeforeLoad, this);
70391             this.store.un('load', this.onLoad, this);
70392             this.store.un('loadexception', this.onLoadException, this);
70393         }else{
70394             var um = this.el.getUpdateManager();
70395             um.un('beforeupdate', this.onBeforeLoad, this);
70396             um.un('update', this.onLoad, this);
70397             um.un('failure', this.onLoad, this);
70398         }
70399     }
70400 };/*
70401  * Based on:
70402  * Ext JS Library 1.1.1
70403  * Copyright(c) 2006-2007, Ext JS, LLC.
70404  *
70405  * Originally Released Under LGPL - original licence link has changed is not relivant.
70406  *
70407  * Fork - LGPL
70408  * <script type="text/javascript">
70409  */
70410
70411
70412 /**
70413  * @class Roo.XTemplate
70414  * @extends Roo.Template
70415  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
70416 <pre><code>
70417 var t = new Roo.XTemplate(
70418         '&lt;select name="{name}"&gt;',
70419                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
70420         '&lt;/select&gt;'
70421 );
70422  
70423 // then append, applying the master template values
70424  </code></pre>
70425  *
70426  * Supported features:
70427  *
70428  *  Tags:
70429
70430 <pre><code>
70431       {a_variable} - output encoded.
70432       {a_variable.format:("Y-m-d")} - call a method on the variable
70433       {a_variable:raw} - unencoded output
70434       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
70435       {a_variable:this.method_on_template(...)} - call a method on the template object.
70436  
70437 </code></pre>
70438  *  The tpl tag:
70439 <pre><code>
70440         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
70441         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
70442         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
70443         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
70444   
70445         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
70446         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
70447 </code></pre>
70448  *      
70449  */
70450 Roo.XTemplate = function()
70451 {
70452     Roo.XTemplate.superclass.constructor.apply(this, arguments);
70453     if (this.html) {
70454         this.compile();
70455     }
70456 };
70457
70458
70459 Roo.extend(Roo.XTemplate, Roo.Template, {
70460
70461     /**
70462      * The various sub templates
70463      */
70464     tpls : false,
70465     /**
70466      *
70467      * basic tag replacing syntax
70468      * WORD:WORD()
70469      *
70470      * // you can fake an object call by doing this
70471      *  x.t:(test,tesT) 
70472      * 
70473      */
70474     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
70475
70476     /**
70477      * compile the template
70478      *
70479      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
70480      *
70481      */
70482     compile: function()
70483     {
70484         var s = this.html;
70485      
70486         s = ['<tpl>', s, '</tpl>'].join('');
70487     
70488         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
70489             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
70490             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
70491             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
70492             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
70493             m,
70494             id     = 0,
70495             tpls   = [];
70496     
70497         while(true == !!(m = s.match(re))){
70498             var forMatch   = m[0].match(nameRe),
70499                 ifMatch   = m[0].match(ifRe),
70500                 execMatch   = m[0].match(execRe),
70501                 namedMatch   = m[0].match(namedRe),
70502                 
70503                 exp  = null, 
70504                 fn   = null,
70505                 exec = null,
70506                 name = forMatch && forMatch[1] ? forMatch[1] : '';
70507                 
70508             if (ifMatch) {
70509                 // if - puts fn into test..
70510                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
70511                 if(exp){
70512                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
70513                 }
70514             }
70515             
70516             if (execMatch) {
70517                 // exec - calls a function... returns empty if true is  returned.
70518                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
70519                 if(exp){
70520                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
70521                 }
70522             }
70523             
70524             
70525             if (name) {
70526                 // for = 
70527                 switch(name){
70528                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
70529                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
70530                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
70531                 }
70532             }
70533             var uid = namedMatch ? namedMatch[1] : id;
70534             
70535             
70536             tpls.push({
70537                 id:     namedMatch ? namedMatch[1] : id,
70538                 target: name,
70539                 exec:   exec,
70540                 test:   fn,
70541                 body:   m[1] || ''
70542             });
70543             if (namedMatch) {
70544                 s = s.replace(m[0], '');
70545             } else { 
70546                 s = s.replace(m[0], '{xtpl'+ id + '}');
70547             }
70548             ++id;
70549         }
70550         this.tpls = [];
70551         for(var i = tpls.length-1; i >= 0; --i){
70552             this.compileTpl(tpls[i]);
70553             this.tpls[tpls[i].id] = tpls[i];
70554         }
70555         this.master = tpls[tpls.length-1];
70556         return this;
70557     },
70558     /**
70559      * same as applyTemplate, except it's done to one of the subTemplates
70560      * when using named templates, you can do:
70561      *
70562      * var str = pl.applySubTemplate('your-name', values);
70563      *
70564      * 
70565      * @param {Number} id of the template
70566      * @param {Object} values to apply to template
70567      * @param {Object} parent (normaly the instance of this object)
70568      */
70569     applySubTemplate : function(id, values, parent)
70570     {
70571         
70572         
70573         var t = this.tpls[id];
70574         
70575         
70576         try { 
70577             if(t.test && !t.test.call(this, values, parent)){
70578                 return '';
70579             }
70580         } catch(e) {
70581             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
70582             Roo.log(e.toString());
70583             Roo.log(t.test);
70584             return ''
70585         }
70586         try { 
70587             
70588             if(t.exec && t.exec.call(this, values, parent)){
70589                 return '';
70590             }
70591         } catch(e) {
70592             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
70593             Roo.log(e.toString());
70594             Roo.log(t.exec);
70595             return ''
70596         }
70597         try {
70598             var vs = t.target ? t.target.call(this, values, parent) : values;
70599             parent = t.target ? values : parent;
70600             if(t.target && vs instanceof Array){
70601                 var buf = [];
70602                 for(var i = 0, len = vs.length; i < len; i++){
70603                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
70604                 }
70605                 return buf.join('');
70606             }
70607             return t.compiled.call(this, vs, parent);
70608         } catch (e) {
70609             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
70610             Roo.log(e.toString());
70611             Roo.log(t.compiled);
70612             return '';
70613         }
70614     },
70615
70616     compileTpl : function(tpl)
70617     {
70618         var fm = Roo.util.Format;
70619         var useF = this.disableFormats !== true;
70620         var sep = Roo.isGecko ? "+" : ",";
70621         var undef = function(str) {
70622             Roo.log("Property not found :"  + str);
70623             return '';
70624         };
70625         
70626         var fn = function(m, name, format, args)
70627         {
70628             //Roo.log(arguments);
70629             args = args ? args.replace(/\\'/g,"'") : args;
70630             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
70631             if (typeof(format) == 'undefined') {
70632                 format= 'htmlEncode';
70633             }
70634             if (format == 'raw' ) {
70635                 format = false;
70636             }
70637             
70638             if(name.substr(0, 4) == 'xtpl'){
70639                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
70640             }
70641             
70642             // build an array of options to determine if value is undefined..
70643             
70644             // basically get 'xxxx.yyyy' then do
70645             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
70646             //    (function () { Roo.log("Property not found"); return ''; })() :
70647             //    ......
70648             
70649             var udef_ar = [];
70650             var lookfor = '';
70651             Roo.each(name.split('.'), function(st) {
70652                 lookfor += (lookfor.length ? '.': '') + st;
70653                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
70654             });
70655             
70656             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
70657             
70658             
70659             if(format && useF){
70660                 
70661                 args = args ? ',' + args : "";
70662                  
70663                 if(format.substr(0, 5) != "this."){
70664                     format = "fm." + format + '(';
70665                 }else{
70666                     format = 'this.call("'+ format.substr(5) + '", ';
70667                     args = ", values";
70668                 }
70669                 
70670                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
70671             }
70672              
70673             if (args.length) {
70674                 // called with xxyx.yuu:(test,test)
70675                 // change to ()
70676                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
70677             }
70678             // raw.. - :raw modifier..
70679             return "'"+ sep + udef_st  + name + ")"+sep+"'";
70680             
70681         };
70682         var body;
70683         // branched to use + in gecko and [].join() in others
70684         if(Roo.isGecko){
70685             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
70686                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
70687                     "';};};";
70688         }else{
70689             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
70690             body.push(tpl.body.replace(/(\r\n|\n)/g,
70691                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
70692             body.push("'].join('');};};");
70693             body = body.join('');
70694         }
70695         
70696         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
70697        
70698         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
70699         eval(body);
70700         
70701         return this;
70702     },
70703
70704     applyTemplate : function(values){
70705         return this.master.compiled.call(this, values, {});
70706         //var s = this.subs;
70707     },
70708
70709     apply : function(){
70710         return this.applyTemplate.apply(this, arguments);
70711     }
70712
70713  });
70714
70715 Roo.XTemplate.from = function(el){
70716     el = Roo.getDom(el);
70717     return new Roo.XTemplate(el.value || el.innerHTML);
70718 };// old names for panel elements
70719 Roo.GridPanel = Roo.panel.Grid;
70720 Roo.CalendarPanel = Roo.panel.Calendar;
70721 Roo.ContentPanel = Roo.panel.Content;
70722 Roo.NestedLayoutPanel = Roo.panel.NestedLayout;
70723 Roo.TabPanel = Roo.panel.Tab;
70724 Roo.TabPanelItem = Roo.panel.TabItem;
70725 Roo.TreePanel = Roo.panel.Tree;
70726
70727
70728 // phase 2 update
70729 Roo.ScrollPanel = Roo.panel.Scroll;
70730
70731 Roo.BorderLayout = Roo.layout.Border;
70732 Roo.BasicLayoutRegion = Roo.layout.BasicRegion;
70733 Roo.LayoutRegion = Roo.layout.Region;
70734 Roo.SplitLayoutRegion = Roo.layout.SplitRegion;
70735 Roo.LayoutManager = Roo.layout.Manager;
70736
70737
70738 Roo.NorthLayoutRegion = Roo.layout.North;
70739 Roo.EastLayoutRegion = Roo.layout.East;
70740 Roo.WestLayoutRegion = Roo.layout.West;
70741 Roo.SouthLayoutRegion = Roo.layout.South;
70742 Roo.CenterLayoutRegion = Roo.layout.Center;
70743
70744
70745 Roo.LayoutStateManager  = Roo.layout.StateManager;
70746 Roo.ReaderLayout = Roo.layout.Reader;
70747