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, include_disabled) {
2709             
2710             include_disabled = typeof(include_disabled) == 'undefined' ? false : include_disabled;
2711
2712             if(typeof form == 'string') {
2713                 form = (document.getElementById(form) || document.forms[form]);
2714             }
2715
2716             var el, name, val, disabled, data = '', hasSubmit = false;
2717             for (var i = 0; i < form.elements.length; i++) {
2718                 el = form.elements[i];
2719                 disabled = include_disabled ? false : form.elements[i].disabled;
2720                 name = form.elements[i].name;
2721                 val = form.elements[i].value;
2722
2723                 if (!disabled && name){
2724                     switch (el.type)
2725                             {
2726                         case 'select-one':
2727                         case 'select-multiple':
2728                             for (var j = 0; j < el.options.length; j++) {
2729                                 if (el.options[j].selected) {
2730                                     if (Roo.isIE) {
2731                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2732                                     }
2733                                     else {
2734                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2735                                     }
2736                                 }
2737                             }
2738                             break;
2739                         case 'radio':
2740                         case 'checkbox':
2741                             if (el.checked) {
2742                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2743                             }
2744                             break;
2745                         case 'file':
2746
2747                         case undefined:
2748
2749                         case 'reset':
2750
2751                         case 'button':
2752
2753                             break;
2754                         case 'submit':
2755                             if(hasSubmit == false) {
2756                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2757                                 hasSubmit = true;
2758                             }
2759                             break;
2760                         default:
2761                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2762                             break;
2763                     }
2764                 }
2765             }
2766             data = data.substr(0, data.length - 1);
2767             return data;
2768         },
2769
2770         headers:{},
2771
2772         hasHeaders:false,
2773
2774         useDefaultHeader:true,
2775
2776         defaultPostHeader:'application/x-www-form-urlencoded',
2777
2778         useDefaultXhrHeader:true,
2779
2780         defaultXhrHeader:'XMLHttpRequest',
2781
2782         hasDefaultHeaders:true,
2783
2784         defaultHeaders:{},
2785
2786         poll:{},
2787
2788         timeout:{},
2789
2790         pollInterval:50,
2791
2792         transactionId:0,
2793
2794         setProgId:function(id)
2795         {
2796             this.activeX.unshift(id);
2797         },
2798
2799         setDefaultPostHeader:function(b)
2800         {
2801             this.useDefaultHeader = b;
2802         },
2803
2804         setDefaultXhrHeader:function(b)
2805         {
2806             this.useDefaultXhrHeader = b;
2807         },
2808
2809         setPollingInterval:function(i)
2810         {
2811             if (typeof i == 'number' && isFinite(i)) {
2812                 this.pollInterval = i;
2813             }
2814         },
2815
2816         createXhrObject:function(transactionId)
2817         {
2818             var obj,http;
2819             try
2820             {
2821
2822                 http = new XMLHttpRequest();
2823
2824                 obj = { conn:http, tId:transactionId };
2825             }
2826             catch(e)
2827             {
2828                 for (var i = 0; i < this.activeX.length; ++i) {
2829                     try
2830                     {
2831
2832                         http = new ActiveXObject(this.activeX[i]);
2833
2834                         obj = { conn:http, tId:transactionId };
2835                         break;
2836                     }
2837                     catch(e) {
2838                     }
2839                 }
2840             }
2841             finally
2842             {
2843                 return obj;
2844             }
2845         },
2846
2847         getConnectionObject:function()
2848         {
2849             var o;
2850             var tId = this.transactionId;
2851
2852             try
2853             {
2854                 o = this.createXhrObject(tId);
2855                 if (o) {
2856                     this.transactionId++;
2857                 }
2858             }
2859             catch(e) {
2860             }
2861             finally
2862             {
2863                 return o;
2864             }
2865         },
2866
2867         asyncRequest:function(method, uri, callback, postData)
2868         {
2869             var o = this.getConnectionObject();
2870
2871             if (!o) {
2872                 return null;
2873             }
2874             else {
2875                 o.conn.open(method, uri, true);
2876
2877                 if (this.useDefaultXhrHeader) {
2878                     if (!this.defaultHeaders['X-Requested-With']) {
2879                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2880                     }
2881                 }
2882
2883                 if(postData && this.useDefaultHeader){
2884                     this.initHeader('Content-Type', this.defaultPostHeader);
2885                 }
2886
2887                  if (this.hasDefaultHeaders || this.hasHeaders) {
2888                     this.setHeader(o);
2889                 }
2890
2891                 this.handleReadyState(o, callback);
2892                 o.conn.send(postData || null);
2893
2894                 return o;
2895             }
2896         },
2897
2898         handleReadyState:function(o, callback)
2899         {
2900             var oConn = this;
2901
2902             if (callback && callback.timeout) {
2903                 
2904                 this.timeout[o.tId] = window.setTimeout(function() {
2905                     oConn.abort(o, callback, true);
2906                 }, callback.timeout);
2907             }
2908
2909             this.poll[o.tId] = window.setInterval(
2910                     function() {
2911                         if (o.conn && o.conn.readyState == 4) {
2912                             window.clearInterval(oConn.poll[o.tId]);
2913                             delete oConn.poll[o.tId];
2914
2915                             if(callback && callback.timeout) {
2916                                 window.clearTimeout(oConn.timeout[o.tId]);
2917                                 delete oConn.timeout[o.tId];
2918                             }
2919
2920                             oConn.handleTransactionResponse(o, callback);
2921                         }
2922                     }
2923                     , this.pollInterval);
2924         },
2925
2926         handleTransactionResponse:function(o, callback, isAbort)
2927         {
2928
2929             if (!callback) {
2930                 this.releaseObject(o);
2931                 return;
2932             }
2933
2934             var httpStatus, responseObject;
2935
2936             try
2937             {
2938                 if (o.conn.status !== undefined && o.conn.status != 0) {
2939                     httpStatus = o.conn.status;
2940                 }
2941                 else {
2942                     httpStatus = 13030;
2943                 }
2944             }
2945             catch(e) {
2946
2947
2948                 httpStatus = 13030;
2949             }
2950
2951             if (httpStatus >= 200 && httpStatus < 300) {
2952                 responseObject = this.createResponseObject(o, callback.argument);
2953                 if (callback.success) {
2954                     if (!callback.scope) {
2955                         callback.success(responseObject);
2956                     }
2957                     else {
2958
2959
2960                         callback.success.apply(callback.scope, [responseObject]);
2961                     }
2962                 }
2963             }
2964             else {
2965                 switch (httpStatus) {
2966
2967                     case 12002:
2968                     case 12029:
2969                     case 12030:
2970                     case 12031:
2971                     case 12152:
2972                     case 13030:
2973                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2974                         if (callback.failure) {
2975                             if (!callback.scope) {
2976                                 callback.failure(responseObject);
2977                             }
2978                             else {
2979                                 callback.failure.apply(callback.scope, [responseObject]);
2980                             }
2981                         }
2982                         break;
2983                     default:
2984                         responseObject = this.createResponseObject(o, callback.argument);
2985                         if (callback.failure) {
2986                             if (!callback.scope) {
2987                                 callback.failure(responseObject);
2988                             }
2989                             else {
2990                                 callback.failure.apply(callback.scope, [responseObject]);
2991                             }
2992                         }
2993                 }
2994             }
2995
2996             this.releaseObject(o);
2997             responseObject = null;
2998         },
2999
3000         createResponseObject:function(o, callbackArg)
3001         {
3002             var obj = {};
3003             var headerObj = {};
3004
3005             try
3006             {
3007                 var headerStr = o.conn.getAllResponseHeaders();
3008                 var header = headerStr.split('\n');
3009                 for (var i = 0; i < header.length; i++) {
3010                     var delimitPos = header[i].indexOf(':');
3011                     if (delimitPos != -1) {
3012                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
3013                     }
3014                 }
3015             }
3016             catch(e) {
3017             }
3018
3019             obj.tId = o.tId;
3020             obj.status = o.conn.status;
3021             obj.statusText = o.conn.statusText;
3022             obj.getResponseHeader = headerObj;
3023             obj.getAllResponseHeaders = headerStr;
3024             obj.responseText = o.conn.responseText;
3025             obj.responseXML = o.conn.responseXML;
3026
3027             if (typeof callbackArg !== undefined) {
3028                 obj.argument = callbackArg;
3029             }
3030
3031             return obj;
3032         },
3033
3034         createExceptionObject:function(tId, callbackArg, isAbort)
3035         {
3036             var COMM_CODE = 0;
3037             var COMM_ERROR = 'communication failure';
3038             var ABORT_CODE = -1;
3039             var ABORT_ERROR = 'transaction aborted';
3040
3041             var obj = {};
3042
3043             obj.tId = tId;
3044             if (isAbort) {
3045                 obj.status = ABORT_CODE;
3046                 obj.statusText = ABORT_ERROR;
3047             }
3048             else {
3049                 obj.status = COMM_CODE;
3050                 obj.statusText = COMM_ERROR;
3051             }
3052
3053             if (callbackArg) {
3054                 obj.argument = callbackArg;
3055             }
3056
3057             return obj;
3058         },
3059
3060         initHeader:function(label, value, isDefault)
3061         {
3062             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
3063
3064             if (headerObj[label] === undefined) {
3065                 headerObj[label] = value;
3066             }
3067             else {
3068
3069
3070                 headerObj[label] = value + "," + headerObj[label];
3071             }
3072
3073             if (isDefault) {
3074                 this.hasDefaultHeaders = true;
3075             }
3076             else {
3077                 this.hasHeaders = true;
3078             }
3079         },
3080
3081
3082         setHeader:function(o)
3083         {
3084             if (this.hasDefaultHeaders) {
3085                 for (var prop in this.defaultHeaders) {
3086                     if (this.defaultHeaders.hasOwnProperty(prop)) {
3087                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
3088                     }
3089                 }
3090             }
3091
3092             if (this.hasHeaders) {
3093                 for (var prop in this.headers) {
3094                     if (this.headers.hasOwnProperty(prop)) {
3095                         o.conn.setRequestHeader(prop, this.headers[prop]);
3096                     }
3097                 }
3098                 this.headers = {};
3099                 this.hasHeaders = false;
3100             }
3101         },
3102
3103         resetDefaultHeaders:function() {
3104             delete this.defaultHeaders;
3105             this.defaultHeaders = {};
3106             this.hasDefaultHeaders = false;
3107         },
3108
3109         abort:function(o, callback, isTimeout)
3110         {
3111             if(this.isCallInProgress(o)) {
3112                 o.conn.abort();
3113                 window.clearInterval(this.poll[o.tId]);
3114                 delete this.poll[o.tId];
3115                 if (isTimeout) {
3116                     delete this.timeout[o.tId];
3117                 }
3118
3119                 this.handleTransactionResponse(o, callback, true);
3120
3121                 return true;
3122             }
3123             else {
3124                 return false;
3125             }
3126         },
3127
3128
3129         isCallInProgress:function(o)
3130         {
3131             if (o && o.conn) {
3132                 return o.conn.readyState != 4 && o.conn.readyState != 0;
3133             }
3134             else {
3135
3136                 return false;
3137             }
3138         },
3139
3140
3141         releaseObject:function(o)
3142         {
3143
3144             o.conn = null;
3145
3146             o = null;
3147         },
3148
3149         activeX:[
3150         'MSXML2.XMLHTTP.3.0',
3151         'MSXML2.XMLHTTP',
3152         'Microsoft.XMLHTTP'
3153         ]
3154
3155
3156     };
3157 })();/*
3158  * Portions of this file are based on pieces of Yahoo User Interface Library
3159  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3160  * YUI licensed under the BSD License:
3161  * http://developer.yahoo.net/yui/license.txt
3162  * <script type="text/javascript">
3163  *
3164  */
3165
3166 Roo.lib.Region = function(t, r, b, l) {
3167     this.top = t;
3168     this[1] = t;
3169     this.right = r;
3170     this.bottom = b;
3171     this.left = l;
3172     this[0] = l;
3173 };
3174
3175
3176 Roo.lib.Region.prototype = {
3177     contains : function(region) {
3178         return ( region.left >= this.left &&
3179                  region.right <= this.right &&
3180                  region.top >= this.top &&
3181                  region.bottom <= this.bottom    );
3182
3183     },
3184
3185     getArea : function() {
3186         return ( (this.bottom - this.top) * (this.right - this.left) );
3187     },
3188
3189     intersect : function(region) {
3190         var t = Math.max(this.top, region.top);
3191         var r = Math.min(this.right, region.right);
3192         var b = Math.min(this.bottom, region.bottom);
3193         var l = Math.max(this.left, region.left);
3194
3195         if (b >= t && r >= l) {
3196             return new Roo.lib.Region(t, r, b, l);
3197         } else {
3198             return null;
3199         }
3200     },
3201     union : function(region) {
3202         var t = Math.min(this.top, region.top);
3203         var r = Math.max(this.right, region.right);
3204         var b = Math.max(this.bottom, region.bottom);
3205         var l = Math.min(this.left, region.left);
3206
3207         return new Roo.lib.Region(t, r, b, l);
3208     },
3209
3210     adjust : function(t, l, b, r) {
3211         this.top += t;
3212         this.left += l;
3213         this.right += r;
3214         this.bottom += b;
3215         return this;
3216     }
3217 };
3218
3219 Roo.lib.Region.getRegion = function(el) {
3220     var p = Roo.lib.Dom.getXY(el);
3221
3222     var t = p[1];
3223     var r = p[0] + el.offsetWidth;
3224     var b = p[1] + el.offsetHeight;
3225     var l = p[0];
3226
3227     return new Roo.lib.Region(t, r, b, l);
3228 };
3229 /*
3230  * Portions of this file are based on pieces of Yahoo User Interface Library
3231  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3232  * YUI licensed under the BSD License:
3233  * http://developer.yahoo.net/yui/license.txt
3234  * <script type="text/javascript">
3235  *
3236  */
3237 //@@dep Roo.lib.Region
3238
3239
3240 Roo.lib.Point = function(x, y) {
3241     if (x instanceof Array) {
3242         y = x[1];
3243         x = x[0];
3244     }
3245     this.x = this.right = this.left = this[0] = x;
3246     this.y = this.top = this.bottom = this[1] = y;
3247 };
3248
3249 Roo.lib.Point.prototype = new Roo.lib.Region();
3250 /*
3251  * Portions of this file are based on pieces of Yahoo User Interface Library
3252  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3253  * YUI licensed under the BSD License:
3254  * http://developer.yahoo.net/yui/license.txt
3255  * <script type="text/javascript">
3256  *
3257  */
3258  
3259 (function() {   
3260
3261     Roo.lib.Anim = {
3262         scroll : function(el, args, duration, easing, cb, scope) {
3263             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3264         },
3265
3266         motion : function(el, args, duration, easing, cb, scope) {
3267             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3268         },
3269
3270         color : function(el, args, duration, easing, cb, scope) {
3271             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3272         },
3273
3274         run : function(el, args, duration, easing, cb, scope, type) {
3275             type = type || Roo.lib.AnimBase;
3276             if (typeof easing == "string") {
3277                 easing = Roo.lib.Easing[easing];
3278             }
3279             var anim = new type(el, args, duration, easing);
3280             anim.animateX(function() {
3281                 Roo.callback(cb, scope);
3282             });
3283             return anim;
3284         }
3285     };
3286 })();/*
3287  * Portions of this file are based on pieces of Yahoo User Interface Library
3288  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3289  * YUI licensed under the BSD License:
3290  * http://developer.yahoo.net/yui/license.txt
3291  * <script type="text/javascript">
3292  *
3293  */
3294
3295 (function() {    
3296     var libFlyweight;
3297     
3298     function fly(el) {
3299         if (!libFlyweight) {
3300             libFlyweight = new Roo.Element.Flyweight();
3301         }
3302         libFlyweight.dom = el;
3303         return libFlyweight;
3304     }
3305
3306     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3307     
3308    
3309     
3310     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3311         if (el) {
3312             this.init(el, attributes, duration, method);
3313         }
3314     };
3315
3316     Roo.lib.AnimBase.fly = fly;
3317     
3318     
3319     
3320     Roo.lib.AnimBase.prototype = {
3321
3322         toString: function() {
3323             var el = this.getEl();
3324             var id = el.id || el.tagName;
3325             return ("Anim " + id);
3326         },
3327
3328         patterns: {
3329             noNegatives:        /width|height|opacity|padding/i,
3330             offsetAttribute:  /^((width|height)|(top|left))$/,
3331             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3332             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3333         },
3334
3335
3336         doMethod: function(attr, start, end) {
3337             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3338         },
3339
3340
3341         setAttribute: function(attr, val, unit) {
3342             if (this.patterns.noNegatives.test(attr)) {
3343                 val = (val > 0) ? val : 0;
3344             }
3345
3346             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3347         },
3348
3349
3350         getAttribute: function(attr) {
3351             var el = this.getEl();
3352             var val = fly(el).getStyle(attr);
3353
3354             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3355                 return parseFloat(val);
3356             }
3357
3358             var a = this.patterns.offsetAttribute.exec(attr) || [];
3359             var pos = !!( a[3] );
3360             var box = !!( a[2] );
3361
3362
3363             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3364                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3365             } else {
3366                 val = 0;
3367             }
3368
3369             return val;
3370         },
3371
3372
3373         getDefaultUnit: function(attr) {
3374             if (this.patterns.defaultUnit.test(attr)) {
3375                 return 'px';
3376             }
3377
3378             return '';
3379         },
3380
3381         animateX : function(callback, scope) {
3382             var f = function() {
3383                 this.onComplete.removeListener(f);
3384                 if (typeof callback == "function") {
3385                     callback.call(scope || this, this);
3386                 }
3387             };
3388             this.onComplete.addListener(f, this);
3389             this.animate();
3390         },
3391
3392
3393         setRuntimeAttribute: function(attr) {
3394             var start;
3395             var end;
3396             var attributes = this.attributes;
3397
3398             this.runtimeAttributes[attr] = {};
3399
3400             var isset = function(prop) {
3401                 return (typeof prop !== 'undefined');
3402             };
3403
3404             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3405                 return false;
3406             }
3407
3408             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3409
3410
3411             if (isset(attributes[attr]['to'])) {
3412                 end = attributes[attr]['to'];
3413             } else if (isset(attributes[attr]['by'])) {
3414                 if (start.constructor == Array) {
3415                     end = [];
3416                     for (var i = 0, len = start.length; i < len; ++i) {
3417                         end[i] = start[i] + attributes[attr]['by'][i];
3418                     }
3419                 } else {
3420                     end = start + attributes[attr]['by'];
3421                 }
3422             }
3423
3424             this.runtimeAttributes[attr].start = start;
3425             this.runtimeAttributes[attr].end = end;
3426
3427
3428             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3429         },
3430
3431
3432         init: function(el, attributes, duration, method) {
3433
3434             var isAnimated = false;
3435
3436
3437             var startTime = null;
3438
3439
3440             var actualFrames = 0;
3441
3442
3443             el = Roo.getDom(el);
3444
3445
3446             this.attributes = attributes || {};
3447
3448
3449             this.duration = duration || 1;
3450
3451
3452             this.method = method || Roo.lib.Easing.easeNone;
3453
3454
3455             this.useSeconds = true;
3456
3457
3458             this.currentFrame = 0;
3459
3460
3461             this.totalFrames = Roo.lib.AnimMgr.fps;
3462
3463
3464             this.getEl = function() {
3465                 return el;
3466             };
3467
3468
3469             this.isAnimated = function() {
3470                 return isAnimated;
3471             };
3472
3473
3474             this.getStartTime = function() {
3475                 return startTime;
3476             };
3477
3478             this.runtimeAttributes = {};
3479
3480
3481             this.animate = function() {
3482                 if (this.isAnimated()) {
3483                     return false;
3484                 }
3485
3486                 this.currentFrame = 0;
3487
3488                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3489
3490                 Roo.lib.AnimMgr.registerElement(this);
3491             };
3492
3493
3494             this.stop = function(finish) {
3495                 if (finish) {
3496                     this.currentFrame = this.totalFrames;
3497                     this._onTween.fire();
3498                 }
3499                 Roo.lib.AnimMgr.stop(this);
3500             };
3501
3502             var onStart = function() {
3503                 this.onStart.fire();
3504
3505                 this.runtimeAttributes = {};
3506                 for (var attr in this.attributes) {
3507                     this.setRuntimeAttribute(attr);
3508                 }
3509
3510                 isAnimated = true;
3511                 actualFrames = 0;
3512                 startTime = new Date();
3513             };
3514
3515
3516             var onTween = function() {
3517                 var data = {
3518                     duration: new Date() - this.getStartTime(),
3519                     currentFrame: this.currentFrame
3520                 };
3521
3522                 data.toString = function() {
3523                     return (
3524                             'duration: ' + data.duration +
3525                             ', currentFrame: ' + data.currentFrame
3526                             );
3527                 };
3528
3529                 this.onTween.fire(data);
3530
3531                 var runtimeAttributes = this.runtimeAttributes;
3532
3533                 for (var attr in runtimeAttributes) {
3534                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3535                 }
3536
3537                 actualFrames += 1;
3538             };
3539
3540             var onComplete = function() {
3541                 var actual_duration = (new Date() - startTime) / 1000 ;
3542
3543                 var data = {
3544                     duration: actual_duration,
3545                     frames: actualFrames,
3546                     fps: actualFrames / actual_duration
3547                 };
3548
3549                 data.toString = function() {
3550                     return (
3551                             'duration: ' + data.duration +
3552                             ', frames: ' + data.frames +
3553                             ', fps: ' + data.fps
3554                             );
3555                 };
3556
3557                 isAnimated = false;
3558                 actualFrames = 0;
3559                 this.onComplete.fire(data);
3560             };
3561
3562
3563             this._onStart = new Roo.util.Event(this);
3564             this.onStart = new Roo.util.Event(this);
3565             this.onTween = new Roo.util.Event(this);
3566             this._onTween = new Roo.util.Event(this);
3567             this.onComplete = new Roo.util.Event(this);
3568             this._onComplete = new Roo.util.Event(this);
3569             this._onStart.addListener(onStart);
3570             this._onTween.addListener(onTween);
3571             this._onComplete.addListener(onComplete);
3572         }
3573     };
3574 })();
3575 /*
3576  * Portions of this file are based on pieces of Yahoo User Interface Library
3577  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3578  * YUI licensed under the BSD License:
3579  * http://developer.yahoo.net/yui/license.txt
3580  * <script type="text/javascript">
3581  *
3582  */
3583
3584 Roo.lib.AnimMgr = new function() {
3585
3586     var thread = null;
3587
3588
3589     var queue = [];
3590
3591
3592     var tweenCount = 0;
3593
3594
3595     this.fps = 1000;
3596
3597
3598     this.delay = 1;
3599
3600
3601     this.registerElement = function(tween) {
3602         queue[queue.length] = tween;
3603         tweenCount += 1;
3604         tween._onStart.fire();
3605         this.start();
3606     };
3607
3608
3609     this.unRegister = function(tween, index) {
3610         tween._onComplete.fire();
3611         index = index || getIndex(tween);
3612         if (index != -1) {
3613             queue.splice(index, 1);
3614         }
3615
3616         tweenCount -= 1;
3617         if (tweenCount <= 0) {
3618             this.stop();
3619         }
3620     };
3621
3622
3623     this.start = function() {
3624         if (thread === null) {
3625             thread = setInterval(this.run, this.delay);
3626         }
3627     };
3628
3629
3630     this.stop = function(tween) {
3631         if (!tween) {
3632             clearInterval(thread);
3633
3634             for (var i = 0, len = queue.length; i < len; ++i) {
3635                 if (queue[0].isAnimated()) {
3636                     this.unRegister(queue[0], 0);
3637                 }
3638             }
3639
3640             queue = [];
3641             thread = null;
3642             tweenCount = 0;
3643         }
3644         else {
3645             this.unRegister(tween);
3646         }
3647     };
3648
3649
3650     this.run = function() {
3651         for (var i = 0, len = queue.length; i < len; ++i) {
3652             var tween = queue[i];
3653             if (!tween || !tween.isAnimated()) {
3654                 continue;
3655             }
3656
3657             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3658             {
3659                 tween.currentFrame += 1;
3660
3661                 if (tween.useSeconds) {
3662                     correctFrame(tween);
3663                 }
3664                 tween._onTween.fire();
3665             }
3666             else {
3667                 Roo.lib.AnimMgr.stop(tween, i);
3668             }
3669         }
3670     };
3671
3672     var getIndex = function(anim) {
3673         for (var i = 0, len = queue.length; i < len; ++i) {
3674             if (queue[i] == anim) {
3675                 return i;
3676             }
3677         }
3678         return -1;
3679     };
3680
3681
3682     var correctFrame = function(tween) {
3683         var frames = tween.totalFrames;
3684         var frame = tween.currentFrame;
3685         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3686         var elapsed = (new Date() - tween.getStartTime());
3687         var tweak = 0;
3688
3689         if (elapsed < tween.duration * 1000) {
3690             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3691         } else {
3692             tweak = frames - (frame + 1);
3693         }
3694         if (tweak > 0 && isFinite(tweak)) {
3695             if (tween.currentFrame + tweak >= frames) {
3696                 tweak = frames - (frame + 1);
3697             }
3698
3699             tween.currentFrame += tweak;
3700         }
3701     };
3702 };
3703
3704     /*
3705  * Portions of this file are based on pieces of Yahoo User Interface Library
3706  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3707  * YUI licensed under the BSD License:
3708  * http://developer.yahoo.net/yui/license.txt
3709  * <script type="text/javascript">
3710  *
3711  */
3712 Roo.lib.Bezier = new function() {
3713
3714         this.getPosition = function(points, t) {
3715             var n = points.length;
3716             var tmp = [];
3717
3718             for (var i = 0; i < n; ++i) {
3719                 tmp[i] = [points[i][0], points[i][1]];
3720             }
3721
3722             for (var j = 1; j < n; ++j) {
3723                 for (i = 0; i < n - j; ++i) {
3724                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3725                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3726                 }
3727             }
3728
3729             return [ tmp[0][0], tmp[0][1] ];
3730
3731         };
3732     }; 
3733
3734 /**
3735  * @class Roo.lib.Color
3736  * @constructor
3737  * An abstract Color implementation. Concrete Color implementations should use
3738  * an instance of this function as their prototype, and implement the getRGB and
3739  * getHSL functions. getRGB should return an object representing the RGB
3740  * components of this Color, with the red, green, and blue components in the
3741  * range [0,255] and the alpha component in the range [0,100]. getHSL should
3742  * return an object representing the HSL components of this Color, with the hue
3743  * component in the range [0,360), the saturation and lightness components in
3744  * the range [0,100], and the alpha component in the range [0,1].
3745  *
3746  *
3747  * Color.js
3748  *
3749  * Functions for Color handling and processing.
3750  *
3751  * http://www.safalra.com/web-design/javascript/Color-handling-and-processing/
3752  *
3753  * The author of this program, Safalra (Stephen Morley), irrevocably releases all
3754  * rights to this program, with the intention of it becoming part of the public
3755  * domain. Because this program is released into the public domain, it comes with
3756  * no warranty either expressed or implied, to the extent permitted by law.
3757  * 
3758  * For more free and public domain JavaScript code by the same author, visit:
3759  * http://www.safalra.com/web-design/javascript/
3760  * 
3761  */
3762 Roo.lib.Color = function() { }
3763
3764
3765 Roo.apply(Roo.lib.Color.prototype, {
3766   
3767   rgb : null,
3768   hsv : null,
3769   hsl : null,
3770   
3771   /**
3772    * getIntegerRGB
3773    * @return {Object} an object representing the RGBA components of this Color. The red,
3774    * green, and blue components are converted to integers in the range [0,255].
3775    * The alpha is a value in the range [0,1].
3776    */
3777   getIntegerRGB : function(){
3778
3779     // get the RGB components of this Color
3780     var rgb = this.getRGB();
3781
3782     // return the integer components
3783     return {
3784       'r' : Math.round(rgb.r),
3785       'g' : Math.round(rgb.g),
3786       'b' : Math.round(rgb.b),
3787       'a' : rgb.a
3788     };
3789
3790   },
3791
3792   /**
3793    * getPercentageRGB
3794    * @return {Object} an object representing the RGBA components of this Color. The red,
3795    * green, and blue components are converted to numbers in the range [0,100].
3796    * The alpha is a value in the range [0,1].
3797    */
3798   getPercentageRGB : function(){
3799
3800     // get the RGB components of this Color
3801     var rgb = this.getRGB();
3802
3803     // return the percentage components
3804     return {
3805       'r' : 100 * rgb.r / 255,
3806       'g' : 100 * rgb.g / 255,
3807       'b' : 100 * rgb.b / 255,
3808       'a' : rgb.a
3809     };
3810
3811   },
3812
3813   /**
3814    * getCSSHexadecimalRGB
3815    * @return {String} a string representing this Color as a CSS hexadecimal RGB Color
3816    * value - that is, a string of the form #RRGGBB where each of RR, GG, and BB
3817    * are two-digit hexadecimal numbers.
3818    */
3819   getCSSHexadecimalRGB : function()
3820   {
3821
3822     // get the integer RGB components
3823     var rgb = this.getIntegerRGB();
3824
3825     // determine the hexadecimal equivalents
3826     var r16 = rgb.r.toString(16);
3827     var g16 = rgb.g.toString(16);
3828     var b16 = rgb.b.toString(16);
3829
3830     // return the CSS RGB Color value
3831     return '#'
3832         + (r16.length == 2 ? r16 : '0' + r16)
3833         + (g16.length == 2 ? g16 : '0' + g16)
3834         + (b16.length == 2 ? b16 : '0' + b16);
3835
3836   },
3837
3838   /**
3839    * getCSSIntegerRGB
3840    * @return {String} a string representing this Color as a CSS integer RGB Color
3841    * value - that is, a string of the form rgb(r,g,b) where each of r, g, and b
3842    * are integers in the range [0,255].
3843    */
3844   getCSSIntegerRGB : function(){
3845
3846     // get the integer RGB components
3847     var rgb = this.getIntegerRGB();
3848
3849     // return the CSS RGB Color value
3850     return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ')';
3851
3852   },
3853
3854   /**
3855    * getCSSIntegerRGBA
3856    * @return {String} Returns a string representing this Color as a CSS integer RGBA Color
3857    * value - that is, a string of the form rgba(r,g,b,a) where each of r, g, and
3858    * b are integers in the range [0,255] and a is in the range [0,1].
3859    */
3860   getCSSIntegerRGBA : function(){
3861
3862     // get the integer RGB components
3863     var rgb = this.getIntegerRGB();
3864
3865     // return the CSS integer RGBA Color value
3866     return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + rgb.a + ')';
3867
3868   },
3869
3870   /**
3871    * getCSSPercentageRGB
3872    * @return {String} a string representing this Color as a CSS percentage RGB Color
3873    * value - that is, a string of the form rgb(r%,g%,b%) where each of r, g, and
3874    * b are in the range [0,100].
3875    */
3876   getCSSPercentageRGB : function(){
3877
3878     // get the percentage RGB components
3879     var rgb = this.getPercentageRGB();
3880
3881     // return the CSS RGB Color value
3882     return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%)';
3883
3884   },
3885
3886   /**
3887    * getCSSPercentageRGBA
3888    * @return {String} a string representing this Color as a CSS percentage RGBA Color
3889    * value - that is, a string of the form rgba(r%,g%,b%,a) where each of r, g,
3890    * and b are in the range [0,100] and a is in the range [0,1].
3891    */
3892   getCSSPercentageRGBA : function(){
3893
3894     // get the percentage RGB components
3895     var rgb = this.getPercentageRGB();
3896
3897     // return the CSS percentage RGBA Color value
3898     return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%,' + rgb.a + ')';
3899
3900   },
3901
3902   /**
3903    * getCSSHSL
3904    * @return {String} a string representing this Color as a CSS HSL Color value - that
3905    * is, a string of the form hsl(h,s%,l%) where h is in the range [0,100] and
3906    * s and l are in the range [0,100].
3907    */
3908   getCSSHSL : function(){
3909
3910     // get the HSL components
3911     var hsl = this.getHSL();
3912
3913     // return the CSS HSL Color value
3914     return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%)';
3915
3916   },
3917
3918   /**
3919    * getCSSHSLA
3920    * @return {String} a string representing this Color as a CSS HSLA Color value - that
3921    * is, a string of the form hsla(h,s%,l%,a) where h is in the range [0,100],
3922    * s and l are in the range [0,100], and a is in the range [0,1].
3923    */
3924   getCSSHSLA : function(){
3925
3926     // get the HSL components
3927     var hsl = this.getHSL();
3928
3929     // return the CSS HSL Color value
3930     return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%,' + hsl.a + ')';
3931
3932   },
3933
3934   /**
3935    * Sets the Color of the specified node to this Color. This functions sets
3936    * the CSS 'color' property for the node. The parameter is:
3937    * 
3938    * @param {DomElement} node - the node whose Color should be set
3939    */
3940   setNodeColor : function(node){
3941
3942     // set the Color of the node
3943     node.style.color = this.getCSSHexadecimalRGB();
3944
3945   },
3946
3947   /**
3948    * Sets the background Color of the specified node to this Color. This
3949    * functions sets the CSS 'background-color' property for the node. The
3950    * parameter is:
3951    *
3952    * @param {DomElement} node - the node whose background Color should be set
3953    */
3954   setNodeBackgroundColor : function(node){
3955
3956     // set the background Color of the node
3957     node.style.backgroundColor = this.getCSSHexadecimalRGB();
3958
3959   },
3960   // convert between formats..
3961   toRGB: function()
3962   {
3963     var r = this.getIntegerRGB();
3964     return new Roo.lib.RGBColor(r.r,r.g,r.b,r.a);
3965     
3966   },
3967   toHSL : function()
3968   {
3969      var hsl = this.getHSL();
3970   // return the CSS HSL Color value
3971     return new Roo.lib.HSLColor(hsl.h,  hsl.s, hsl.l ,  hsl.a );
3972     
3973   },
3974   
3975   toHSV : function()
3976   {
3977     var rgb = this.toRGB();
3978     var hsv = rgb.getHSV();
3979    // return the CSS HSL Color value
3980     return new Roo.lib.HSVColor(hsv.h,  hsv.s, hsv.v ,  hsv.a );
3981     
3982   },
3983   
3984   // modify  v = 0 ... 1 (eg. 0.5)
3985   saturate : function(v)
3986   {
3987       var rgb = this.toRGB();
3988       var hsv = rgb.getHSV();
3989       return new Roo.lib.HSVColor(hsv.h,  hsv.s * v, hsv.v ,  hsv.a );
3990       
3991   
3992   },
3993   
3994    
3995   /**
3996    * getRGB
3997    * @return {Object} the RGB and alpha components of this Color as an object with r,
3998    * g, b, and a properties. r, g, and b are in the range [0,255] and a is in
3999    * the range [0,1].
4000    */
4001   getRGB: function(){
4002    
4003     // return the RGB components
4004     return {
4005       'r' : this.rgb.r,
4006       'g' : this.rgb.g,
4007       'b' : this.rgb.b,
4008       'a' : this.alpha
4009     };
4010
4011   },
4012
4013   /**
4014    * getHSV
4015    * @return {Object} the HSV and alpha components of this Color as an object with h,
4016    * s, v, and a properties. h is in the range [0,360), s and v are in the range
4017    * [0,100], and a is in the range [0,1].
4018    */
4019   getHSV : function()
4020   {
4021     
4022     // calculate the HSV components if necessary
4023     if (this.hsv == null) {
4024       this.calculateHSV();
4025     }
4026
4027     // return the HSV components
4028     return {
4029       'h' : this.hsv.h,
4030       's' : this.hsv.s,
4031       'v' : this.hsv.v,
4032       'a' : this.alpha
4033     };
4034
4035   },
4036
4037   /**
4038    * getHSL
4039    * @return {Object} the HSL and alpha components of this Color as an object with h,
4040    * s, l, and a properties. h is in the range [0,360), s and l are in the range
4041    * [0,100], and a is in the range [0,1].
4042    */
4043   getHSL : function(){
4044     
4045      
4046     // calculate the HSV components if necessary
4047     if (this.hsl == null) { this.calculateHSL(); }
4048
4049     // return the HSL components
4050     return {
4051       'h' : this.hsl.h,
4052       's' : this.hsl.s,
4053       'l' : this.hsl.l,
4054       'a' : this.alpha
4055     };
4056
4057   }
4058   
4059
4060 });
4061
4062
4063 /**
4064  * @class Roo.lib.RGBColor
4065  * @extends Roo.lib.Color
4066  * Creates a Color specified in the RGB Color space, with an optional alpha
4067  * component. The parameters are:
4068  * @constructor
4069  * 
4070
4071  * @param {Number} r - the red component, clipped to the range [0,255]
4072  * @param {Number} g - the green component, clipped to the range [0,255]
4073  * @param {Number} b - the blue component, clipped to the range [0,255]
4074  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4075  *     optional and defaults to 1
4076  */
4077 Roo.lib.RGBColor = function (r, g, b, a){
4078
4079   // store the alpha component after clipping it if necessary
4080   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4081
4082   // store the RGB components after clipping them if necessary
4083   this.rgb =
4084       {
4085         'r' : Math.max(0, Math.min(255, r)),
4086         'g' : Math.max(0, Math.min(255, g)),
4087         'b' : Math.max(0, Math.min(255, b))
4088       };
4089
4090   // initialise the HSV and HSL components to null
4091   
4092
4093   /* 
4094    * //private returns the HSV or HSL hue component of this RGBColor. The hue is in the
4095    * range [0,360). The parameters are:
4096    *
4097    * maximum - the maximum of the RGB component values
4098    * range   - the range of the RGB component values
4099    */
4100    
4101
4102 }
4103 // this does an 'exteds'
4104 Roo.extend(Roo.lib.RGBColor, Roo.lib.Color, {
4105
4106   
4107     getHue  : function(maximum, range)
4108     {
4109       var rgb = this.rgb;
4110        
4111       // check whether the range is zero
4112       if (range == 0){
4113   
4114         // set the hue to zero (any hue is acceptable as the Color is grey)
4115         var hue = 0;
4116   
4117       }else{
4118   
4119         // determine which of the components has the highest value and set the hue
4120         switch (maximum){
4121   
4122           // red has the highest value
4123           case rgb.r:
4124             var hue = (rgb.g - rgb.b) / range * 60;
4125             if (hue < 0) { hue += 360; }
4126             break;
4127   
4128           // green has the highest value
4129           case rgb.g:
4130             var hue = (rgb.b - rgb.r) / range * 60 + 120;
4131             break;
4132   
4133           // blue has the highest value
4134           case rgb.b:
4135             var hue = (rgb.r - rgb.g) / range * 60 + 240;
4136             break;
4137   
4138         }
4139   
4140       }
4141   
4142       // return the hue
4143       return hue;
4144   
4145     },
4146
4147   /* //private Calculates and stores the HSV components of this RGBColor so that they can
4148    * be returned be the getHSV function.
4149    */
4150    calculateHSV : function(){
4151     var rgb = this.rgb;
4152     // get the maximum and range of the RGB component values
4153     var maximum = Math.max(rgb.r, rgb.g, rgb.b);
4154     var range   = maximum - Math.min(rgb.r, rgb.g, rgb.b);
4155
4156     // store the HSV components
4157     this.hsv =
4158         {
4159           'h' : this.getHue(maximum, range),
4160           's' : (maximum == 0 ? 0 : 100 * range / maximum),
4161           'v' : maximum / 2.55
4162         };
4163
4164   },
4165
4166   /* //private Calculates and stores the HSL components of this RGBColor so that they can
4167    * be returned be the getHSL function.
4168    */
4169    calculateHSL : function(){
4170     var rgb = this.rgb;
4171     // get the maximum and range of the RGB component values
4172     var maximum = Math.max(rgb.r, rgb.g, rgb.b);
4173     var range   = maximum - Math.min(rgb.r, rgb.g, rgb.b);
4174
4175     // determine the lightness in the range [0,1]
4176     var l = maximum / 255 - range / 510;
4177
4178     // store the HSL components
4179     this.hsl =
4180         {
4181           'h' : this.getHue(maximum, range),
4182           's' : (range == 0 ? 0 : range / 2.55 / (l < 0.5 ? l * 2 : 2 - l * 2)),
4183           'l' : 100 * l
4184         };
4185
4186   }
4187
4188 });
4189
4190 /**
4191  * @class Roo.lib.HSVColor
4192  * @extends Roo.lib.Color
4193  * Creates a Color specified in the HSV Color space, with an optional alpha
4194  * component. The parameters are:
4195  * @constructor
4196  *
4197  * @param {Number} h - the hue component, wrapped to the range [0,360)
4198  * @param {Number} s - the saturation component, clipped to the range [0,100]
4199  * @param {Number} v - the value component, clipped to the range [0,100]
4200  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4201  *     optional and defaults to 1
4202  */
4203 Roo.lib.HSVColor = function (h, s, v, a){
4204
4205   // store the alpha component after clipping it if necessary
4206   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4207
4208   // store the HSV components after clipping or wrapping them if necessary
4209   this.hsv =
4210       {
4211         'h' : (h % 360 + 360) % 360,
4212         's' : Math.max(0, Math.min(100, s)),
4213         'v' : Math.max(0, Math.min(100, v))
4214       };
4215
4216   // initialise the RGB and HSL components to null
4217   this.rgb = null;
4218   this.hsl = null;
4219 }
4220
4221 Roo.extend(Roo.lib.HSVColor, Roo.lib.Color, {
4222   /* Calculates and stores the RGB components of this HSVColor so that they can
4223    * be returned be the getRGB function.
4224    */
4225   calculateRGB: function ()
4226   {
4227     var hsv = this.hsv;
4228     // check whether the saturation is zero
4229     if (hsv.s == 0){
4230
4231       // set the Color to the appropriate shade of grey
4232       var r = hsv.v;
4233       var g = hsv.v;
4234       var b = hsv.v;
4235
4236     }else{
4237
4238       // set some temporary values
4239       var f  = hsv.h / 60 - Math.floor(hsv.h / 60);
4240       var p  = hsv.v * (1 - hsv.s / 100);
4241       var q  = hsv.v * (1 - hsv.s / 100 * f);
4242       var t  = hsv.v * (1 - hsv.s / 100 * (1 - f));
4243
4244       // set the RGB Color components to their temporary values
4245       switch (Math.floor(hsv.h / 60)){
4246         case 0: var r = hsv.v; var g = t; var b = p; break;
4247         case 1: var r = q; var g = hsv.v; var b = p; break;
4248         case 2: var r = p; var g = hsv.v; var b = t; break;
4249         case 3: var r = p; var g = q; var b = hsv.v; break;
4250         case 4: var r = t; var g = p; var b = hsv.v; break;
4251         case 5: var r = hsv.v; var g = p; var b = q; break;
4252       }
4253
4254     }
4255
4256     // store the RGB components
4257     this.rgb =
4258         {
4259           'r' : r * 2.55,
4260           'g' : g * 2.55,
4261           'b' : b * 2.55
4262         };
4263
4264   },
4265
4266   /* Calculates and stores the HSL components of this HSVColor so that they can
4267    * be returned be the getHSL function.
4268    */
4269   calculateHSL : function (){
4270
4271     var hsv = this.hsv;
4272     // determine the lightness in the range [0,100]
4273     var l = (2 - hsv.s / 100) * hsv.v / 2;
4274
4275     // store the HSL components
4276     this.hsl =
4277         {
4278           'h' : hsv.h,
4279           's' : hsv.s * hsv.v / (l < 50 ? l * 2 : 200 - l * 2),
4280           'l' : l
4281         };
4282
4283     // correct a division-by-zero error
4284     if (isNaN(hsl.s)) { hsl.s = 0; }
4285
4286   } 
4287  
4288
4289 });
4290  
4291
4292 /**
4293  * @class Roo.lib.HSLColor
4294  * @extends Roo.lib.Color
4295  *
4296  * @constructor
4297  * Creates a Color specified in the HSL Color space, with an optional alpha
4298  * component. The parameters are:
4299  *
4300  * @param {Number} h - the hue component, wrapped to the range [0,360)
4301  * @param {Number} s - the saturation component, clipped to the range [0,100]
4302  * @param {Number} l - the lightness component, clipped to the range [0,100]
4303  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4304  *     optional and defaults to 1
4305  */
4306
4307 Roo.lib.HSLColor = function(h, s, l, a){
4308
4309   // store the alpha component after clipping it if necessary
4310   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4311
4312   // store the HSL components after clipping or wrapping them if necessary
4313   this.hsl =
4314       {
4315         'h' : (h % 360 + 360) % 360,
4316         's' : Math.max(0, Math.min(100, s)),
4317         'l' : Math.max(0, Math.min(100, l))
4318       };
4319
4320   // initialise the RGB and HSV components to null
4321 }
4322
4323 Roo.extend(Roo.lib.HSLColor, Roo.lib.Color, {
4324
4325   /* Calculates and stores the RGB components of this HSLColor so that they can
4326    * be returned be the getRGB function.
4327    */
4328   calculateRGB: function (){
4329
4330     // check whether the saturation is zero
4331     if (this.hsl.s == 0){
4332
4333       // store the RGB components representing the appropriate shade of grey
4334       this.rgb =
4335           {
4336             'r' : this.hsl.l * 2.55,
4337             'g' : this.hsl.l * 2.55,
4338             'b' : this.hsl.l * 2.55
4339           };
4340
4341     }else{
4342
4343       // set some temporary values
4344       var p = this.hsl.l < 50
4345             ? this.hsl.l * (1 + hsl.s / 100)
4346             : this.hsl.l + hsl.s - hsl.l * hsl.s / 100;
4347       var q = 2 * hsl.l - p;
4348
4349       // initialise the RGB components
4350       this.rgb =
4351           {
4352             'r' : (h + 120) / 60 % 6,
4353             'g' : h / 60,
4354             'b' : (h + 240) / 60 % 6
4355           };
4356
4357       // loop over the RGB components
4358       for (var key in this.rgb){
4359
4360         // ensure that the property is not inherited from the root object
4361         if (this.rgb.hasOwnProperty(key)){
4362
4363           // set the component to its value in the range [0,100]
4364           if (this.rgb[key] < 1){
4365             this.rgb[key] = q + (p - q) * this.rgb[key];
4366           }else if (this.rgb[key] < 3){
4367             this.rgb[key] = p;
4368           }else if (this.rgb[key] < 4){
4369             this.rgb[key] = q + (p - q) * (4 - this.rgb[key]);
4370           }else{
4371             this.rgb[key] = q;
4372           }
4373
4374           // set the component to its value in the range [0,255]
4375           this.rgb[key] *= 2.55;
4376
4377         }
4378
4379       }
4380
4381     }
4382
4383   },
4384
4385   /* Calculates and stores the HSV components of this HSLColor so that they can
4386    * be returned be the getHSL function.
4387    */
4388    calculateHSV : function(){
4389
4390     // set a temporary value
4391     var t = this.hsl.s * (this.hsl.l < 50 ? this.hsl.l : 100 - this.hsl.l) / 100;
4392
4393     // store the HSV components
4394     this.hsv =
4395         {
4396           'h' : this.hsl.h,
4397           's' : 200 * t / (this.hsl.l + t),
4398           'v' : t + this.hsl.l
4399         };
4400
4401     // correct a division-by-zero error
4402     if (isNaN(this.hsv.s)) { this.hsv.s = 0; }
4403
4404   }
4405  
4406
4407 });
4408 /*
4409  * Portions of this file are based on pieces of Yahoo User Interface Library
4410  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4411  * YUI licensed under the BSD License:
4412  * http://developer.yahoo.net/yui/license.txt
4413  * <script type="text/javascript">
4414  *
4415  */
4416 (function() {
4417
4418     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
4419         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
4420     };
4421
4422     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
4423
4424     var fly = Roo.lib.AnimBase.fly;
4425     var Y = Roo.lib;
4426     var superclass = Y.ColorAnim.superclass;
4427     var proto = Y.ColorAnim.prototype;
4428
4429     proto.toString = function() {
4430         var el = this.getEl();
4431         var id = el.id || el.tagName;
4432         return ("ColorAnim " + id);
4433     };
4434
4435     proto.patterns.color = /color$/i;
4436     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
4437     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
4438     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
4439     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
4440
4441
4442     proto.parseColor = function(s) {
4443         if (s.length == 3) {
4444             return s;
4445         }
4446
4447         var c = this.patterns.hex.exec(s);
4448         if (c && c.length == 4) {
4449             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
4450         }
4451
4452         c = this.patterns.rgb.exec(s);
4453         if (c && c.length == 4) {
4454             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
4455         }
4456
4457         c = this.patterns.hex3.exec(s);
4458         if (c && c.length == 4) {
4459             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
4460         }
4461
4462         return null;
4463     };
4464     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
4465     proto.getAttribute = function(attr) {
4466         var el = this.getEl();
4467         if (this.patterns.color.test(attr)) {
4468             var val = fly(el).getStyle(attr);
4469
4470             if (this.patterns.transparent.test(val)) {
4471                 var parent = el.parentNode;
4472                 val = fly(parent).getStyle(attr);
4473
4474                 while (parent && this.patterns.transparent.test(val)) {
4475                     parent = parent.parentNode;
4476                     val = fly(parent).getStyle(attr);
4477                     if (parent.tagName.toUpperCase() == 'HTML') {
4478                         val = '#fff';
4479                     }
4480                 }
4481             }
4482         } else {
4483             val = superclass.getAttribute.call(this, attr);
4484         }
4485
4486         return val;
4487     };
4488     proto.getAttribute = function(attr) {
4489         var el = this.getEl();
4490         if (this.patterns.color.test(attr)) {
4491             var val = fly(el).getStyle(attr);
4492
4493             if (this.patterns.transparent.test(val)) {
4494                 var parent = el.parentNode;
4495                 val = fly(parent).getStyle(attr);
4496
4497                 while (parent && this.patterns.transparent.test(val)) {
4498                     parent = parent.parentNode;
4499                     val = fly(parent).getStyle(attr);
4500                     if (parent.tagName.toUpperCase() == 'HTML') {
4501                         val = '#fff';
4502                     }
4503                 }
4504             }
4505         } else {
4506             val = superclass.getAttribute.call(this, attr);
4507         }
4508
4509         return val;
4510     };
4511
4512     proto.doMethod = function(attr, start, end) {
4513         var val;
4514
4515         if (this.patterns.color.test(attr)) {
4516             val = [];
4517             for (var i = 0, len = start.length; i < len; ++i) {
4518                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
4519             }
4520
4521             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
4522         }
4523         else {
4524             val = superclass.doMethod.call(this, attr, start, end);
4525         }
4526
4527         return val;
4528     };
4529
4530     proto.setRuntimeAttribute = function(attr) {
4531         superclass.setRuntimeAttribute.call(this, attr);
4532
4533         if (this.patterns.color.test(attr)) {
4534             var attributes = this.attributes;
4535             var start = this.parseColor(this.runtimeAttributes[attr].start);
4536             var end = this.parseColor(this.runtimeAttributes[attr].end);
4537
4538             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
4539                 end = this.parseColor(attributes[attr].by);
4540
4541                 for (var i = 0, len = start.length; i < len; ++i) {
4542                     end[i] = start[i] + end[i];
4543                 }
4544             }
4545
4546             this.runtimeAttributes[attr].start = start;
4547             this.runtimeAttributes[attr].end = end;
4548         }
4549     };
4550 })();
4551
4552 /*
4553  * Portions of this file are based on pieces of Yahoo User Interface Library
4554  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4555  * YUI licensed under the BSD License:
4556  * http://developer.yahoo.net/yui/license.txt
4557  * <script type="text/javascript">
4558  *
4559  */
4560 Roo.lib.Easing = {
4561
4562
4563     easeNone: function (t, b, c, d) {
4564         return c * t / d + b;
4565     },
4566
4567
4568     easeIn: function (t, b, c, d) {
4569         return c * (t /= d) * t + b;
4570     },
4571
4572
4573     easeOut: function (t, b, c, d) {
4574         return -c * (t /= d) * (t - 2) + b;
4575     },
4576
4577
4578     easeBoth: function (t, b, c, d) {
4579         if ((t /= d / 2) < 1) {
4580             return c / 2 * t * t + b;
4581         }
4582
4583         return -c / 2 * ((--t) * (t - 2) - 1) + b;
4584     },
4585
4586
4587     easeInStrong: function (t, b, c, d) {
4588         return c * (t /= d) * t * t * t + b;
4589     },
4590
4591
4592     easeOutStrong: function (t, b, c, d) {
4593         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
4594     },
4595
4596
4597     easeBothStrong: function (t, b, c, d) {
4598         if ((t /= d / 2) < 1) {
4599             return c / 2 * t * t * t * t + b;
4600         }
4601
4602         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
4603     },
4604
4605
4606
4607     elasticIn: function (t, b, c, d, a, p) {
4608         if (t == 0) {
4609             return b;
4610         }
4611         if ((t /= d) == 1) {
4612             return b + c;
4613         }
4614         if (!p) {
4615             p = d * .3;
4616         }
4617
4618         if (!a || a < Math.abs(c)) {
4619             a = c;
4620             var s = p / 4;
4621         }
4622         else {
4623             var s = p / (2 * Math.PI) * Math.asin(c / a);
4624         }
4625
4626         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4627     },
4628
4629
4630     elasticOut: function (t, b, c, d, a, p) {
4631         if (t == 0) {
4632             return b;
4633         }
4634         if ((t /= d) == 1) {
4635             return b + c;
4636         }
4637         if (!p) {
4638             p = d * .3;
4639         }
4640
4641         if (!a || a < Math.abs(c)) {
4642             a = c;
4643             var s = p / 4;
4644         }
4645         else {
4646             var s = p / (2 * Math.PI) * Math.asin(c / a);
4647         }
4648
4649         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
4650     },
4651
4652
4653     elasticBoth: function (t, b, c, d, a, p) {
4654         if (t == 0) {
4655             return b;
4656         }
4657
4658         if ((t /= d / 2) == 2) {
4659             return b + c;
4660         }
4661
4662         if (!p) {
4663             p = d * (.3 * 1.5);
4664         }
4665
4666         if (!a || a < Math.abs(c)) {
4667             a = c;
4668             var s = p / 4;
4669         }
4670         else {
4671             var s = p / (2 * Math.PI) * Math.asin(c / a);
4672         }
4673
4674         if (t < 1) {
4675             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
4676                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4677         }
4678         return a * Math.pow(2, -10 * (t -= 1)) *
4679                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
4680     },
4681
4682
4683
4684     backIn: function (t, b, c, d, s) {
4685         if (typeof s == 'undefined') {
4686             s = 1.70158;
4687         }
4688         return c * (t /= d) * t * ((s + 1) * t - s) + b;
4689     },
4690
4691
4692     backOut: function (t, b, c, d, s) {
4693         if (typeof s == 'undefined') {
4694             s = 1.70158;
4695         }
4696         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
4697     },
4698
4699
4700     backBoth: function (t, b, c, d, s) {
4701         if (typeof s == 'undefined') {
4702             s = 1.70158;
4703         }
4704
4705         if ((t /= d / 2 ) < 1) {
4706             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
4707         }
4708         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
4709     },
4710
4711
4712     bounceIn: function (t, b, c, d) {
4713         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
4714     },
4715
4716
4717     bounceOut: function (t, b, c, d) {
4718         if ((t /= d) < (1 / 2.75)) {
4719             return c * (7.5625 * t * t) + b;
4720         } else if (t < (2 / 2.75)) {
4721             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
4722         } else if (t < (2.5 / 2.75)) {
4723             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
4724         }
4725         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
4726     },
4727
4728
4729     bounceBoth: function (t, b, c, d) {
4730         if (t < d / 2) {
4731             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
4732         }
4733         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
4734     }
4735 };/*
4736  * Portions of this file are based on pieces of Yahoo User Interface Library
4737  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4738  * YUI licensed under the BSD License:
4739  * http://developer.yahoo.net/yui/license.txt
4740  * <script type="text/javascript">
4741  *
4742  */
4743     (function() {
4744         Roo.lib.Motion = function(el, attributes, duration, method) {
4745             if (el) {
4746                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
4747             }
4748         };
4749
4750         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
4751
4752
4753         var Y = Roo.lib;
4754         var superclass = Y.Motion.superclass;
4755         var proto = Y.Motion.prototype;
4756
4757         proto.toString = function() {
4758             var el = this.getEl();
4759             var id = el.id || el.tagName;
4760             return ("Motion " + id);
4761         };
4762
4763         proto.patterns.points = /^points$/i;
4764
4765         proto.setAttribute = function(attr, val, unit) {
4766             if (this.patterns.points.test(attr)) {
4767                 unit = unit || 'px';
4768                 superclass.setAttribute.call(this, 'left', val[0], unit);
4769                 superclass.setAttribute.call(this, 'top', val[1], unit);
4770             } else {
4771                 superclass.setAttribute.call(this, attr, val, unit);
4772             }
4773         };
4774
4775         proto.getAttribute = function(attr) {
4776             if (this.patterns.points.test(attr)) {
4777                 var val = [
4778                         superclass.getAttribute.call(this, 'left'),
4779                         superclass.getAttribute.call(this, 'top')
4780                         ];
4781             } else {
4782                 val = superclass.getAttribute.call(this, attr);
4783             }
4784
4785             return val;
4786         };
4787
4788         proto.doMethod = function(attr, start, end) {
4789             var val = null;
4790
4791             if (this.patterns.points.test(attr)) {
4792                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
4793                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
4794             } else {
4795                 val = superclass.doMethod.call(this, attr, start, end);
4796             }
4797             return val;
4798         };
4799
4800         proto.setRuntimeAttribute = function(attr) {
4801             if (this.patterns.points.test(attr)) {
4802                 var el = this.getEl();
4803                 var attributes = this.attributes;
4804                 var start;
4805                 var control = attributes['points']['control'] || [];
4806                 var end;
4807                 var i, len;
4808
4809                 if (control.length > 0 && !(control[0] instanceof Array)) {
4810                     control = [control];
4811                 } else {
4812                     var tmp = [];
4813                     for (i = 0,len = control.length; i < len; ++i) {
4814                         tmp[i] = control[i];
4815                     }
4816                     control = tmp;
4817                 }
4818
4819                 Roo.fly(el).position();
4820
4821                 if (isset(attributes['points']['from'])) {
4822                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
4823                 }
4824                 else {
4825                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4826                 }
4827
4828                 start = this.getAttribute('points');
4829
4830
4831                 if (isset(attributes['points']['to'])) {
4832                     end = translateValues.call(this, attributes['points']['to'], start);
4833
4834                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4835                     for (i = 0,len = control.length; i < len; ++i) {
4836                         control[i] = translateValues.call(this, control[i], start);
4837                     }
4838
4839
4840                 } else if (isset(attributes['points']['by'])) {
4841                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4842
4843                     for (i = 0,len = control.length; i < len; ++i) {
4844                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4845                     }
4846                 }
4847
4848                 this.runtimeAttributes[attr] = [start];
4849
4850                 if (control.length > 0) {
4851                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4852                 }
4853
4854                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4855             }
4856             else {
4857                 superclass.setRuntimeAttribute.call(this, attr);
4858             }
4859         };
4860
4861         var translateValues = function(val, start) {
4862             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4863             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4864
4865             return val;
4866         };
4867
4868         var isset = function(prop) {
4869             return (typeof prop !== 'undefined');
4870         };
4871     })();
4872 /*
4873  * Portions of this file are based on pieces of Yahoo User Interface Library
4874  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4875  * YUI licensed under the BSD License:
4876  * http://developer.yahoo.net/yui/license.txt
4877  * <script type="text/javascript">
4878  *
4879  */
4880     (function() {
4881         Roo.lib.Scroll = function(el, attributes, duration, method) {
4882             if (el) {
4883                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4884             }
4885         };
4886
4887         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4888
4889
4890         var Y = Roo.lib;
4891         var superclass = Y.Scroll.superclass;
4892         var proto = Y.Scroll.prototype;
4893
4894         proto.toString = function() {
4895             var el = this.getEl();
4896             var id = el.id || el.tagName;
4897             return ("Scroll " + id);
4898         };
4899
4900         proto.doMethod = function(attr, start, end) {
4901             var val = null;
4902
4903             if (attr == 'scroll') {
4904                 val = [
4905                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4906                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4907                         ];
4908
4909             } else {
4910                 val = superclass.doMethod.call(this, attr, start, end);
4911             }
4912             return val;
4913         };
4914
4915         proto.getAttribute = function(attr) {
4916             var val = null;
4917             var el = this.getEl();
4918
4919             if (attr == 'scroll') {
4920                 val = [ el.scrollLeft, el.scrollTop ];
4921             } else {
4922                 val = superclass.getAttribute.call(this, attr);
4923             }
4924
4925             return val;
4926         };
4927
4928         proto.setAttribute = function(attr, val, unit) {
4929             var el = this.getEl();
4930
4931             if (attr == 'scroll') {
4932                 el.scrollLeft = val[0];
4933                 el.scrollTop = val[1];
4934             } else {
4935                 superclass.setAttribute.call(this, attr, val, unit);
4936             }
4937         };
4938     })();
4939 /**
4940  * Originally based of this code... - refactored for Roo...
4941  * https://github.com/aaalsaleh/undo-manager
4942  
4943  * undo-manager.js
4944  * @author  Abdulrahman Alsaleh 
4945  * @copyright 2015 Abdulrahman Alsaleh 
4946  * @license  MIT License (c) 
4947  *
4948  * Hackily modifyed by alan@roojs.com
4949  *
4950  *
4951  *  
4952  *
4953  *  TOTALLY UNTESTED...
4954  *
4955  *  Documentation to be done....
4956  */
4957  
4958
4959 /**
4960 * @class Roo.lib.UndoManager
4961 * An undo manager implementation in JavaScript. It follows the W3C UndoManager and DOM Transaction
4962 * Draft and the undocumented and disabled Mozilla Firefox's UndoManager implementation.
4963
4964  * Usage:
4965  * <pre><code>
4966
4967
4968 editor.undoManager = new Roo.lib.UndoManager(1000, editor);
4969  
4970 </code></pre>
4971
4972 * For more information see this blog post with examples:
4973 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4974      - Create Elements using DOM, HTML fragments and Templates</a>. 
4975 * @constructor
4976 * @param {Number} limit how far back to go ... use 1000?
4977 * @param {Object} scope usually use document..
4978 */
4979
4980 Roo.lib.UndoManager = function (limit, undoScopeHost)
4981 {
4982     this.stack = [];
4983     this.limit = limit;
4984     this.scope = undoScopeHost;
4985     this.fireEvent = typeof CustomEvent != 'undefined' && undoScopeHost && undoScopeHost.dispatchEvent;
4986     if (this.fireEvent) {
4987         this.bindEvents();
4988     }
4989     this.reset();
4990     
4991 };
4992         
4993 Roo.lib.UndoManager.prototype = {
4994     
4995     limit : false,
4996     stack : false,
4997     scope :  false,
4998     fireEvent : false,
4999     position : 0,
5000     length : 0,
5001     
5002     
5003      /**
5004      * To push and execute a transaction, the method undoManager.transact
5005      * must be called by passing a transaction object as the first argument, and a merge
5006      * flag as the second argument. A transaction object has the following properties:
5007      *
5008      * Usage:
5009 <pre><code>
5010 undoManager.transact({
5011     label: 'Typing',
5012     execute: function() { ... },
5013     undo: function() { ... },
5014     // redo same as execute
5015     redo: function() { this.execute(); }
5016 }, false);
5017
5018 // merge transaction
5019 undoManager.transact({
5020     label: 'Typing',
5021     execute: function() { ... },  // this will be run...
5022     undo: function() { ... }, // what to do when undo is run.
5023     // redo same as execute
5024     redo: function() { this.execute(); }
5025 }, true); 
5026 </code></pre> 
5027      *
5028      * 
5029      * @param {Object} transaction The transaction to add to the stack.
5030      * @return {String} The HTML fragment
5031      */
5032     
5033     
5034     transact : function (transaction, merge)
5035     {
5036         if (arguments.length < 2) {
5037             throw new TypeError('Not enough arguments to UndoManager.transact.');
5038         }
5039
5040         transaction.execute();
5041
5042         this.stack.splice(0, this.position);
5043         if (merge && this.length) {
5044             this.stack[0].push(transaction);
5045         } else {
5046             this.stack.unshift([transaction]);
5047         }
5048     
5049         this.position = 0;
5050
5051         if (this.limit && this.stack.length > this.limit) {
5052             this.length = this.stack.length = this.limit;
5053         } else {
5054             this.length = this.stack.length;
5055         }
5056
5057         if (this.fireEvent) {
5058             this.scope.dispatchEvent(
5059                 new CustomEvent('DOMTransaction', {
5060                     detail: {
5061                         transactions: this.stack[0].slice()
5062                     },
5063                     bubbles: true,
5064                     cancelable: false
5065                 })
5066             );
5067         }
5068         
5069         //Roo.log("transaction: pos:" + this.position + " len: " + this.length + " slen:" + this.stack.length);
5070       
5071         
5072     },
5073
5074     undo : function ()
5075     {
5076         //Roo.log("undo: pos:" + this.position + " len: " + this.length + " slen:" + this.stack.length);
5077         
5078         if (this.position < this.length) {
5079             for (var i = this.stack[this.position].length - 1; i >= 0; i--) {
5080                 this.stack[this.position][i].undo();
5081             }
5082             this.position++;
5083
5084             if (this.fireEvent) {
5085                 this.scope.dispatchEvent(
5086                     new CustomEvent('undo', {
5087                         detail: {
5088                             transactions: this.stack[this.position - 1].slice()
5089                         },
5090                         bubbles: true,
5091                         cancelable: false
5092                     })
5093                 );
5094             }
5095         }
5096     },
5097
5098     redo : function ()
5099     {
5100         if (this.position > 0) {
5101             for (var i = 0, n = this.stack[this.position - 1].length; i < n; i++) {
5102                 this.stack[this.position - 1][i].redo();
5103             }
5104             this.position--;
5105
5106             if (this.fireEvent) {
5107                 this.scope.dispatchEvent(
5108                     new CustomEvent('redo', {
5109                         detail: {
5110                             transactions: this.stack[this.position].slice()
5111                         },
5112                         bubbles: true,
5113                         cancelable: false
5114                     })
5115                 );
5116             }
5117         }
5118     },
5119
5120     item : function (index)
5121     {
5122         if (index >= 0 && index < this.length) {
5123             return this.stack[index].slice();
5124         }
5125         return null;
5126     },
5127
5128     clearUndo : function () {
5129         this.stack.length = this.length = this.position;
5130     },
5131
5132     clearRedo : function () {
5133         this.stack.splice(0, this.position);
5134         this.position = 0;
5135         this.length = this.stack.length;
5136     },
5137     /**
5138      * Reset the undo - probaly done on load to clear all history.
5139      */
5140     reset : function()
5141     {
5142         this.stack = [];
5143         this.position = 0;
5144         this.length = 0;
5145         this.current_html = this.scope.innerHTML;
5146         if (this.timer !== false) {
5147             clearTimeout(this.timer);
5148         }
5149         this.timer = false;
5150         this.merge = false;
5151         this.addEvent();
5152         
5153     },
5154     current_html : '',
5155     timer : false,
5156     merge : false,
5157     
5158     
5159     // this will handle the undo/redo on the element.?
5160     bindEvents : function()
5161     {
5162         var el  = this.scope;
5163         el.undoManager = this;
5164         
5165         
5166         this.scope.addEventListener('keydown', function(e) {
5167             if ((e.ctrlKey || e.metaKey) && e.keyCode === 90) {
5168                 if (e.shiftKey) {
5169                     el.undoManager.redo(); // Ctrl/Command + Shift + Z
5170                 } else {
5171                     el.undoManager.undo(); // Ctrl/Command + Z
5172                 }
5173         
5174                 e.preventDefault();
5175             }
5176         });
5177         /// ignore keyup..
5178         this.scope.addEventListener('keyup', function(e) {
5179             if ((e.ctrlKey || e.metaKey) && e.keyCode === 90) {
5180                 e.preventDefault();
5181             }
5182         });
5183         
5184         
5185         
5186         var t = this;
5187         
5188         el.addEventListener('input', function(e) {
5189             if(el.innerHTML == t.current_html) {
5190                 return;
5191             }
5192             // only record events every second.
5193             if (t.timer !== false) {
5194                clearTimeout(t.timer);
5195                t.timer = false;
5196             }
5197             t.timer = setTimeout(function() { t.merge = false; }, 1000);
5198             
5199             t.addEvent(t.merge);
5200             t.merge = true; // ignore changes happening every second..
5201         });
5202         },
5203     /**
5204      * Manually add an event.
5205      * Normall called without arguements - and it will just get added to the stack.
5206      * 
5207      */
5208     
5209     addEvent : function(merge)
5210     {
5211         //Roo.log("undomanager +" + (merge ? 'Y':'n'));
5212         // not sure if this should clear the timer 
5213         merge = typeof(merge) == 'undefined' ? false : merge; 
5214         
5215         this.scope.undoManager.transact({
5216             scope : this.scope,
5217             oldHTML: this.current_html,
5218             newHTML: this.scope.innerHTML,
5219             // nothing to execute (content already changed when input is fired)
5220             execute: function() { },
5221             undo: function() {
5222                 this.scope.innerHTML = this.current_html = this.oldHTML;
5223             },
5224             redo: function() {
5225                 this.scope.innerHTML = this.current_html = this.newHTML;
5226             }
5227         }, false); //merge);
5228         
5229         this.merge = merge;
5230         
5231         this.current_html = this.scope.innerHTML;
5232     }
5233     
5234     
5235      
5236     
5237     
5238     
5239 };
5240 /**
5241  * @class Roo.lib.Range
5242  * @constructor
5243  * This is a toolkit, normally used to copy features into a Dom Range element
5244  * Roo.lib.Range.wrap(x);
5245  *
5246  *
5247  *
5248  */
5249 Roo.lib.Range = function() { };
5250
5251 /**
5252  * Wrap a Dom Range object, to give it new features...
5253  * @static
5254  * @param {Range} the range to wrap
5255  */
5256 Roo.lib.Range.wrap = function(r) {
5257     return Roo.apply(r, Roo.lib.Range.prototype);
5258 };
5259 /**
5260  * find a parent node eg. LI / OL
5261  * @param {string|Array} node name or array of nodenames
5262  * @return {DomElement|false}
5263  */
5264 Roo.apply(Roo.lib.Range.prototype,
5265 {
5266     
5267     closest : function(str)
5268     {
5269         if (typeof(str) != 'string') {
5270             // assume it's a array.
5271             for(var i = 0;i < str.length;i++) {
5272                 var r = this.closest(str[i]);
5273                 if (r !== false) {
5274                     return r;
5275                 }
5276                 
5277             }
5278             return false;
5279         }
5280         str = str.toLowerCase();
5281         var n = this.commonAncestorContainer; // might not be a node
5282         while (n.nodeType != 1) {
5283             n = n.parentNode;
5284         }
5285         
5286         if (n.nodeName.toLowerCase() == str ) {
5287             return n;
5288         }
5289         if (n.nodeName.toLowerCase() == 'body') {
5290             return false;
5291         }
5292             
5293         return n.closest(str) || false;
5294         
5295     },
5296     cloneRange : function()
5297     {
5298         return Roo.lib.Range.wrap(Range.prototype.cloneRange.call(this));
5299     }
5300 });/**
5301  * @class Roo.lib.Selection
5302  * @constructor
5303  * This is a toolkit, normally used to copy features into a Dom Selection element
5304  * Roo.lib.Selection.wrap(x);
5305  *
5306  *
5307  *
5308  */
5309 Roo.lib.Selection = function() { };
5310
5311 /**
5312  * Wrap a Dom Range object, to give it new features...
5313  * @static
5314  * @param {Range} the range to wrap
5315  */
5316 Roo.lib.Selection.wrap = function(r, doc) {
5317     Roo.apply(r, Roo.lib.Selection.prototype);
5318     r.ownerDocument = doc; // usefull so we dont have to keep referening to it.
5319     return r;
5320 };
5321 /**
5322  * find a parent node eg. LI / OL
5323  * @param {string|Array} node name or array of nodenames
5324  * @return {DomElement|false}
5325  */
5326 Roo.apply(Roo.lib.Selection.prototype,
5327 {
5328     /**
5329      * the owner document
5330      */
5331     ownerDocument : false,
5332     
5333     getRangeAt : function(n)
5334     {
5335         return Roo.lib.Range.wrap(Selection.prototype.getRangeAt.call(this,n));
5336     },
5337     
5338     /**
5339      * insert node at selection 
5340      * @param {DomElement|string} node
5341      * @param {string} cursor (after|in|none) where to place the cursor after inserting.
5342      */
5343     insertNode: function(node, cursor)
5344     {
5345         if (typeof(node) == 'string') {
5346             node = this.ownerDocument.createElement(node);
5347             if (cursor == 'in') {
5348                 node.innerHTML = '&nbsp;';
5349             }
5350         }
5351         
5352         var range = this.getRangeAt(0);
5353         
5354         if (this.type != 'Caret') {
5355             range.deleteContents();
5356         }
5357         var sn = node.childNodes[0]; // select the contents.
5358
5359         
5360         
5361         range.insertNode(node);
5362         if (cursor == 'after') {
5363             node.insertAdjacentHTML('afterend', '&nbsp;');
5364             sn = node.nextSibling;
5365         }
5366         
5367         if (cursor == 'none') {
5368             return;
5369         }
5370         
5371         this.cursorText(sn);
5372     },
5373     
5374     cursorText : function(n)
5375     {
5376        
5377         //var range = this.getRangeAt(0);
5378         range = Roo.lib.Range.wrap(new Range());
5379         //range.selectNode(n);
5380         
5381         var ix = Array.from(n.parentNode.childNodes).indexOf(n);
5382         range.setStart(n.parentNode,ix);
5383         range.setEnd(n.parentNode,ix+1);
5384         //range.collapse(false);
5385          
5386         this.removeAllRanges();
5387         this.addRange(range);
5388         
5389         Roo.log([n, range, this,this.baseOffset,this.extentOffset, this.type]);
5390     },
5391     cursorAfter : function(n)
5392     {
5393         if (!n.nextSibling || n.nextSibling.nodeValue != '&nbsp;') {
5394             n.insertAdjacentHTML('afterend', '&nbsp;');
5395         }
5396         this.cursorText (n.nextSibling);
5397     }
5398         
5399     
5400 });/*
5401  * Based on:
5402  * Ext JS Library 1.1.1
5403  * Copyright(c) 2006-2007, Ext JS, LLC.
5404  *
5405  * Originally Released Under LGPL - original licence link has changed is not relivant.
5406  *
5407  * Fork - LGPL
5408  * <script type="text/javascript">
5409  */
5410
5411
5412 // nasty IE9 hack - what a pile of crap that is..
5413
5414  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
5415     Range.prototype.createContextualFragment = function (html) {
5416         var doc = window.document;
5417         var container = doc.createElement("div");
5418         container.innerHTML = html;
5419         var frag = doc.createDocumentFragment(), n;
5420         while ((n = container.firstChild)) {
5421             frag.appendChild(n);
5422         }
5423         return frag;
5424     };
5425 }
5426
5427 /**
5428  * @class Roo.DomHelper
5429  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
5430  * 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>.
5431  * @static
5432  */
5433 Roo.DomHelper = function(){
5434     var tempTableEl = null;
5435     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
5436     var tableRe = /^table|tbody|tr|td$/i;
5437     var xmlns = {};
5438     // build as innerHTML where available
5439     /** @ignore */
5440     var createHtml = function(o){
5441         if(typeof o == 'string'){
5442             return o;
5443         }
5444         var b = "";
5445         if(!o.tag){
5446             o.tag = "div";
5447         }
5448         b += "<" + o.tag;
5449         for(var attr in o){
5450             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
5451             if(attr == "style"){
5452                 var s = o["style"];
5453                 if(typeof s == "function"){
5454                     s = s.call();
5455                 }
5456                 if(typeof s == "string"){
5457                     b += ' style="' + s + '"';
5458                 }else if(typeof s == "object"){
5459                     b += ' style="';
5460                     for(var key in s){
5461                         if(typeof s[key] != "function"){
5462                             b += key + ":" + s[key] + ";";
5463                         }
5464                     }
5465                     b += '"';
5466                 }
5467             }else{
5468                 if(attr == "cls"){
5469                     b += ' class="' + o["cls"] + '"';
5470                 }else if(attr == "htmlFor"){
5471                     b += ' for="' + o["htmlFor"] + '"';
5472                 }else{
5473                     b += " " + attr + '="' + o[attr] + '"';
5474                 }
5475             }
5476         }
5477         if(emptyTags.test(o.tag)){
5478             b += "/>";
5479         }else{
5480             b += ">";
5481             var cn = o.children || o.cn;
5482             if(cn){
5483                 //http://bugs.kde.org/show_bug.cgi?id=71506
5484                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
5485                     for(var i = 0, len = cn.length; i < len; i++) {
5486                         b += createHtml(cn[i], b);
5487                     }
5488                 }else{
5489                     b += createHtml(cn, b);
5490                 }
5491             }
5492             if(o.html){
5493                 b += o.html;
5494             }
5495             b += "</" + o.tag + ">";
5496         }
5497         return b;
5498     };
5499
5500     // build as dom
5501     /** @ignore */
5502     var createDom = function(o, parentNode){
5503          
5504         // defininition craeted..
5505         var ns = false;
5506         if (o.ns && o.ns != 'html') {
5507                
5508             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
5509                 xmlns[o.ns] = o.xmlns;
5510                 ns = o.xmlns;
5511             }
5512             if (typeof(xmlns[o.ns]) == 'undefined') {
5513                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
5514             }
5515             ns = xmlns[o.ns];
5516         }
5517         
5518         
5519         if (typeof(o) == 'string') {
5520             return parentNode.appendChild(document.createTextNode(o));
5521         }
5522         o.tag = o.tag || 'div';
5523         if (o.ns && Roo.isIE) {
5524             ns = false;
5525             o.tag = o.ns + ':' + o.tag;
5526             
5527         }
5528         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
5529         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
5530         for(var attr in o){
5531             
5532             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
5533                     attr == "style" || typeof o[attr] == "function") { continue; }
5534                     
5535             if(attr=="cls" && Roo.isIE){
5536                 el.className = o["cls"];
5537             }else{
5538                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
5539                 else { 
5540                     el[attr] = o[attr];
5541                 }
5542             }
5543         }
5544         Roo.DomHelper.applyStyles(el, o.style);
5545         var cn = o.children || o.cn;
5546         if(cn){
5547             //http://bugs.kde.org/show_bug.cgi?id=71506
5548              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
5549                 for(var i = 0, len = cn.length; i < len; i++) {
5550                     createDom(cn[i], el);
5551                 }
5552             }else{
5553                 createDom(cn, el);
5554             }
5555         }
5556         if(o.html){
5557             el.innerHTML = o.html;
5558         }
5559         if(parentNode){
5560            parentNode.appendChild(el);
5561         }
5562         return el;
5563     };
5564
5565     var ieTable = function(depth, s, h, e){
5566         tempTableEl.innerHTML = [s, h, e].join('');
5567         var i = -1, el = tempTableEl;
5568         while(++i < depth && el.firstChild){
5569             el = el.firstChild;
5570         }
5571         return el;
5572     };
5573
5574     // kill repeat to save bytes
5575     var ts = '<table>',
5576         te = '</table>',
5577         tbs = ts+'<tbody>',
5578         tbe = '</tbody>'+te,
5579         trs = tbs + '<tr>',
5580         tre = '</tr>'+tbe;
5581
5582     /**
5583      * @ignore
5584      * Nasty code for IE's broken table implementation
5585      */
5586     var insertIntoTable = function(tag, where, el, html){
5587         if(!tempTableEl){
5588             tempTableEl = document.createElement('div');
5589         }
5590         var node;
5591         var before = null;
5592         if(tag == 'td'){
5593             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
5594                 return;
5595             }
5596             if(where == 'beforebegin'){
5597                 before = el;
5598                 el = el.parentNode;
5599             } else{
5600                 before = el.nextSibling;
5601                 el = el.parentNode;
5602             }
5603             node = ieTable(4, trs, html, tre);
5604         }
5605         else if(tag == 'tr'){
5606             if(where == 'beforebegin'){
5607                 before = el;
5608                 el = el.parentNode;
5609                 node = ieTable(3, tbs, html, tbe);
5610             } else if(where == 'afterend'){
5611                 before = el.nextSibling;
5612                 el = el.parentNode;
5613                 node = ieTable(3, tbs, html, tbe);
5614             } else{ // INTO a TR
5615                 if(where == 'afterbegin'){
5616                     before = el.firstChild;
5617                 }
5618                 node = ieTable(4, trs, html, tre);
5619             }
5620         } else if(tag == 'tbody'){
5621             if(where == 'beforebegin'){
5622                 before = el;
5623                 el = el.parentNode;
5624                 node = ieTable(2, ts, html, te);
5625             } else if(where == 'afterend'){
5626                 before = el.nextSibling;
5627                 el = el.parentNode;
5628                 node = ieTable(2, ts, html, te);
5629             } else{
5630                 if(where == 'afterbegin'){
5631                     before = el.firstChild;
5632                 }
5633                 node = ieTable(3, tbs, html, tbe);
5634             }
5635         } else{ // TABLE
5636             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
5637                 return;
5638             }
5639             if(where == 'afterbegin'){
5640                 before = el.firstChild;
5641             }
5642             node = ieTable(2, ts, html, te);
5643         }
5644         el.insertBefore(node, before);
5645         return node;
5646     };
5647     
5648     // this is a bit like the react update code...
5649     // 
5650     
5651     var updateNode = function(from, to)
5652     {
5653         // should we handle non-standard elements?
5654         Roo.log(["UpdateNode" , from, to]);
5655         if (from.nodeType != to.nodeType) {
5656             Roo.log(["ReplaceChild - mismatch notType" , to, from ]);
5657             from.parentNode.replaceChild(to, from);
5658         }
5659         
5660         if (from.nodeType == 3) {
5661             // assume it's text?!
5662             if (from.data == to.data) {
5663                 return;
5664             }
5665             from.data = to.data;
5666             return;
5667         }
5668         if (!from.parentNode) {
5669             // not sure why this is happening?
5670             return;
5671         }
5672         // assume 'to' doesnt have '1/3 nodetypes!
5673         // not sure why, by from, parent node might not exist?
5674         if (from.nodeType !=1 || from.tagName != to.tagName) {
5675             Roo.log(["ReplaceChild" , from, to ]);
5676             
5677             from.parentNode.replaceChild(to, from);
5678             return;
5679         }
5680         // compare attributes
5681         var ar = Array.from(from.attributes);
5682         for(var i = 0; i< ar.length;i++) {
5683             if (to.hasAttribute(ar[i].name)) {
5684                 continue;
5685             }
5686             if (ar[i].name == 'id') { // always keep ids?
5687                continue;
5688             }
5689             //if (ar[i].name == 'style') {
5690             //   throw "style removed?";
5691             //}
5692             Roo.log("removeAttribute" + ar[i].name);
5693             from.removeAttribute(ar[i].name);
5694         }
5695         ar = to.attributes;
5696         for(var i = 0; i< ar.length;i++) {
5697             if (from.getAttribute(ar[i].name) == to.getAttribute(ar[i].name)) {
5698                 Roo.log("skipAttribute " + ar[i].name  + '=' + to.getAttribute(ar[i].name));
5699                 continue;
5700             }
5701             Roo.log("updateAttribute " + ar[i].name + '=>' + to.getAttribute(ar[i].name));
5702             from.setAttribute(ar[i].name, to.getAttribute(ar[i].name));
5703         }
5704         // children
5705         var far = Array.from(from.childNodes);
5706         var tar = Array.from(to.childNodes);
5707         // if the lengths are different.. then it's probably a editable content change, rather than
5708         // a change of the block definition..
5709         
5710         // this did notwork , as our rebuilt nodes did not include ID's so did not match at all.
5711          /*if (from.innerHTML == to.innerHTML) {
5712             return;
5713         }
5714         if (far.length != tar.length) {
5715             from.innerHTML = to.innerHTML;
5716             return;
5717         }
5718         */
5719         
5720         for(var i = 0; i < Math.max(tar.length, far.length); i++) {
5721             if (i >= far.length) {
5722                 from.appendChild(tar[i]);
5723                 Roo.log(["add", tar[i]]);
5724                 
5725             } else if ( i  >= tar.length) {
5726                 from.removeChild(far[i]);
5727                 Roo.log(["remove", far[i]]);
5728             } else {
5729                 
5730                 updateNode(far[i], tar[i]);
5731             }    
5732         }
5733         
5734         
5735         
5736         
5737     };
5738     
5739     
5740
5741     return {
5742         /** True to force the use of DOM instead of html fragments @type Boolean */
5743         useDom : false,
5744     
5745         /**
5746          * Returns the markup for the passed Element(s) config
5747          * @param {Object} o The Dom object spec (and children)
5748          * @return {String}
5749          */
5750         markup : function(o){
5751             return createHtml(o);
5752         },
5753     
5754         /**
5755          * Applies a style specification to an element
5756          * @param {String/HTMLElement} el The element to apply styles to
5757          * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
5758          * a function which returns such a specification.
5759          */
5760         applyStyles : function(el, styles){
5761             if(styles){
5762                el = Roo.fly(el);
5763                if(typeof styles == "string"){
5764                    var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
5765                    var matches;
5766                    while ((matches = re.exec(styles)) != null){
5767                        el.setStyle(matches[1], matches[2]);
5768                    }
5769                }else if (typeof styles == "object"){
5770                    for (var style in styles){
5771                       el.setStyle(style, styles[style]);
5772                    }
5773                }else if (typeof styles == "function"){
5774                     Roo.DomHelper.applyStyles(el, styles.call());
5775                }
5776             }
5777         },
5778     
5779         /**
5780          * Inserts an HTML fragment into the Dom
5781          * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
5782          * @param {HTMLElement} el The context element
5783          * @param {String} html The HTML fragmenet
5784          * @return {HTMLElement} The new node
5785          */
5786         insertHtml : function(where, el, html){
5787             where = where.toLowerCase();
5788             if(el.insertAdjacentHTML){
5789                 if(tableRe.test(el.tagName)){
5790                     var rs;
5791                     if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
5792                         return rs;
5793                     }
5794                 }
5795                 switch(where){
5796                     case "beforebegin":
5797                         el.insertAdjacentHTML('BeforeBegin', html);
5798                         return el.previousSibling;
5799                     case "afterbegin":
5800                         el.insertAdjacentHTML('AfterBegin', html);
5801                         return el.firstChild;
5802                     case "beforeend":
5803                         el.insertAdjacentHTML('BeforeEnd', html);
5804                         return el.lastChild;
5805                     case "afterend":
5806                         el.insertAdjacentHTML('AfterEnd', html);
5807                         return el.nextSibling;
5808                 }
5809                 throw 'Illegal insertion point -> "' + where + '"';
5810             }
5811             var range = el.ownerDocument.createRange();
5812             var frag;
5813             switch(where){
5814                  case "beforebegin":
5815                     range.setStartBefore(el);
5816                     frag = range.createContextualFragment(html);
5817                     el.parentNode.insertBefore(frag, el);
5818                     return el.previousSibling;
5819                  case "afterbegin":
5820                     if(el.firstChild){
5821                         range.setStartBefore(el.firstChild);
5822                         frag = range.createContextualFragment(html);
5823                         el.insertBefore(frag, el.firstChild);
5824                         return el.firstChild;
5825                     }else{
5826                         el.innerHTML = html;
5827                         return el.firstChild;
5828                     }
5829                 case "beforeend":
5830                     if(el.lastChild){
5831                         range.setStartAfter(el.lastChild);
5832                         frag = range.createContextualFragment(html);
5833                         el.appendChild(frag);
5834                         return el.lastChild;
5835                     }else{
5836                         el.innerHTML = html;
5837                         return el.lastChild;
5838                     }
5839                 case "afterend":
5840                     range.setStartAfter(el);
5841                     frag = range.createContextualFragment(html);
5842                     el.parentNode.insertBefore(frag, el.nextSibling);
5843                     return el.nextSibling;
5844                 }
5845                 throw 'Illegal insertion point -> "' + where + '"';
5846         },
5847     
5848         /**
5849          * Creates new Dom element(s) and inserts them before el
5850          * @param {String/HTMLElement/Element} el The context element
5851          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5852          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5853          * @return {HTMLElement/Roo.Element} The new node
5854          */
5855         insertBefore : function(el, o, returnElement){
5856             return this.doInsert(el, o, returnElement, "beforeBegin");
5857         },
5858     
5859         /**
5860          * Creates new Dom element(s) and inserts them after el
5861          * @param {String/HTMLElement/Element} el The context element
5862          * @param {Object} o The Dom object spec (and children)
5863          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5864          * @return {HTMLElement/Roo.Element} The new node
5865          */
5866         insertAfter : function(el, o, returnElement){
5867             return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
5868         },
5869     
5870         /**
5871          * Creates new Dom element(s) and inserts them as the first child of el
5872          * @param {String/HTMLElement/Element} el The context element
5873          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5874          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5875          * @return {HTMLElement/Roo.Element} The new node
5876          */
5877         insertFirst : function(el, o, returnElement){
5878             return this.doInsert(el, o, returnElement, "afterBegin");
5879         },
5880     
5881         // private
5882         doInsert : function(el, o, returnElement, pos, sibling){
5883             el = Roo.getDom(el);
5884             var newNode;
5885             if(this.useDom || o.ns){
5886                 newNode = createDom(o, null);
5887                 el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
5888             }else{
5889                 var html = createHtml(o);
5890                 newNode = this.insertHtml(pos, el, html);
5891             }
5892             return returnElement ? Roo.get(newNode, true) : newNode;
5893         },
5894     
5895         /**
5896          * Creates new Dom element(s) and appends them to el
5897          * @param {String/HTMLElement/Element} el The context element
5898          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5899          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5900          * @return {HTMLElement/Roo.Element} The new node
5901          */
5902         append : function(el, o, returnElement){
5903             el = Roo.getDom(el);
5904             var newNode;
5905             if(this.useDom || o.ns){
5906                 newNode = createDom(o, null);
5907                 el.appendChild(newNode);
5908             }else{
5909                 var html = createHtml(o);
5910                 newNode = this.insertHtml("beforeEnd", el, html);
5911             }
5912             return returnElement ? Roo.get(newNode, true) : newNode;
5913         },
5914     
5915         /**
5916          * Creates new Dom element(s) and overwrites the contents of el with them
5917          * @param {String/HTMLElement/Element} el The context element
5918          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5919          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5920          * @return {HTMLElement/Roo.Element} The new node
5921          */
5922         overwrite : function(el, o, returnElement)
5923         {
5924             el = Roo.getDom(el);
5925             if (o.ns) {
5926               
5927                 while (el.childNodes.length) {
5928                     el.removeChild(el.firstChild);
5929                 }
5930                 createDom(o, el);
5931             } else {
5932                 el.innerHTML = createHtml(o);   
5933             }
5934             
5935             return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
5936         },
5937     
5938         /**
5939          * Creates a new Roo.DomHelper.Template from the Dom object spec
5940          * @param {Object} o The Dom object spec (and children)
5941          * @return {Roo.DomHelper.Template} The new template
5942          */
5943         createTemplate : function(o){
5944             var html = createHtml(o);
5945             return new Roo.Template(html);
5946         },
5947          /**
5948          * Updates the first element with the spec from the o (replacing if necessary)
5949          * This iterates through the children, and updates attributes / children etc..
5950          * @param {String/HTMLElement/Element} el The context element
5951          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5952          */
5953         
5954         update : function(el, o)
5955         {
5956             updateNode(Roo.getDom(el), createDom(o));
5957             
5958         }
5959         
5960         
5961     };
5962 }();
5963 /*
5964  * Based on:
5965  * Ext JS Library 1.1.1
5966  * Copyright(c) 2006-2007, Ext JS, LLC.
5967  *
5968  * Originally Released Under LGPL - original licence link has changed is not relivant.
5969  *
5970  * Fork - LGPL
5971  * <script type="text/javascript">
5972  */
5973  
5974 /**
5975 * @class Roo.Template
5976 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
5977 * For a list of available format functions, see {@link Roo.util.Format}.<br />
5978 * Usage:
5979 <pre><code>
5980 var t = new Roo.Template({
5981     html :  '&lt;div name="{id}"&gt;' + 
5982         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
5983         '&lt;/div&gt;',
5984     myformat: function (value, allValues) {
5985         return 'XX' + value;
5986     }
5987 });
5988 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
5989 </code></pre>
5990 * For more information see this blog post with examples:
5991 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
5992      - Create Elements using DOM, HTML fragments and Templates</a>. 
5993 * @constructor
5994 * @param {Object} cfg - Configuration object.
5995 */
5996 Roo.Template = function(cfg){
5997     // BC!
5998     if(cfg instanceof Array){
5999         cfg = cfg.join("");
6000     }else if(arguments.length > 1){
6001         cfg = Array.prototype.join.call(arguments, "");
6002     }
6003     
6004     
6005     if (typeof(cfg) == 'object') {
6006         Roo.apply(this,cfg)
6007     } else {
6008         // bc
6009         this.html = cfg;
6010     }
6011     if (this.url) {
6012         this.load();
6013     }
6014     
6015 };
6016 Roo.Template.prototype = {
6017     
6018     /**
6019      * @cfg {Function} onLoad Called after the template has been loaded and complied (usually from a remove source)
6020      */
6021     onLoad : false,
6022     
6023     
6024     /**
6025      * @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..
6026      *                    it should be fixed so that template is observable...
6027      */
6028     url : false,
6029     /**
6030      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
6031      */
6032     html : '',
6033     
6034     
6035     compiled : false,
6036     loaded : false,
6037     /**
6038      * Returns an HTML fragment of this template with the specified values applied.
6039      * @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'})
6040      * @return {String} The HTML fragment
6041      */
6042     
6043    
6044     
6045     applyTemplate : function(values){
6046         //Roo.log(["applyTemplate", values]);
6047         try {
6048            
6049             if(this.compiled){
6050                 return this.compiled(values);
6051             }
6052             var useF = this.disableFormats !== true;
6053             var fm = Roo.util.Format, tpl = this;
6054             var fn = function(m, name, format, args){
6055                 if(format && useF){
6056                     if(format.substr(0, 5) == "this."){
6057                         return tpl.call(format.substr(5), values[name], values);
6058                     }else{
6059                         if(args){
6060                             // quoted values are required for strings in compiled templates, 
6061                             // but for non compiled we need to strip them
6062                             // quoted reversed for jsmin
6063                             var re = /^\s*['"](.*)["']\s*$/;
6064                             args = args.split(',');
6065                             for(var i = 0, len = args.length; i < len; i++){
6066                                 args[i] = args[i].replace(re, "$1");
6067                             }
6068                             args = [values[name]].concat(args);
6069                         }else{
6070                             args = [values[name]];
6071                         }
6072                         return fm[format].apply(fm, args);
6073                     }
6074                 }else{
6075                     return values[name] !== undefined ? values[name] : "";
6076                 }
6077             };
6078             return this.html.replace(this.re, fn);
6079         } catch (e) {
6080             Roo.log(e);
6081             throw e;
6082         }
6083          
6084     },
6085     
6086     loading : false,
6087       
6088     load : function ()
6089     {
6090          
6091         if (this.loading) {
6092             return;
6093         }
6094         var _t = this;
6095         
6096         this.loading = true;
6097         this.compiled = false;
6098         
6099         var cx = new Roo.data.Connection();
6100         cx.request({
6101             url : this.url,
6102             method : 'GET',
6103             success : function (response) {
6104                 _t.loading = false;
6105                 _t.url = false;
6106                 
6107                 _t.set(response.responseText,true);
6108                 _t.loaded = true;
6109                 if (_t.onLoad) {
6110                     _t.onLoad();
6111                 }
6112              },
6113             failure : function(response) {
6114                 Roo.log("Template failed to load from " + _t.url);
6115                 _t.loading = false;
6116             }
6117         });
6118     },
6119
6120     /**
6121      * Sets the HTML used as the template and optionally compiles it.
6122      * @param {String} html
6123      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
6124      * @return {Roo.Template} this
6125      */
6126     set : function(html, compile){
6127         this.html = html;
6128         this.compiled = false;
6129         if(compile){
6130             this.compile();
6131         }
6132         return this;
6133     },
6134     
6135     /**
6136      * True to disable format functions (defaults to false)
6137      * @type Boolean
6138      */
6139     disableFormats : false,
6140     
6141     /**
6142     * The regular expression used to match template variables 
6143     * @type RegExp
6144     * @property 
6145     */
6146     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
6147     
6148     /**
6149      * Compiles the template into an internal function, eliminating the RegEx overhead.
6150      * @return {Roo.Template} this
6151      */
6152     compile : function(){
6153         var fm = Roo.util.Format;
6154         var useF = this.disableFormats !== true;
6155         var sep = Roo.isGecko ? "+" : ",";
6156         var fn = function(m, name, format, args){
6157             if(format && useF){
6158                 args = args ? ',' + args : "";
6159                 if(format.substr(0, 5) != "this."){
6160                     format = "fm." + format + '(';
6161                 }else{
6162                     format = 'this.call("'+ format.substr(5) + '", ';
6163                     args = ", values";
6164                 }
6165             }else{
6166                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
6167             }
6168             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
6169         };
6170         var body;
6171         // branched to use + in gecko and [].join() in others
6172         if(Roo.isGecko){
6173             body = "this.compiled = function(values){ return '" +
6174                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
6175                     "';};";
6176         }else{
6177             body = ["this.compiled = function(values){ return ['"];
6178             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
6179             body.push("'].join('');};");
6180             body = body.join('');
6181         }
6182         /**
6183          * eval:var:values
6184          * eval:var:fm
6185          */
6186         eval(body);
6187         return this;
6188     },
6189     
6190     // private function used to call members
6191     call : function(fnName, value, allValues){
6192         return this[fnName](value, allValues);
6193     },
6194     
6195     /**
6196      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
6197      * @param {String/HTMLElement/Roo.Element} el The context element
6198      * @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'})
6199      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6200      * @return {HTMLElement/Roo.Element} The new node or Element
6201      */
6202     insertFirst: function(el, values, returnElement){
6203         return this.doInsert('afterBegin', el, values, returnElement);
6204     },
6205
6206     /**
6207      * Applies the supplied values to the template and inserts the new node(s) before el.
6208      * @param {String/HTMLElement/Roo.Element} el The context element
6209      * @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'})
6210      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6211      * @return {HTMLElement/Roo.Element} The new node or Element
6212      */
6213     insertBefore: function(el, values, returnElement){
6214         return this.doInsert('beforeBegin', el, values, returnElement);
6215     },
6216
6217     /**
6218      * Applies the supplied values to the template and inserts the new node(s) after el.
6219      * @param {String/HTMLElement/Roo.Element} el The context element
6220      * @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'})
6221      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6222      * @return {HTMLElement/Roo.Element} The new node or Element
6223      */
6224     insertAfter : function(el, values, returnElement){
6225         return this.doInsert('afterEnd', el, values, returnElement);
6226     },
6227     
6228     /**
6229      * Applies the supplied values to the template and appends the new node(s) to el.
6230      * @param {String/HTMLElement/Roo.Element} el The context element
6231      * @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'})
6232      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6233      * @return {HTMLElement/Roo.Element} The new node or Element
6234      */
6235     append : function(el, values, returnElement){
6236         return this.doInsert('beforeEnd', el, values, returnElement);
6237     },
6238
6239     doInsert : function(where, el, values, returnEl){
6240         el = Roo.getDom(el);
6241         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
6242         return returnEl ? Roo.get(newNode, true) : newNode;
6243     },
6244
6245     /**
6246      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
6247      * @param {String/HTMLElement/Roo.Element} el The context element
6248      * @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'})
6249      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6250      * @return {HTMLElement/Roo.Element} The new node or Element
6251      */
6252     overwrite : function(el, values, returnElement){
6253         el = Roo.getDom(el);
6254         el.innerHTML = this.applyTemplate(values);
6255         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
6256     }
6257 };
6258 /**
6259  * Alias for {@link #applyTemplate}
6260  * @method
6261  */
6262 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
6263
6264 // backwards compat
6265 Roo.DomHelper.Template = Roo.Template;
6266
6267 /**
6268  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
6269  * @param {String/HTMLElement} el A DOM element or its id
6270  * @returns {Roo.Template} The created template
6271  * @static
6272  */
6273 Roo.Template.from = function(el){
6274     el = Roo.getDom(el);
6275     return new Roo.Template(el.value || el.innerHTML);
6276 };/*
6277  * Based on:
6278  * Ext JS Library 1.1.1
6279  * Copyright(c) 2006-2007, Ext JS, LLC.
6280  *
6281  * Originally Released Under LGPL - original licence link has changed is not relivant.
6282  *
6283  * Fork - LGPL
6284  * <script type="text/javascript">
6285  */
6286  
6287
6288 /*
6289  * This is code is also distributed under MIT license for use
6290  * with jQuery and prototype JavaScript libraries.
6291  */
6292 /**
6293  * @class Roo.DomQuery
6294 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).
6295 <p>
6296 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>
6297
6298 <p>
6299 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.
6300 </p>
6301 <h4>Element Selectors:</h4>
6302 <ul class="list">
6303     <li> <b>*</b> any element</li>
6304     <li> <b>E</b> an element with the tag E</li>
6305     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
6306     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
6307     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
6308     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
6309 </ul>
6310 <h4>Attribute Selectors:</h4>
6311 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
6312 <ul class="list">
6313     <li> <b>E[foo]</b> has an attribute "foo"</li>
6314     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
6315     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
6316     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
6317     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
6318     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
6319     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
6320 </ul>
6321 <h4>Pseudo Classes:</h4>
6322 <ul class="list">
6323     <li> <b>E:first-child</b> E is the first child of its parent</li>
6324     <li> <b>E:last-child</b> E is the last child of its parent</li>
6325     <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>
6326     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
6327     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
6328     <li> <b>E:only-child</b> E is the only child of its parent</li>
6329     <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>
6330     <li> <b>E:first</b> the first E in the resultset</li>
6331     <li> <b>E:last</b> the last E in the resultset</li>
6332     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
6333     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
6334     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
6335     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
6336     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
6337     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
6338     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
6339     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
6340     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
6341 </ul>
6342 <h4>CSS Value Selectors:</h4>
6343 <ul class="list">
6344     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
6345     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
6346     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
6347     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
6348     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
6349     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
6350 </ul>
6351  * @static
6352  */
6353 Roo.DomQuery = function(){
6354     var cache = {}, simpleCache = {}, valueCache = {};
6355     var nonSpace = /\S/;
6356     var trimRe = /^\s+|\s+$/g;
6357     var tplRe = /\{(\d+)\}/g;
6358     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
6359     var tagTokenRe = /^(#)?([\w-\*]+)/;
6360     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
6361
6362     function child(p, index){
6363         var i = 0;
6364         var n = p.firstChild;
6365         while(n){
6366             if(n.nodeType == 1){
6367                if(++i == index){
6368                    return n;
6369                }
6370             }
6371             n = n.nextSibling;
6372         }
6373         return null;
6374     };
6375
6376     function next(n){
6377         while((n = n.nextSibling) && n.nodeType != 1);
6378         return n;
6379     };
6380
6381     function prev(n){
6382         while((n = n.previousSibling) && n.nodeType != 1);
6383         return n;
6384     };
6385
6386     function children(d){
6387         var n = d.firstChild, ni = -1;
6388             while(n){
6389                 var nx = n.nextSibling;
6390                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
6391                     d.removeChild(n);
6392                 }else{
6393                     n.nodeIndex = ++ni;
6394                 }
6395                 n = nx;
6396             }
6397             return this;
6398         };
6399
6400     function byClassName(c, a, v){
6401         if(!v){
6402             return c;
6403         }
6404         var r = [], ri = -1, cn;
6405         for(var i = 0, ci; ci = c[i]; i++){
6406             
6407             
6408             if((' '+
6409                 ( (ci instanceof SVGElement) ? ci.className.baseVal : ci.className)
6410                  +' ').indexOf(v) != -1){
6411                 r[++ri] = ci;
6412             }
6413         }
6414         return r;
6415     };
6416
6417     function attrValue(n, attr){
6418         if(!n.tagName && typeof n.length != "undefined"){
6419             n = n[0];
6420         }
6421         if(!n){
6422             return null;
6423         }
6424         if(attr == "for"){
6425             return n.htmlFor;
6426         }
6427         if(attr == "class" || attr == "className"){
6428             return (n instanceof SVGElement) ? n.className.baseVal : n.className;
6429         }
6430         return n.getAttribute(attr) || n[attr];
6431
6432     };
6433
6434     function getNodes(ns, mode, tagName){
6435         var result = [], ri = -1, cs;
6436         if(!ns){
6437             return result;
6438         }
6439         tagName = tagName || "*";
6440         if(typeof ns.getElementsByTagName != "undefined"){
6441             ns = [ns];
6442         }
6443         if(!mode){
6444             for(var i = 0, ni; ni = ns[i]; i++){
6445                 cs = ni.getElementsByTagName(tagName);
6446                 for(var j = 0, ci; ci = cs[j]; j++){
6447                     result[++ri] = ci;
6448                 }
6449             }
6450         }else if(mode == "/" || mode == ">"){
6451             var utag = tagName.toUpperCase();
6452             for(var i = 0, ni, cn; ni = ns[i]; i++){
6453                 cn = ni.children || ni.childNodes;
6454                 for(var j = 0, cj; cj = cn[j]; j++){
6455                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
6456                         result[++ri] = cj;
6457                     }
6458                 }
6459             }
6460         }else if(mode == "+"){
6461             var utag = tagName.toUpperCase();
6462             for(var i = 0, n; n = ns[i]; i++){
6463                 while((n = n.nextSibling) && n.nodeType != 1);
6464                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
6465                     result[++ri] = n;
6466                 }
6467             }
6468         }else if(mode == "~"){
6469             for(var i = 0, n; n = ns[i]; i++){
6470                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
6471                 if(n){
6472                     result[++ri] = n;
6473                 }
6474             }
6475         }
6476         return result;
6477     };
6478
6479     function concat(a, b){
6480         if(b.slice){
6481             return a.concat(b);
6482         }
6483         for(var i = 0, l = b.length; i < l; i++){
6484             a[a.length] = b[i];
6485         }
6486         return a;
6487     }
6488
6489     function byTag(cs, tagName){
6490         if(cs.tagName || cs == document){
6491             cs = [cs];
6492         }
6493         if(!tagName){
6494             return cs;
6495         }
6496         var r = [], ri = -1;
6497         tagName = tagName.toLowerCase();
6498         for(var i = 0, ci; ci = cs[i]; i++){
6499             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
6500                 r[++ri] = ci;
6501             }
6502         }
6503         return r;
6504     };
6505
6506     function byId(cs, attr, id){
6507         if(cs.tagName || cs == document){
6508             cs = [cs];
6509         }
6510         if(!id){
6511             return cs;
6512         }
6513         var r = [], ri = -1;
6514         for(var i = 0,ci; ci = cs[i]; i++){
6515             if(ci && ci.id == id){
6516                 r[++ri] = ci;
6517                 return r;
6518             }
6519         }
6520         return r;
6521     };
6522
6523     function byAttribute(cs, attr, value, op, custom){
6524         var r = [], ri = -1, st = custom=="{";
6525         var f = Roo.DomQuery.operators[op];
6526         for(var i = 0, ci; ci = cs[i]; i++){
6527             var a;
6528             if(st){
6529                 a = Roo.DomQuery.getStyle(ci, attr);
6530             }
6531             else if(attr == "class" || attr == "className"){
6532                 a = (ci instanceof SVGElement) ? ci.className.baseVal : ci.className;
6533             }else if(attr == "for"){
6534                 a = ci.htmlFor;
6535             }else if(attr == "href"){
6536                 a = ci.getAttribute("href", 2);
6537             }else{
6538                 a = ci.getAttribute(attr);
6539             }
6540             if((f && f(a, value)) || (!f && a)){
6541                 r[++ri] = ci;
6542             }
6543         }
6544         return r;
6545     };
6546
6547     function byPseudo(cs, name, value){
6548         return Roo.DomQuery.pseudos[name](cs, value);
6549     };
6550
6551     // This is for IE MSXML which does not support expandos.
6552     // IE runs the same speed using setAttribute, however FF slows way down
6553     // and Safari completely fails so they need to continue to use expandos.
6554     var isIE = window.ActiveXObject ? true : false;
6555
6556     // this eval is stop the compressor from
6557     // renaming the variable to something shorter
6558     
6559     /** eval:var:batch */
6560     var batch = 30803; 
6561
6562     var key = 30803;
6563
6564     function nodupIEXml(cs){
6565         var d = ++key;
6566         cs[0].setAttribute("_nodup", d);
6567         var r = [cs[0]];
6568         for(var i = 1, len = cs.length; i < len; i++){
6569             var c = cs[i];
6570             if(!c.getAttribute("_nodup") != d){
6571                 c.setAttribute("_nodup", d);
6572                 r[r.length] = c;
6573             }
6574         }
6575         for(var i = 0, len = cs.length; i < len; i++){
6576             cs[i].removeAttribute("_nodup");
6577         }
6578         return r;
6579     }
6580
6581     function nodup(cs){
6582         if(!cs){
6583             return [];
6584         }
6585         var len = cs.length, c, i, r = cs, cj, ri = -1;
6586         if(!len || typeof cs.nodeType != "undefined" || len == 1){
6587             return cs;
6588         }
6589         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
6590             return nodupIEXml(cs);
6591         }
6592         var d = ++key;
6593         cs[0]._nodup = d;
6594         for(i = 1; c = cs[i]; i++){
6595             if(c._nodup != d){
6596                 c._nodup = d;
6597             }else{
6598                 r = [];
6599                 for(var j = 0; j < i; j++){
6600                     r[++ri] = cs[j];
6601                 }
6602                 for(j = i+1; cj = cs[j]; j++){
6603                     if(cj._nodup != d){
6604                         cj._nodup = d;
6605                         r[++ri] = cj;
6606                     }
6607                 }
6608                 return r;
6609             }
6610         }
6611         return r;
6612     }
6613
6614     function quickDiffIEXml(c1, c2){
6615         var d = ++key;
6616         for(var i = 0, len = c1.length; i < len; i++){
6617             c1[i].setAttribute("_qdiff", d);
6618         }
6619         var r = [];
6620         for(var i = 0, len = c2.length; i < len; i++){
6621             if(c2[i].getAttribute("_qdiff") != d){
6622                 r[r.length] = c2[i];
6623             }
6624         }
6625         for(var i = 0, len = c1.length; i < len; i++){
6626            c1[i].removeAttribute("_qdiff");
6627         }
6628         return r;
6629     }
6630
6631     function quickDiff(c1, c2){
6632         var len1 = c1.length;
6633         if(!len1){
6634             return c2;
6635         }
6636         if(isIE && c1[0].selectSingleNode){
6637             return quickDiffIEXml(c1, c2);
6638         }
6639         var d = ++key;
6640         for(var i = 0; i < len1; i++){
6641             c1[i]._qdiff = d;
6642         }
6643         var r = [];
6644         for(var i = 0, len = c2.length; i < len; i++){
6645             if(c2[i]._qdiff != d){
6646                 r[r.length] = c2[i];
6647             }
6648         }
6649         return r;
6650     }
6651
6652     function quickId(ns, mode, root, id){
6653         if(ns == root){
6654            var d = root.ownerDocument || root;
6655            return d.getElementById(id);
6656         }
6657         ns = getNodes(ns, mode, "*");
6658         return byId(ns, null, id);
6659     }
6660
6661     return {
6662         getStyle : function(el, name){
6663             return Roo.fly(el).getStyle(name);
6664         },
6665         /**
6666          * Compiles a selector/xpath query into a reusable function. The returned function
6667          * takes one parameter "root" (optional), which is the context node from where the query should start.
6668          * @param {String} selector The selector/xpath query
6669          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
6670          * @return {Function}
6671          */
6672         compile : function(path, type){
6673             type = type || "select";
6674             
6675             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
6676             var q = path, mode, lq;
6677             var tk = Roo.DomQuery.matchers;
6678             var tklen = tk.length;
6679             var mm;
6680
6681             // accept leading mode switch
6682             var lmode = q.match(modeRe);
6683             if(lmode && lmode[1]){
6684                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
6685                 q = q.replace(lmode[1], "");
6686             }
6687             // strip leading slashes
6688             while(path.substr(0, 1)=="/"){
6689                 path = path.substr(1);
6690             }
6691
6692             while(q && lq != q){
6693                 lq = q;
6694                 var tm = q.match(tagTokenRe);
6695                 if(type == "select"){
6696                     if(tm){
6697                         if(tm[1] == "#"){
6698                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
6699                         }else{
6700                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
6701                         }
6702                         q = q.replace(tm[0], "");
6703                     }else if(q.substr(0, 1) != '@'){
6704                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
6705                     }
6706                 }else{
6707                     if(tm){
6708                         if(tm[1] == "#"){
6709                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
6710                         }else{
6711                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
6712                         }
6713                         q = q.replace(tm[0], "");
6714                     }
6715                 }
6716                 while(!(mm = q.match(modeRe))){
6717                     var matched = false;
6718                     for(var j = 0; j < tklen; j++){
6719                         var t = tk[j];
6720                         var m = q.match(t.re);
6721                         if(m){
6722                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
6723                                                     return m[i];
6724                                                 });
6725                             q = q.replace(m[0], "");
6726                             matched = true;
6727                             break;
6728                         }
6729                     }
6730                     // prevent infinite loop on bad selector
6731                     if(!matched){
6732                         throw 'Error parsing selector, parsing failed at "' + q + '"';
6733                     }
6734                 }
6735                 if(mm[1]){
6736                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
6737                     q = q.replace(mm[1], "");
6738                 }
6739             }
6740             fn[fn.length] = "return nodup(n);\n}";
6741             
6742              /** 
6743               * list of variables that need from compression as they are used by eval.
6744              *  eval:var:batch 
6745              *  eval:var:nodup
6746              *  eval:var:byTag
6747              *  eval:var:ById
6748              *  eval:var:getNodes
6749              *  eval:var:quickId
6750              *  eval:var:mode
6751              *  eval:var:root
6752              *  eval:var:n
6753              *  eval:var:byClassName
6754              *  eval:var:byPseudo
6755              *  eval:var:byAttribute
6756              *  eval:var:attrValue
6757              * 
6758              **/ 
6759             eval(fn.join(""));
6760             return f;
6761         },
6762
6763         /**
6764          * Selects a group of elements.
6765          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
6766          * @param {Node} root (optional) The start of the query (defaults to document).
6767          * @return {Array}
6768          */
6769         select : function(path, root, type){
6770             if(!root || root == document){
6771                 root = document;
6772             }
6773             if(typeof root == "string"){
6774                 root = document.getElementById(root);
6775             }
6776             var paths = path.split(",");
6777             var results = [];
6778             for(var i = 0, len = paths.length; i < len; i++){
6779                 var p = paths[i].replace(trimRe, "");
6780                 if(!cache[p]){
6781                     cache[p] = Roo.DomQuery.compile(p);
6782                     if(!cache[p]){
6783                         throw p + " is not a valid selector";
6784                     }
6785                 }
6786                 var result = cache[p](root);
6787                 if(result && result != document){
6788                     results = results.concat(result);
6789                 }
6790             }
6791             if(paths.length > 1){
6792                 return nodup(results);
6793             }
6794             return results;
6795         },
6796
6797         /**
6798          * Selects a single element.
6799          * @param {String} selector The selector/xpath query
6800          * @param {Node} root (optional) The start of the query (defaults to document).
6801          * @return {Element}
6802          */
6803         selectNode : function(path, root){
6804             return Roo.DomQuery.select(path, root)[0];
6805         },
6806
6807         /**
6808          * Selects the value of a node, optionally replacing null with the defaultValue.
6809          * @param {String} selector The selector/xpath query
6810          * @param {Node} root (optional) The start of the query (defaults to document).
6811          * @param {String} defaultValue
6812          */
6813         selectValue : function(path, root, defaultValue){
6814             path = path.replace(trimRe, "");
6815             if(!valueCache[path]){
6816                 valueCache[path] = Roo.DomQuery.compile(path, "select");
6817             }
6818             var n = valueCache[path](root);
6819             n = n[0] ? n[0] : n;
6820             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
6821             return ((v === null||v === undefined||v==='') ? defaultValue : v);
6822         },
6823
6824         /**
6825          * Selects the value of a node, parsing integers and floats.
6826          * @param {String} selector The selector/xpath query
6827          * @param {Node} root (optional) The start of the query (defaults to document).
6828          * @param {Number} defaultValue
6829          * @return {Number}
6830          */
6831         selectNumber : function(path, root, defaultValue){
6832             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
6833             return parseFloat(v);
6834         },
6835
6836         /**
6837          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
6838          * @param {String/HTMLElement/Array} el An element id, element or array of elements
6839          * @param {String} selector The simple selector to test
6840          * @return {Boolean}
6841          */
6842         is : function(el, ss){
6843             if(typeof el == "string"){
6844                 el = document.getElementById(el);
6845             }
6846             var isArray = (el instanceof Array);
6847             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
6848             return isArray ? (result.length == el.length) : (result.length > 0);
6849         },
6850
6851         /**
6852          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
6853          * @param {Array} el An array of elements to filter
6854          * @param {String} selector The simple selector to test
6855          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
6856          * the selector instead of the ones that match
6857          * @return {Array}
6858          */
6859         filter : function(els, ss, nonMatches){
6860             ss = ss.replace(trimRe, "");
6861             if(!simpleCache[ss]){
6862                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
6863             }
6864             var result = simpleCache[ss](els);
6865             return nonMatches ? quickDiff(result, els) : result;
6866         },
6867
6868         /**
6869          * Collection of matching regular expressions and code snippets.
6870          */
6871         matchers : [{
6872                 re: /^\.([\w-]+)/,
6873                 select: 'n = byClassName(n, null, " {1} ");'
6874             }, {
6875                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
6876                 select: 'n = byPseudo(n, "{1}", "{2}");'
6877             },{
6878                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
6879                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
6880             }, {
6881                 re: /^#([\w-]+)/,
6882                 select: 'n = byId(n, null, "{1}");'
6883             },{
6884                 re: /^@([\w-]+)/,
6885                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
6886             }
6887         ],
6888
6889         /**
6890          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
6891          * 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;.
6892          */
6893         operators : {
6894             "=" : function(a, v){
6895                 return a == v;
6896             },
6897             "!=" : function(a, v){
6898                 return a != v;
6899             },
6900             "^=" : function(a, v){
6901                 return a && a.substr(0, v.length) == v;
6902             },
6903             "$=" : function(a, v){
6904                 return a && a.substr(a.length-v.length) == v;
6905             },
6906             "*=" : function(a, v){
6907                 return a && a.indexOf(v) !== -1;
6908             },
6909             "%=" : function(a, v){
6910                 return (a % v) == 0;
6911             },
6912             "|=" : function(a, v){
6913                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
6914             },
6915             "~=" : function(a, v){
6916                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
6917             }
6918         },
6919
6920         /**
6921          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
6922          * and the argument (if any) supplied in the selector.
6923          */
6924         pseudos : {
6925             "first-child" : function(c){
6926                 var r = [], ri = -1, n;
6927                 for(var i = 0, ci; ci = n = c[i]; i++){
6928                     while((n = n.previousSibling) && n.nodeType != 1);
6929                     if(!n){
6930                         r[++ri] = ci;
6931                     }
6932                 }
6933                 return r;
6934             },
6935
6936             "last-child" : function(c){
6937                 var r = [], ri = -1, n;
6938                 for(var i = 0, ci; ci = n = c[i]; i++){
6939                     while((n = n.nextSibling) && n.nodeType != 1);
6940                     if(!n){
6941                         r[++ri] = ci;
6942                     }
6943                 }
6944                 return r;
6945             },
6946
6947             "nth-child" : function(c, a) {
6948                 var r = [], ri = -1;
6949                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
6950                 var f = (m[1] || 1) - 0, l = m[2] - 0;
6951                 for(var i = 0, n; n = c[i]; i++){
6952                     var pn = n.parentNode;
6953                     if (batch != pn._batch) {
6954                         var j = 0;
6955                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
6956                             if(cn.nodeType == 1){
6957                                cn.nodeIndex = ++j;
6958                             }
6959                         }
6960                         pn._batch = batch;
6961                     }
6962                     if (f == 1) {
6963                         if (l == 0 || n.nodeIndex == l){
6964                             r[++ri] = n;
6965                         }
6966                     } else if ((n.nodeIndex + l) % f == 0){
6967                         r[++ri] = n;
6968                     }
6969                 }
6970
6971                 return r;
6972             },
6973
6974             "only-child" : function(c){
6975                 var r = [], ri = -1;;
6976                 for(var i = 0, ci; ci = c[i]; i++){
6977                     if(!prev(ci) && !next(ci)){
6978                         r[++ri] = ci;
6979                     }
6980                 }
6981                 return r;
6982             },
6983
6984             "empty" : function(c){
6985                 var r = [], ri = -1;
6986                 for(var i = 0, ci; ci = c[i]; i++){
6987                     var cns = ci.childNodes, j = 0, cn, empty = true;
6988                     while(cn = cns[j]){
6989                         ++j;
6990                         if(cn.nodeType == 1 || cn.nodeType == 3){
6991                             empty = false;
6992                             break;
6993                         }
6994                     }
6995                     if(empty){
6996                         r[++ri] = ci;
6997                     }
6998                 }
6999                 return r;
7000             },
7001
7002             "contains" : function(c, v){
7003                 var r = [], ri = -1;
7004                 for(var i = 0, ci; ci = c[i]; i++){
7005                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
7006                         r[++ri] = ci;
7007                     }
7008                 }
7009                 return r;
7010             },
7011
7012             "nodeValue" : function(c, v){
7013                 var r = [], ri = -1;
7014                 for(var i = 0, ci; ci = c[i]; i++){
7015                     if(ci.firstChild && ci.firstChild.nodeValue == v){
7016                         r[++ri] = ci;
7017                     }
7018                 }
7019                 return r;
7020             },
7021
7022             "checked" : function(c){
7023                 var r = [], ri = -1;
7024                 for(var i = 0, ci; ci = c[i]; i++){
7025                     if(ci.checked == true){
7026                         r[++ri] = ci;
7027                     }
7028                 }
7029                 return r;
7030             },
7031
7032             "not" : function(c, ss){
7033                 return Roo.DomQuery.filter(c, ss, true);
7034             },
7035
7036             "odd" : function(c){
7037                 return this["nth-child"](c, "odd");
7038             },
7039
7040             "even" : function(c){
7041                 return this["nth-child"](c, "even");
7042             },
7043
7044             "nth" : function(c, a){
7045                 return c[a-1] || [];
7046             },
7047
7048             "first" : function(c){
7049                 return c[0] || [];
7050             },
7051
7052             "last" : function(c){
7053                 return c[c.length-1] || [];
7054             },
7055
7056             "has" : function(c, ss){
7057                 var s = Roo.DomQuery.select;
7058                 var r = [], ri = -1;
7059                 for(var i = 0, ci; ci = c[i]; i++){
7060                     if(s(ss, ci).length > 0){
7061                         r[++ri] = ci;
7062                     }
7063                 }
7064                 return r;
7065             },
7066
7067             "next" : function(c, ss){
7068                 var is = Roo.DomQuery.is;
7069                 var r = [], ri = -1;
7070                 for(var i = 0, ci; ci = c[i]; i++){
7071                     var n = next(ci);
7072                     if(n && is(n, ss)){
7073                         r[++ri] = ci;
7074                     }
7075                 }
7076                 return r;
7077             },
7078
7079             "prev" : function(c, ss){
7080                 var is = Roo.DomQuery.is;
7081                 var r = [], ri = -1;
7082                 for(var i = 0, ci; ci = c[i]; i++){
7083                     var n = prev(ci);
7084                     if(n && is(n, ss)){
7085                         r[++ri] = ci;
7086                     }
7087                 }
7088                 return r;
7089             }
7090         }
7091     };
7092 }();
7093
7094 /**
7095  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
7096  * @param {String} path The selector/xpath query
7097  * @param {Node} root (optional) The start of the query (defaults to document).
7098  * @return {Array}
7099  * @member Roo
7100  * @method query
7101  */
7102 Roo.query = Roo.DomQuery.select;
7103 /*
7104  * Based on:
7105  * Ext JS Library 1.1.1
7106  * Copyright(c) 2006-2007, Ext JS, LLC.
7107  *
7108  * Originally Released Under LGPL - original licence link has changed is not relivant.
7109  *
7110  * Fork - LGPL
7111  * <script type="text/javascript">
7112  */
7113
7114 /**
7115  * @class Roo.util.Observable
7116  * Base class that provides a common interface for publishing events. Subclasses are expected to
7117  * to have a property "events" with all the events defined.<br>
7118  * For example:
7119  * <pre><code>
7120  Employee = function(name){
7121     this.name = name;
7122     this.addEvents({
7123         "fired" : true,
7124         "quit" : true
7125     });
7126  }
7127  Roo.extend(Employee, Roo.util.Observable);
7128 </code></pre>
7129  * @param {Object} config properties to use (incuding events / listeners)
7130  */
7131
7132 Roo.util.Observable = function(cfg){
7133     
7134     cfg = cfg|| {};
7135     this.addEvents(cfg.events || {});
7136     if (cfg.events) {
7137         delete cfg.events; // make sure
7138     }
7139      
7140     Roo.apply(this, cfg);
7141     
7142     if(this.listeners){
7143         this.on(this.listeners);
7144         delete this.listeners;
7145     }
7146 };
7147 Roo.util.Observable.prototype = {
7148     /** 
7149  * @cfg {Object} listeners  list of events and functions to call for this object, 
7150  * For example :
7151  * <pre><code>
7152     listeners :  { 
7153        'click' : function(e) {
7154            ..... 
7155         } ,
7156         .... 
7157     } 
7158   </code></pre>
7159  */
7160     
7161     
7162     /**
7163      * Fires the specified event with the passed parameters (minus the event name).
7164      * @param {String} eventName
7165      * @param {Object...} args Variable number of parameters are passed to handlers
7166      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
7167      */
7168     fireEvent : function(){
7169         var ce = this.events[arguments[0].toLowerCase()];
7170         if(typeof ce == "object"){
7171             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
7172         }else{
7173             return true;
7174         }
7175     },
7176
7177     // private
7178     filterOptRe : /^(?:scope|delay|buffer|single)$/,
7179
7180     /**
7181      * Appends an event handler to this component
7182      * @param {String}   eventName The type of event to listen for
7183      * @param {Function} handler The method the event invokes
7184      * @param {Object}   scope (optional) The scope in which to execute the handler
7185      * function. The handler function's "this" context.
7186      * @param {Object}   options (optional) An object containing handler configuration
7187      * properties. This may contain any of the following properties:<ul>
7188      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7189      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7190      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7191      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7192      * by the specified number of milliseconds. If the event fires again within that time, the original
7193      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7194      * </ul><br>
7195      * <p>
7196      * <b>Combining Options</b><br>
7197      * Using the options argument, it is possible to combine different types of listeners:<br>
7198      * <br>
7199      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
7200                 <pre><code>
7201                 el.on('click', this.onClick, this, {
7202                         single: true,
7203                 delay: 100,
7204                 forumId: 4
7205                 });
7206                 </code></pre>
7207      * <p>
7208      * <b>Attaching multiple handlers in 1 call</b><br>
7209      * The method also allows for a single argument to be passed which is a config object containing properties
7210      * which specify multiple handlers.
7211      * <pre><code>
7212                 el.on({
7213                         'click': {
7214                         fn: this.onClick,
7215                         scope: this,
7216                         delay: 100
7217                 }, 
7218                 'mouseover': {
7219                         fn: this.onMouseOver,
7220                         scope: this
7221                 },
7222                 'mouseout': {
7223                         fn: this.onMouseOut,
7224                         scope: this
7225                 }
7226                 });
7227                 </code></pre>
7228      * <p>
7229      * Or a shorthand syntax which passes the same scope object to all handlers:
7230         <pre><code>
7231                 el.on({
7232                         'click': this.onClick,
7233                 'mouseover': this.onMouseOver,
7234                 'mouseout': this.onMouseOut,
7235                 scope: this
7236                 });
7237                 </code></pre>
7238      */
7239     addListener : function(eventName, fn, scope, o){
7240         if(typeof eventName == "object"){
7241             o = eventName;
7242             for(var e in o){
7243                 if(this.filterOptRe.test(e)){
7244                     continue;
7245                 }
7246                 if(typeof o[e] == "function"){
7247                     // shared options
7248                     this.addListener(e, o[e], o.scope,  o);
7249                 }else{
7250                     // individual options
7251                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
7252                 }
7253             }
7254             return;
7255         }
7256         o = (!o || typeof o == "boolean") ? {} : o;
7257         eventName = eventName.toLowerCase();
7258         var ce = this.events[eventName] || true;
7259         if(typeof ce == "boolean"){
7260             ce = new Roo.util.Event(this, eventName);
7261             this.events[eventName] = ce;
7262         }
7263         ce.addListener(fn, scope, o);
7264     },
7265
7266     /**
7267      * Removes a listener
7268      * @param {String}   eventName     The type of event to listen for
7269      * @param {Function} handler        The handler to remove
7270      * @param {Object}   scope  (optional) The scope (this object) for the handler
7271      */
7272     removeListener : function(eventName, fn, scope){
7273         var ce = this.events[eventName.toLowerCase()];
7274         if(typeof ce == "object"){
7275             ce.removeListener(fn, scope);
7276         }
7277     },
7278
7279     /**
7280      * Removes all listeners for this object
7281      */
7282     purgeListeners : function(){
7283         for(var evt in this.events){
7284             if(typeof this.events[evt] == "object"){
7285                  this.events[evt].clearListeners();
7286             }
7287         }
7288     },
7289
7290     relayEvents : function(o, events){
7291         var createHandler = function(ename){
7292             return function(){
7293                  
7294                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
7295             };
7296         };
7297         for(var i = 0, len = events.length; i < len; i++){
7298             var ename = events[i];
7299             if(!this.events[ename]){
7300                 this.events[ename] = true;
7301             };
7302             o.on(ename, createHandler(ename), this);
7303         }
7304     },
7305
7306     /**
7307      * Used to define events on this Observable
7308      * @param {Object} object The object with the events defined
7309      */
7310     addEvents : function(o){
7311         if(!this.events){
7312             this.events = {};
7313         }
7314         Roo.applyIf(this.events, o);
7315     },
7316
7317     /**
7318      * Checks to see if this object has any listeners for a specified event
7319      * @param {String} eventName The name of the event to check for
7320      * @return {Boolean} True if the event is being listened for, else false
7321      */
7322     hasListener : function(eventName){
7323         var e = this.events[eventName];
7324         return typeof e == "object" && e.listeners.length > 0;
7325     }
7326 };
7327 /**
7328  * Appends an event handler to this element (shorthand for addListener)
7329  * @param {String}   eventName     The type of event to listen for
7330  * @param {Function} handler        The method the event invokes
7331  * @param {Object}   scope (optional) The scope in which to execute the handler
7332  * function. The handler function's "this" context.
7333  * @param {Object}   options  (optional)
7334  * @method
7335  */
7336 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
7337 /**
7338  * Removes a listener (shorthand for removeListener)
7339  * @param {String}   eventName     The type of event to listen for
7340  * @param {Function} handler        The handler to remove
7341  * @param {Object}   scope  (optional) The scope (this object) for the handler
7342  * @method
7343  */
7344 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
7345
7346 /**
7347  * Starts capture on the specified Observable. All events will be passed
7348  * to the supplied function with the event name + standard signature of the event
7349  * <b>before</b> the event is fired. If the supplied function returns false,
7350  * the event will not fire.
7351  * @param {Observable} o The Observable to capture
7352  * @param {Function} fn The function to call
7353  * @param {Object} scope (optional) The scope (this object) for the fn
7354  * @static
7355  */
7356 Roo.util.Observable.capture = function(o, fn, scope){
7357     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
7358 };
7359
7360 /**
7361  * Removes <b>all</b> added captures from the Observable.
7362  * @param {Observable} o The Observable to release
7363  * @static
7364  */
7365 Roo.util.Observable.releaseCapture = function(o){
7366     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
7367 };
7368
7369 (function(){
7370
7371     var createBuffered = function(h, o, scope){
7372         var task = new Roo.util.DelayedTask();
7373         return function(){
7374             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
7375         };
7376     };
7377
7378     var createSingle = function(h, e, fn, scope){
7379         return function(){
7380             e.removeListener(fn, scope);
7381             return h.apply(scope, arguments);
7382         };
7383     };
7384
7385     var createDelayed = function(h, o, scope){
7386         return function(){
7387             var args = Array.prototype.slice.call(arguments, 0);
7388             setTimeout(function(){
7389                 h.apply(scope, args);
7390             }, o.delay || 10);
7391         };
7392     };
7393
7394     Roo.util.Event = function(obj, name){
7395         this.name = name;
7396         this.obj = obj;
7397         this.listeners = [];
7398     };
7399
7400     Roo.util.Event.prototype = {
7401         addListener : function(fn, scope, options){
7402             var o = options || {};
7403             scope = scope || this.obj;
7404             if(!this.isListening(fn, scope)){
7405                 var l = {fn: fn, scope: scope, options: o};
7406                 var h = fn;
7407                 if(o.delay){
7408                     h = createDelayed(h, o, scope);
7409                 }
7410                 if(o.single){
7411                     h = createSingle(h, this, fn, scope);
7412                 }
7413                 if(o.buffer){
7414                     h = createBuffered(h, o, scope);
7415                 }
7416                 l.fireFn = h;
7417                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
7418                     this.listeners.push(l);
7419                 }else{
7420                     this.listeners = this.listeners.slice(0);
7421                     this.listeners.push(l);
7422                 }
7423             }
7424         },
7425
7426         findListener : function(fn, scope){
7427             scope = scope || this.obj;
7428             var ls = this.listeners;
7429             for(var i = 0, len = ls.length; i < len; i++){
7430                 var l = ls[i];
7431                 if(l.fn == fn && l.scope == scope){
7432                     return i;
7433                 }
7434             }
7435             return -1;
7436         },
7437
7438         isListening : function(fn, scope){
7439             return this.findListener(fn, scope) != -1;
7440         },
7441
7442         removeListener : function(fn, scope){
7443             var index;
7444             if((index = this.findListener(fn, scope)) != -1){
7445                 if(!this.firing){
7446                     this.listeners.splice(index, 1);
7447                 }else{
7448                     this.listeners = this.listeners.slice(0);
7449                     this.listeners.splice(index, 1);
7450                 }
7451                 return true;
7452             }
7453             return false;
7454         },
7455
7456         clearListeners : function(){
7457             this.listeners = [];
7458         },
7459
7460         fire : function(){
7461             var ls = this.listeners, scope, len = ls.length;
7462             if(len > 0){
7463                 this.firing = true;
7464                 var args = Array.prototype.slice.call(arguments, 0);                
7465                 for(var i = 0; i < len; i++){
7466                     var l = ls[i];
7467                     if(l.fireFn.apply(l.scope||this.obj||window, args) === false){
7468                         this.firing = false;
7469                         return false;
7470                     }
7471                 }
7472                 this.firing = false;
7473             }
7474             return true;
7475         }
7476     };
7477 })();/*
7478  * RooJS Library 
7479  * Copyright(c) 2007-2017, Roo J Solutions Ltd
7480  *
7481  * Licence LGPL 
7482  *
7483  */
7484  
7485 /**
7486  * @class Roo.Document
7487  * @extends Roo.util.Observable
7488  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
7489  * 
7490  * @param {Object} config the methods and properties of the 'base' class for the application.
7491  * 
7492  *  Generic Page handler - implement this to start your app..
7493  * 
7494  * eg.
7495  *  MyProject = new Roo.Document({
7496         events : {
7497             'load' : true // your events..
7498         },
7499         listeners : {
7500             'ready' : function() {
7501                 // fired on Roo.onReady()
7502             }
7503         }
7504  * 
7505  */
7506 Roo.Document = function(cfg) {
7507      
7508     this.addEvents({ 
7509         'ready' : true
7510     });
7511     Roo.util.Observable.call(this,cfg);
7512     
7513     var _this = this;
7514     
7515     Roo.onReady(function() {
7516         _this.fireEvent('ready');
7517     },null,false);
7518     
7519     
7520 }
7521
7522 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
7523  * Based on:
7524  * Ext JS Library 1.1.1
7525  * Copyright(c) 2006-2007, Ext JS, LLC.
7526  *
7527  * Originally Released Under LGPL - original licence link has changed is not relivant.
7528  *
7529  * Fork - LGPL
7530  * <script type="text/javascript">
7531  */
7532
7533 /**
7534  * @class Roo.EventManager
7535  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
7536  * several useful events directly.
7537  * See {@link Roo.EventObject} for more details on normalized event objects.
7538  * @static
7539  */
7540 Roo.EventManager = function(){
7541     var docReadyEvent, docReadyProcId, docReadyState = false;
7542     var resizeEvent, resizeTask, textEvent, textSize;
7543     var E = Roo.lib.Event;
7544     var D = Roo.lib.Dom;
7545
7546     
7547     
7548
7549     var fireDocReady = function(){
7550         if(!docReadyState){
7551             docReadyState = true;
7552             Roo.isReady = true;
7553             if(docReadyProcId){
7554                 clearInterval(docReadyProcId);
7555             }
7556             if(Roo.isGecko || Roo.isOpera) {
7557                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
7558             }
7559             if(Roo.isIE){
7560                 var defer = document.getElementById("ie-deferred-loader");
7561                 if(defer){
7562                     defer.onreadystatechange = null;
7563                     defer.parentNode.removeChild(defer);
7564                 }
7565             }
7566             if(docReadyEvent){
7567                 docReadyEvent.fire();
7568                 docReadyEvent.clearListeners();
7569             }
7570         }
7571     };
7572     
7573     var initDocReady = function(){
7574         docReadyEvent = new Roo.util.Event();
7575         if(Roo.isGecko || Roo.isOpera) {
7576             document.addEventListener("DOMContentLoaded", fireDocReady, false);
7577         }else if(Roo.isIE){
7578             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
7579             var defer = document.getElementById("ie-deferred-loader");
7580             defer.onreadystatechange = function(){
7581                 if(this.readyState == "complete"){
7582                     fireDocReady();
7583                 }
7584             };
7585         }else if(Roo.isSafari){ 
7586             docReadyProcId = setInterval(function(){
7587                 var rs = document.readyState;
7588                 if(rs == "complete") {
7589                     fireDocReady();     
7590                  }
7591             }, 10);
7592         }
7593         // no matter what, make sure it fires on load
7594         E.on(window, "load", fireDocReady);
7595     };
7596
7597     var createBuffered = function(h, o){
7598         var task = new Roo.util.DelayedTask(h);
7599         return function(e){
7600             // create new event object impl so new events don't wipe out properties
7601             e = new Roo.EventObjectImpl(e);
7602             task.delay(o.buffer, h, null, [e]);
7603         };
7604     };
7605
7606     var createSingle = function(h, el, ename, fn){
7607         return function(e){
7608             Roo.EventManager.removeListener(el, ename, fn);
7609             h(e);
7610         };
7611     };
7612
7613     var createDelayed = function(h, o){
7614         return function(e){
7615             // create new event object impl so new events don't wipe out properties
7616             e = new Roo.EventObjectImpl(e);
7617             setTimeout(function(){
7618                 h(e);
7619             }, o.delay || 10);
7620         };
7621     };
7622     var transitionEndVal = false;
7623     
7624     var transitionEnd = function()
7625     {
7626         if (transitionEndVal) {
7627             return transitionEndVal;
7628         }
7629         var el = document.createElement('div');
7630
7631         var transEndEventNames = {
7632             WebkitTransition : 'webkitTransitionEnd',
7633             MozTransition    : 'transitionend',
7634             OTransition      : 'oTransitionEnd otransitionend',
7635             transition       : 'transitionend'
7636         };
7637     
7638         for (var name in transEndEventNames) {
7639             if (el.style[name] !== undefined) {
7640                 transitionEndVal = transEndEventNames[name];
7641                 return  transitionEndVal ;
7642             }
7643         }
7644     }
7645     
7646   
7647
7648     var listen = function(element, ename, opt, fn, scope)
7649     {
7650         var o = (!opt || typeof opt == "boolean") ? {} : opt;
7651         fn = fn || o.fn; scope = scope || o.scope;
7652         var el = Roo.getDom(element);
7653         
7654         
7655         if(!el){
7656             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
7657         }
7658         
7659         if (ename == 'transitionend') {
7660             ename = transitionEnd();
7661         }
7662         var h = function(e){
7663             e = Roo.EventObject.setEvent(e);
7664             var t;
7665             if(o.delegate){
7666                 t = e.getTarget(o.delegate, el);
7667                 if(!t){
7668                     return;
7669                 }
7670             }else{
7671                 t = e.target;
7672             }
7673             if(o.stopEvent === true){
7674                 e.stopEvent();
7675             }
7676             if(o.preventDefault === true){
7677                e.preventDefault();
7678             }
7679             if(o.stopPropagation === true){
7680                 e.stopPropagation();
7681             }
7682
7683             if(o.normalized === false){
7684                 e = e.browserEvent;
7685             }
7686
7687             fn.call(scope || el, e, t, o);
7688         };
7689         if(o.delay){
7690             h = createDelayed(h, o);
7691         }
7692         if(o.single){
7693             h = createSingle(h, el, ename, fn);
7694         }
7695         if(o.buffer){
7696             h = createBuffered(h, o);
7697         }
7698         
7699         fn._handlers = fn._handlers || [];
7700         
7701         
7702         fn._handlers.push([Roo.id(el), ename, h]);
7703         
7704         
7705          
7706         E.on(el, ename, h); // this adds the actuall listener to the object..
7707         
7708         
7709         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
7710             el.addEventListener("DOMMouseScroll", h, false);
7711             E.on(window, 'unload', function(){
7712                 el.removeEventListener("DOMMouseScroll", h, false);
7713             });
7714         }
7715         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7716             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
7717         }
7718         return h;
7719     };
7720
7721     var stopListening = function(el, ename, fn){
7722         var id = Roo.id(el), hds = fn._handlers, hd = fn;
7723         if(hds){
7724             for(var i = 0, len = hds.length; i < len; i++){
7725                 var h = hds[i];
7726                 if(h[0] == id && h[1] == ename){
7727                     hd = h[2];
7728                     hds.splice(i, 1);
7729                     break;
7730                 }
7731             }
7732         }
7733         E.un(el, ename, hd);
7734         el = Roo.getDom(el);
7735         if(ename == "mousewheel" && el.addEventListener){
7736             el.removeEventListener("DOMMouseScroll", hd, false);
7737         }
7738         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7739             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
7740         }
7741     };
7742
7743     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
7744     
7745     var pub = {
7746         
7747         
7748         /** 
7749          * Fix for doc tools
7750          * @scope Roo.EventManager
7751          */
7752         
7753         
7754         /** 
7755          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
7756          * object with a Roo.EventObject
7757          * @param {Function} fn        The method the event invokes
7758          * @param {Object}   scope    An object that becomes the scope of the handler
7759          * @param {boolean}  override If true, the obj passed in becomes
7760          *                             the execution scope of the listener
7761          * @return {Function} The wrapped function
7762          * @deprecated
7763          */
7764         wrap : function(fn, scope, override){
7765             return function(e){
7766                 Roo.EventObject.setEvent(e);
7767                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
7768             };
7769         },
7770         
7771         /**
7772      * Appends an event handler to an element (shorthand for addListener)
7773      * @param {String/HTMLElement}   element        The html element or id to assign the
7774      * @param {String}   eventName The type of event to listen for
7775      * @param {Function} handler The method the event invokes
7776      * @param {Object}   scope (optional) The scope in which to execute the handler
7777      * function. The handler function's "this" context.
7778      * @param {Object}   options (optional) An object containing handler configuration
7779      * properties. This may contain any of the following properties:<ul>
7780      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7781      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7782      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7783      * <li>preventDefault {Boolean} True to prevent the default action</li>
7784      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7785      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7786      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7787      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7788      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7789      * by the specified number of milliseconds. If the event fires again within that time, the original
7790      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7791      * </ul><br>
7792      * <p>
7793      * <b>Combining Options</b><br>
7794      * Using the options argument, it is possible to combine different types of listeners:<br>
7795      * <br>
7796      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7797      * Code:<pre><code>
7798 el.on('click', this.onClick, this, {
7799     single: true,
7800     delay: 100,
7801     stopEvent : true,
7802     forumId: 4
7803 });</code></pre>
7804      * <p>
7805      * <b>Attaching multiple handlers in 1 call</b><br>
7806       * The method also allows for a single argument to be passed which is a config object containing properties
7807      * which specify multiple handlers.
7808      * <p>
7809      * Code:<pre><code>
7810 el.on({
7811     'click' : {
7812         fn: this.onClick
7813         scope: this,
7814         delay: 100
7815     },
7816     'mouseover' : {
7817         fn: this.onMouseOver
7818         scope: this
7819     },
7820     'mouseout' : {
7821         fn: this.onMouseOut
7822         scope: this
7823     }
7824 });</code></pre>
7825      * <p>
7826      * Or a shorthand syntax:<br>
7827      * Code:<pre><code>
7828 el.on({
7829     'click' : this.onClick,
7830     'mouseover' : this.onMouseOver,
7831     'mouseout' : this.onMouseOut
7832     scope: this
7833 });</code></pre>
7834      */
7835         addListener : function(element, eventName, fn, scope, options){
7836             if(typeof eventName == "object"){
7837                 var o = eventName;
7838                 for(var e in o){
7839                     if(propRe.test(e)){
7840                         continue;
7841                     }
7842                     if(typeof o[e] == "function"){
7843                         // shared options
7844                         listen(element, e, o, o[e], o.scope);
7845                     }else{
7846                         // individual options
7847                         listen(element, e, o[e]);
7848                     }
7849                 }
7850                 return;
7851             }
7852             return listen(element, eventName, options, fn, scope);
7853         },
7854         
7855         /**
7856          * Removes an event handler
7857          *
7858          * @param {String/HTMLElement}   element        The id or html element to remove the 
7859          *                             event from
7860          * @param {String}   eventName     The type of event
7861          * @param {Function} fn
7862          * @return {Boolean} True if a listener was actually removed
7863          */
7864         removeListener : function(element, eventName, fn){
7865             return stopListening(element, eventName, fn);
7866         },
7867         
7868         /**
7869          * Fires when the document is ready (before onload and before images are loaded). Can be 
7870          * accessed shorthanded Roo.onReady().
7871          * @param {Function} fn        The method the event invokes
7872          * @param {Object}   scope    An  object that becomes the scope of the handler
7873          * @param {boolean}  options
7874          */
7875         onDocumentReady : function(fn, scope, options){
7876             if(docReadyState){ // if it already fired
7877                 docReadyEvent.addListener(fn, scope, options);
7878                 docReadyEvent.fire();
7879                 docReadyEvent.clearListeners();
7880                 return;
7881             }
7882             if(!docReadyEvent){
7883                 initDocReady();
7884             }
7885             docReadyEvent.addListener(fn, scope, options);
7886         },
7887         
7888         /**
7889          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
7890          * @param {Function} fn        The method the event invokes
7891          * @param {Object}   scope    An object that becomes the scope of the handler
7892          * @param {boolean}  options
7893          */
7894         onWindowResize : function(fn, scope, options)
7895         {
7896             if(!resizeEvent){
7897                 resizeEvent = new Roo.util.Event();
7898                 resizeTask = new Roo.util.DelayedTask(function(){
7899                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7900                 });
7901                 E.on(window, "resize", function()
7902                 {
7903                     if (Roo.isIE) {
7904                         resizeTask.delay(50);
7905                     } else {
7906                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7907                     }
7908                 });
7909             }
7910             resizeEvent.addListener(fn, scope, options);
7911         },
7912
7913         /**
7914          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
7915          * @param {Function} fn        The method the event invokes
7916          * @param {Object}   scope    An object that becomes the scope of the handler
7917          * @param {boolean}  options
7918          */
7919         onTextResize : function(fn, scope, options){
7920             if(!textEvent){
7921                 textEvent = new Roo.util.Event();
7922                 var textEl = new Roo.Element(document.createElement('div'));
7923                 textEl.dom.className = 'x-text-resize';
7924                 textEl.dom.innerHTML = 'X';
7925                 textEl.appendTo(document.body);
7926                 textSize = textEl.dom.offsetHeight;
7927                 setInterval(function(){
7928                     if(textEl.dom.offsetHeight != textSize){
7929                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
7930                     }
7931                 }, this.textResizeInterval);
7932             }
7933             textEvent.addListener(fn, scope, options);
7934         },
7935
7936         /**
7937          * Removes the passed window resize listener.
7938          * @param {Function} fn        The method the event invokes
7939          * @param {Object}   scope    The scope of handler
7940          */
7941         removeResizeListener : function(fn, scope){
7942             if(resizeEvent){
7943                 resizeEvent.removeListener(fn, scope);
7944             }
7945         },
7946
7947         // private
7948         fireResize : function(){
7949             if(resizeEvent){
7950                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7951             }   
7952         },
7953         /**
7954          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
7955          */
7956         ieDeferSrc : false,
7957         /**
7958          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
7959          */
7960         textResizeInterval : 50
7961     };
7962     
7963     /**
7964      * Fix for doc tools
7965      * @scopeAlias pub=Roo.EventManager
7966      */
7967     
7968      /**
7969      * Appends an event handler to an element (shorthand for addListener)
7970      * @param {String/HTMLElement}   element        The html element or id to assign the
7971      * @param {String}   eventName The type of event to listen for
7972      * @param {Function} handler The method the event invokes
7973      * @param {Object}   scope (optional) The scope in which to execute the handler
7974      * function. The handler function's "this" context.
7975      * @param {Object}   options (optional) An object containing handler configuration
7976      * properties. This may contain any of the following properties:<ul>
7977      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7978      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7979      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7980      * <li>preventDefault {Boolean} True to prevent the default action</li>
7981      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7982      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7983      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7984      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7985      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7986      * by the specified number of milliseconds. If the event fires again within that time, the original
7987      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7988      * </ul><br>
7989      * <p>
7990      * <b>Combining Options</b><br>
7991      * Using the options argument, it is possible to combine different types of listeners:<br>
7992      * <br>
7993      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7994      * Code:<pre><code>
7995 el.on('click', this.onClick, this, {
7996     single: true,
7997     delay: 100,
7998     stopEvent : true,
7999     forumId: 4
8000 });</code></pre>
8001      * <p>
8002      * <b>Attaching multiple handlers in 1 call</b><br>
8003       * The method also allows for a single argument to be passed which is a config object containing properties
8004      * which specify multiple handlers.
8005      * <p>
8006      * Code:<pre><code>
8007 el.on({
8008     'click' : {
8009         fn: this.onClick
8010         scope: this,
8011         delay: 100
8012     },
8013     'mouseover' : {
8014         fn: this.onMouseOver
8015         scope: this
8016     },
8017     'mouseout' : {
8018         fn: this.onMouseOut
8019         scope: this
8020     }
8021 });</code></pre>
8022      * <p>
8023      * Or a shorthand syntax:<br>
8024      * Code:<pre><code>
8025 el.on({
8026     'click' : this.onClick,
8027     'mouseover' : this.onMouseOver,
8028     'mouseout' : this.onMouseOut
8029     scope: this
8030 });</code></pre>
8031      */
8032     pub.on = pub.addListener;
8033     pub.un = pub.removeListener;
8034
8035     pub.stoppedMouseDownEvent = new Roo.util.Event();
8036     return pub;
8037 }();
8038 /**
8039   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
8040   * @param {Function} fn        The method the event invokes
8041   * @param {Object}   scope    An  object that becomes the scope of the handler
8042   * @param {boolean}  override If true, the obj passed in becomes
8043   *                             the execution scope of the listener
8044   * @member Roo
8045   * @method onReady
8046  */
8047 Roo.onReady = Roo.EventManager.onDocumentReady;
8048
8049 Roo.onReady(function(){
8050     var bd = Roo.get(document.body);
8051     if(!bd){ return; }
8052
8053     var cls = [
8054             Roo.isIE ? "roo-ie"
8055             : Roo.isIE11 ? "roo-ie11"
8056             : Roo.isEdge ? "roo-edge"
8057             : Roo.isGecko ? "roo-gecko"
8058             : Roo.isOpera ? "roo-opera"
8059             : Roo.isSafari ? "roo-safari" : ""];
8060
8061     if(Roo.isMac){
8062         cls.push("roo-mac");
8063     }
8064     if(Roo.isLinux){
8065         cls.push("roo-linux");
8066     }
8067     if(Roo.isIOS){
8068         cls.push("roo-ios");
8069     }
8070     if(Roo.isTouch){
8071         cls.push("roo-touch");
8072     }
8073     if(Roo.isBorderBox){
8074         cls.push('roo-border-box');
8075     }
8076     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
8077         var p = bd.dom.parentNode;
8078         if(p){
8079             p.className += ' roo-strict';
8080         }
8081     }
8082     bd.addClass(cls.join(' '));
8083 });
8084
8085 /**
8086  * @class Roo.EventObject
8087  * EventObject exposes the Yahoo! UI Event functionality directly on the object
8088  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
8089  * Example:
8090  * <pre><code>
8091  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
8092     e.preventDefault();
8093     var target = e.getTarget();
8094     ...
8095  }
8096  var myDiv = Roo.get("myDiv");
8097  myDiv.on("click", handleClick);
8098  //or
8099  Roo.EventManager.on("myDiv", 'click', handleClick);
8100  Roo.EventManager.addListener("myDiv", 'click', handleClick);
8101  </code></pre>
8102  * @static
8103  */
8104 Roo.EventObject = function(){
8105     
8106     var E = Roo.lib.Event;
8107     
8108     // safari keypress events for special keys return bad keycodes
8109     var safariKeys = {
8110         63234 : 37, // left
8111         63235 : 39, // right
8112         63232 : 38, // up
8113         63233 : 40, // down
8114         63276 : 33, // page up
8115         63277 : 34, // page down
8116         63272 : 46, // delete
8117         63273 : 36, // home
8118         63275 : 35  // end
8119     };
8120
8121     // normalize button clicks
8122     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
8123                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
8124
8125     Roo.EventObjectImpl = function(e){
8126         if(e){
8127             this.setEvent(e.browserEvent || e);
8128         }
8129     };
8130     Roo.EventObjectImpl.prototype = {
8131         /**
8132          * Used to fix doc tools.
8133          * @scope Roo.EventObject.prototype
8134          */
8135             
8136
8137         
8138         
8139         /** The normal browser event */
8140         browserEvent : null,
8141         /** The button pressed in a mouse event */
8142         button : -1,
8143         /** True if the shift key was down during the event */
8144         shiftKey : false,
8145         /** True if the control key was down during the event */
8146         ctrlKey : false,
8147         /** True if the alt key was down during the event */
8148         altKey : false,
8149
8150         /** Key constant 
8151         * @type Number */
8152         BACKSPACE : 8,
8153         /** Key constant 
8154         * @type Number */
8155         TAB : 9,
8156         /** Key constant 
8157         * @type Number */
8158         RETURN : 13,
8159         /** Key constant 
8160         * @type Number */
8161         ENTER : 13,
8162         /** Key constant 
8163         * @type Number */
8164         SHIFT : 16,
8165         /** Key constant 
8166         * @type Number */
8167         CONTROL : 17,
8168         /** Key constant 
8169         * @type Number */
8170         ESC : 27,
8171         /** Key constant 
8172         * @type Number */
8173         SPACE : 32,
8174         /** Key constant 
8175         * @type Number */
8176         PAGEUP : 33,
8177         /** Key constant 
8178         * @type Number */
8179         PAGEDOWN : 34,
8180         /** Key constant 
8181         * @type Number */
8182         END : 35,
8183         /** Key constant 
8184         * @type Number */
8185         HOME : 36,
8186         /** Key constant 
8187         * @type Number */
8188         LEFT : 37,
8189         /** Key constant 
8190         * @type Number */
8191         UP : 38,
8192         /** Key constant 
8193         * @type Number */
8194         RIGHT : 39,
8195         /** Key constant 
8196         * @type Number */
8197         DOWN : 40,
8198         /** Key constant 
8199         * @type Number */
8200         DELETE : 46,
8201         /** Key constant 
8202         * @type Number */
8203         F5 : 116,
8204
8205            /** @private */
8206         setEvent : function(e){
8207             if(e == this || (e && e.browserEvent)){ // already wrapped
8208                 return e;
8209             }
8210             this.browserEvent = e;
8211             if(e){
8212                 // normalize buttons
8213                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
8214                 if(e.type == 'click' && this.button == -1){
8215                     this.button = 0;
8216                 }
8217                 this.type = e.type;
8218                 this.shiftKey = e.shiftKey;
8219                 // mac metaKey behaves like ctrlKey
8220                 this.ctrlKey = e.ctrlKey || e.metaKey;
8221                 this.altKey = e.altKey;
8222                 // in getKey these will be normalized for the mac
8223                 this.keyCode = e.keyCode;
8224                 // keyup warnings on firefox.
8225                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
8226                 // cache the target for the delayed and or buffered events
8227                 this.target = E.getTarget(e);
8228                 // same for XY
8229                 this.xy = E.getXY(e);
8230             }else{
8231                 this.button = -1;
8232                 this.shiftKey = false;
8233                 this.ctrlKey = false;
8234                 this.altKey = false;
8235                 this.keyCode = 0;
8236                 this.charCode =0;
8237                 this.target = null;
8238                 this.xy = [0, 0];
8239             }
8240             return this;
8241         },
8242
8243         /**
8244          * Stop the event (preventDefault and stopPropagation)
8245          */
8246         stopEvent : function(){
8247             if(this.browserEvent){
8248                 if(this.browserEvent.type == 'mousedown'){
8249                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
8250                 }
8251                 E.stopEvent(this.browserEvent);
8252             }
8253         },
8254
8255         /**
8256          * Prevents the browsers default handling of the event.
8257          */
8258         preventDefault : function(){
8259             if(this.browserEvent){
8260                 E.preventDefault(this.browserEvent);
8261             }
8262         },
8263
8264         /** @private */
8265         isNavKeyPress : function(){
8266             var k = this.keyCode;
8267             k = Roo.isSafari ? (safariKeys[k] || k) : k;
8268             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
8269         },
8270
8271         isSpecialKey : function(){
8272             var k = this.keyCode;
8273             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
8274             (k == 16) || (k == 17) ||
8275             (k >= 18 && k <= 20) ||
8276             (k >= 33 && k <= 35) ||
8277             (k >= 36 && k <= 39) ||
8278             (k >= 44 && k <= 45);
8279         },
8280         /**
8281          * Cancels bubbling of the event.
8282          */
8283         stopPropagation : function(){
8284             if(this.browserEvent){
8285                 if(this.type == 'mousedown'){
8286                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
8287                 }
8288                 E.stopPropagation(this.browserEvent);
8289             }
8290         },
8291
8292         /**
8293          * Gets the key code for the event.
8294          * @return {Number}
8295          */
8296         getCharCode : function(){
8297             return this.charCode || this.keyCode;
8298         },
8299
8300         /**
8301          * Returns a normalized keyCode for the event.
8302          * @return {Number} The key code
8303          */
8304         getKey : function(){
8305             var k = this.keyCode || this.charCode;
8306             return Roo.isSafari ? (safariKeys[k] || k) : k;
8307         },
8308
8309         /**
8310          * Gets the x coordinate of the event.
8311          * @return {Number}
8312          */
8313         getPageX : function(){
8314             return this.xy[0];
8315         },
8316
8317         /**
8318          * Gets the y coordinate of the event.
8319          * @return {Number}
8320          */
8321         getPageY : function(){
8322             return this.xy[1];
8323         },
8324
8325         /**
8326          * Gets the time of the event.
8327          * @return {Number}
8328          */
8329         getTime : function(){
8330             if(this.browserEvent){
8331                 return E.getTime(this.browserEvent);
8332             }
8333             return null;
8334         },
8335
8336         /**
8337          * Gets the page coordinates of the event.
8338          * @return {Array} The xy values like [x, y]
8339          */
8340         getXY : function(){
8341             return this.xy;
8342         },
8343
8344         /**
8345          * Gets the target for the event.
8346          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
8347          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8348                 search as a number or element (defaults to 10 || document.body)
8349          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8350          * @return {HTMLelement}
8351          */
8352         getTarget : function(selector, maxDepth, returnEl){
8353             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
8354         },
8355         /**
8356          * Gets the related target.
8357          * @return {HTMLElement}
8358          */
8359         getRelatedTarget : function(){
8360             if(this.browserEvent){
8361                 return E.getRelatedTarget(this.browserEvent);
8362             }
8363             return null;
8364         },
8365
8366         /**
8367          * Normalizes mouse wheel delta across browsers
8368          * @return {Number} The delta
8369          */
8370         getWheelDelta : function(){
8371             var e = this.browserEvent;
8372             var delta = 0;
8373             if(e.wheelDelta){ /* IE/Opera. */
8374                 delta = e.wheelDelta/120;
8375             }else if(e.detail){ /* Mozilla case. */
8376                 delta = -e.detail/3;
8377             }
8378             return delta;
8379         },
8380
8381         /**
8382          * Returns true if the control, meta, shift or alt key was pressed during this event.
8383          * @return {Boolean}
8384          */
8385         hasModifier : function(){
8386             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
8387         },
8388
8389         /**
8390          * Returns true if the target of this event equals el or is a child of el
8391          * @param {String/HTMLElement/Element} el
8392          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
8393          * @return {Boolean}
8394          */
8395         within : function(el, related){
8396             var t = this[related ? "getRelatedTarget" : "getTarget"]();
8397             return t && Roo.fly(el).contains(t);
8398         },
8399
8400         getPoint : function(){
8401             return new Roo.lib.Point(this.xy[0], this.xy[1]);
8402         }
8403     };
8404
8405     return new Roo.EventObjectImpl();
8406 }();
8407             
8408     /*
8409  * Based on:
8410  * Ext JS Library 1.1.1
8411  * Copyright(c) 2006-2007, Ext JS, LLC.
8412  *
8413  * Originally Released Under LGPL - original licence link has changed is not relivant.
8414  *
8415  * Fork - LGPL
8416  * <script type="text/javascript">
8417  */
8418
8419  
8420 // was in Composite Element!??!?!
8421  
8422 (function(){
8423     var D = Roo.lib.Dom;
8424     var E = Roo.lib.Event;
8425     var A = Roo.lib.Anim;
8426
8427     // local style camelizing for speed
8428     var propCache = {};
8429     var camelRe = /(-[a-z])/gi;
8430     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
8431     var view = document.defaultView;
8432
8433 /**
8434  * @class Roo.Element
8435  * Represents an Element in the DOM.<br><br>
8436  * Usage:<br>
8437 <pre><code>
8438 var el = Roo.get("my-div");
8439
8440 // or with getEl
8441 var el = getEl("my-div");
8442
8443 // or with a DOM element
8444 var el = Roo.get(myDivElement);
8445 </code></pre>
8446  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
8447  * each call instead of constructing a new one.<br><br>
8448  * <b>Animations</b><br />
8449  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
8450  * should either be a boolean (true) or an object literal with animation options. The animation options are:
8451 <pre>
8452 Option    Default   Description
8453 --------- --------  ---------------------------------------------
8454 duration  .35       The duration of the animation in seconds
8455 easing    easeOut   The YUI easing method
8456 callback  none      A function to execute when the anim completes
8457 scope     this      The scope (this) of the callback function
8458 </pre>
8459 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
8460 * manipulate the animation. Here's an example:
8461 <pre><code>
8462 var el = Roo.get("my-div");
8463
8464 // no animation
8465 el.setWidth(100);
8466
8467 // default animation
8468 el.setWidth(100, true);
8469
8470 // animation with some options set
8471 el.setWidth(100, {
8472     duration: 1,
8473     callback: this.foo,
8474     scope: this
8475 });
8476
8477 // using the "anim" property to get the Anim object
8478 var opt = {
8479     duration: 1,
8480     callback: this.foo,
8481     scope: this
8482 };
8483 el.setWidth(100, opt);
8484 ...
8485 if(opt.anim.isAnimated()){
8486     opt.anim.stop();
8487 }
8488 </code></pre>
8489 * <b> Composite (Collections of) Elements</b><br />
8490  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
8491  * @constructor Create a new Element directly.
8492  * @param {String/HTMLElement} element
8493  * @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).
8494  */
8495     Roo.Element = function(element, forceNew)
8496     {
8497         var dom = typeof element == "string" ?
8498                 document.getElementById(element) : element;
8499         
8500         this.listeners = {};
8501         
8502         if(!dom){ // invalid id/element
8503             return null;
8504         }
8505         var id = dom.id;
8506         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
8507             return Roo.Element.cache[id];
8508         }
8509
8510         /**
8511          * The DOM element
8512          * @type HTMLElement
8513          */
8514         this.dom = dom;
8515
8516         /**
8517          * The DOM element ID
8518          * @type String
8519          */
8520         this.id = id || Roo.id(dom);
8521         
8522         return this; // assumed for cctor?
8523     };
8524
8525     var El = Roo.Element;
8526
8527     El.prototype = {
8528         /**
8529          * The element's default display mode  (defaults to "") 
8530          * @type String
8531          */
8532         originalDisplay : "",
8533
8534         
8535         // note this is overridden in BS version..
8536         visibilityMode : 1, 
8537         /**
8538          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
8539          * @type String
8540          */
8541         defaultUnit : "px",
8542         
8543         /**
8544          * Sets the element's visibility mode. When setVisible() is called it
8545          * will use this to determine whether to set the visibility or the display property.
8546          * @param visMode Element.VISIBILITY or Element.DISPLAY
8547          * @return {Roo.Element} this
8548          */
8549         setVisibilityMode : function(visMode){
8550             this.visibilityMode = visMode;
8551             return this;
8552         },
8553         /**
8554          * Convenience method for setVisibilityMode(Element.DISPLAY)
8555          * @param {String} display (optional) What to set display to when visible
8556          * @return {Roo.Element} this
8557          */
8558         enableDisplayMode : function(display){
8559             this.setVisibilityMode(El.DISPLAY);
8560             if(typeof display != "undefined") { this.originalDisplay = display; }
8561             return this;
8562         },
8563
8564         /**
8565          * 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)
8566          * @param {String} selector The simple selector to test
8567          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8568                 search as a number or element (defaults to 10 || document.body)
8569          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8570          * @return {HTMLElement} The matching DOM node (or null if no match was found)
8571          */
8572         findParent : function(simpleSelector, maxDepth, returnEl){
8573             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
8574             maxDepth = maxDepth || 50;
8575             if(typeof maxDepth != "number"){
8576                 stopEl = Roo.getDom(maxDepth);
8577                 maxDepth = 10;
8578             }
8579             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
8580                 if(dq.is(p, simpleSelector)){
8581                     return returnEl ? Roo.get(p) : p;
8582                 }
8583                 depth++;
8584                 p = p.parentNode;
8585             }
8586             return null;
8587         },
8588
8589
8590         /**
8591          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
8592          * @param {String} selector The simple selector to test
8593          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8594                 search as a number or element (defaults to 10 || document.body)
8595          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8596          * @return {HTMLElement} The matching DOM node (or null if no match was found)
8597          */
8598         findParentNode : function(simpleSelector, maxDepth, returnEl){
8599             var p = Roo.fly(this.dom.parentNode, '_internal');
8600             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
8601         },
8602         
8603         /**
8604          * Looks at  the scrollable parent element
8605          */
8606         findScrollableParent : function()
8607         {
8608             var overflowRegex = /(auto|scroll)/;
8609             
8610             if(this.getStyle('position') === 'fixed'){
8611                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8612             }
8613             
8614             var excludeStaticParent = this.getStyle('position') === "absolute";
8615             
8616             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
8617                 
8618                 if (excludeStaticParent && parent.getStyle('position') === "static") {
8619                     continue;
8620                 }
8621                 
8622                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
8623                     return parent;
8624                 }
8625                 
8626                 if(parent.dom.nodeName.toLowerCase() == 'body'){
8627                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8628                 }
8629             }
8630             
8631             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8632         },
8633
8634         /**
8635          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
8636          * This is a shortcut for findParentNode() that always returns an Roo.Element.
8637          * @param {String} selector The simple selector to test
8638          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8639                 search as a number or element (defaults to 10 || document.body)
8640          * @return {Roo.Element} The matching DOM node (or null if no match was found)
8641          */
8642         up : function(simpleSelector, maxDepth){
8643             return this.findParentNode(simpleSelector, maxDepth, true);
8644         },
8645
8646
8647
8648         /**
8649          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
8650          * @param {String} selector The simple selector to test
8651          * @return {Boolean} True if this element matches the selector, else false
8652          */
8653         is : function(simpleSelector){
8654             return Roo.DomQuery.is(this.dom, simpleSelector);
8655         },
8656
8657         /**
8658          * Perform animation on this element.
8659          * @param {Object} args The YUI animation control args
8660          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
8661          * @param {Function} onComplete (optional) Function to call when animation completes
8662          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
8663          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
8664          * @return {Roo.Element} this
8665          */
8666         animate : function(args, duration, onComplete, easing, animType){
8667             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
8668             return this;
8669         },
8670
8671         /*
8672          * @private Internal animation call
8673          */
8674         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
8675             animType = animType || 'run';
8676             opt = opt || {};
8677             var anim = Roo.lib.Anim[animType](
8678                 this.dom, args,
8679                 (opt.duration || defaultDur) || .35,
8680                 (opt.easing || defaultEase) || 'easeOut',
8681                 function(){
8682                     Roo.callback(cb, this);
8683                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
8684                 },
8685                 this
8686             );
8687             opt.anim = anim;
8688             return anim;
8689         },
8690
8691         // private legacy anim prep
8692         preanim : function(a, i){
8693             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
8694         },
8695
8696         /**
8697          * Removes worthless text nodes
8698          * @param {Boolean} forceReclean (optional) By default the element
8699          * keeps track if it has been cleaned already so
8700          * you can call this over and over. However, if you update the element and
8701          * need to force a reclean, you can pass true.
8702          */
8703         clean : function(forceReclean){
8704             if(this.isCleaned && forceReclean !== true){
8705                 return this;
8706             }
8707             var ns = /\S/;
8708             var d = this.dom, n = d.firstChild, ni = -1;
8709             while(n){
8710                 var nx = n.nextSibling;
8711                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
8712                     d.removeChild(n);
8713                 }else{
8714                     n.nodeIndex = ++ni;
8715                 }
8716                 n = nx;
8717             }
8718             this.isCleaned = true;
8719             return this;
8720         },
8721
8722         // private
8723         calcOffsetsTo : function(el){
8724             el = Roo.get(el);
8725             var d = el.dom;
8726             var restorePos = false;
8727             if(el.getStyle('position') == 'static'){
8728                 el.position('relative');
8729                 restorePos = true;
8730             }
8731             var x = 0, y =0;
8732             var op = this.dom;
8733             while(op && op != d && op.tagName != 'HTML'){
8734                 x+= op.offsetLeft;
8735                 y+= op.offsetTop;
8736                 op = op.offsetParent;
8737             }
8738             if(restorePos){
8739                 el.position('static');
8740             }
8741             return [x, y];
8742         },
8743
8744         /**
8745          * Scrolls this element into view within the passed container.
8746          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
8747          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
8748          * @return {Roo.Element} this
8749          */
8750         scrollIntoView : function(container, hscroll){
8751             var c = Roo.getDom(container) || document.body;
8752             var el = this.dom;
8753
8754             var o = this.calcOffsetsTo(c),
8755                 l = o[0],
8756                 t = o[1],
8757                 b = t+el.offsetHeight,
8758                 r = l+el.offsetWidth;
8759
8760             var ch = c.clientHeight;
8761             var ct = parseInt(c.scrollTop, 10);
8762             var cl = parseInt(c.scrollLeft, 10);
8763             var cb = ct + ch;
8764             var cr = cl + c.clientWidth;
8765
8766             if(t < ct){
8767                 c.scrollTop = t;
8768             }else if(b > cb){
8769                 c.scrollTop = b-ch;
8770             }
8771
8772             if(hscroll !== false){
8773                 if(l < cl){
8774                     c.scrollLeft = l;
8775                 }else if(r > cr){
8776                     c.scrollLeft = r-c.clientWidth;
8777                 }
8778             }
8779             return this;
8780         },
8781
8782         // private
8783         scrollChildIntoView : function(child, hscroll){
8784             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
8785         },
8786
8787         /**
8788          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
8789          * the new height may not be available immediately.
8790          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
8791          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
8792          * @param {Function} onComplete (optional) Function to call when animation completes
8793          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
8794          * @return {Roo.Element} this
8795          */
8796         autoHeight : function(animate, duration, onComplete, easing){
8797             var oldHeight = this.getHeight();
8798             this.clip();
8799             this.setHeight(1); // force clipping
8800             setTimeout(function(){
8801                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
8802                 if(!animate){
8803                     this.setHeight(height);
8804                     this.unclip();
8805                     if(typeof onComplete == "function"){
8806                         onComplete();
8807                     }
8808                 }else{
8809                     this.setHeight(oldHeight); // restore original height
8810                     this.setHeight(height, animate, duration, function(){
8811                         this.unclip();
8812                         if(typeof onComplete == "function") { onComplete(); }
8813                     }.createDelegate(this), easing);
8814                 }
8815             }.createDelegate(this), 0);
8816             return this;
8817         },
8818
8819         /**
8820          * Returns true if this element is an ancestor of the passed element
8821          * @param {HTMLElement/String} el The element to check
8822          * @return {Boolean} True if this element is an ancestor of el, else false
8823          */
8824         contains : function(el){
8825             if(!el){return false;}
8826             return D.isAncestor(this.dom, el.dom ? el.dom : el);
8827         },
8828
8829         /**
8830          * Checks whether the element is currently visible using both visibility and display properties.
8831          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
8832          * @return {Boolean} True if the element is currently visible, else false
8833          */
8834         isVisible : function(deep) {
8835             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
8836             if(deep !== true || !vis){
8837                 return vis;
8838             }
8839             var p = this.dom.parentNode;
8840             while(p && p.tagName.toLowerCase() != "body"){
8841                 if(!Roo.fly(p, '_isVisible').isVisible()){
8842                     return false;
8843                 }
8844                 p = p.parentNode;
8845             }
8846             return true;
8847         },
8848
8849         /**
8850          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
8851          * @param {String} selector The CSS selector
8852          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
8853          * @return {CompositeElement/CompositeElementLite} The composite element
8854          */
8855         select : function(selector, unique){
8856             return El.select(selector, unique, this.dom);
8857         },
8858
8859         /**
8860          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
8861          * @param {String} selector The CSS selector
8862          * @return {Array} An array of the matched nodes
8863          */
8864         query : function(selector, unique){
8865             return Roo.DomQuery.select(selector, this.dom);
8866         },
8867
8868         /**
8869          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
8870          * @param {String} selector The CSS selector
8871          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8872          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8873          */
8874         child : function(selector, returnDom){
8875             var n = Roo.DomQuery.selectNode(selector, this.dom);
8876             return returnDom ? n : Roo.get(n);
8877         },
8878
8879         /**
8880          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
8881          * @param {String} selector The CSS selector
8882          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8883          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8884          */
8885         down : function(selector, returnDom){
8886             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
8887             return returnDom ? n : Roo.get(n);
8888         },
8889
8890         /**
8891          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
8892          * @param {String} group The group the DD object is member of
8893          * @param {Object} config The DD config object
8894          * @param {Object} overrides An object containing methods to override/implement on the DD object
8895          * @return {Roo.dd.DD} The DD object
8896          */
8897         initDD : function(group, config, overrides){
8898             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
8899             return Roo.apply(dd, overrides);
8900         },
8901
8902         /**
8903          * Initializes a {@link Roo.dd.DDProxy} object for this element.
8904          * @param {String} group The group the DDProxy object is member of
8905          * @param {Object} config The DDProxy config object
8906          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
8907          * @return {Roo.dd.DDProxy} The DDProxy object
8908          */
8909         initDDProxy : function(group, config, overrides){
8910             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
8911             return Roo.apply(dd, overrides);
8912         },
8913
8914         /**
8915          * Initializes a {@link Roo.dd.DDTarget} object for this element.
8916          * @param {String} group The group the DDTarget object is member of
8917          * @param {Object} config The DDTarget config object
8918          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
8919          * @return {Roo.dd.DDTarget} The DDTarget object
8920          */
8921         initDDTarget : function(group, config, overrides){
8922             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
8923             return Roo.apply(dd, overrides);
8924         },
8925
8926         /**
8927          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
8928          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
8929          * @param {Boolean} visible Whether the element is visible
8930          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8931          * @return {Roo.Element} this
8932          */
8933          setVisible : function(visible, animate){
8934             if(!animate || !A){
8935                 if(this.visibilityMode == El.DISPLAY){
8936                     this.setDisplayed(visible);
8937                 }else{
8938                     this.fixDisplay();
8939                     this.dom.style.visibility = visible ? "visible" : "hidden";
8940                 }
8941             }else{
8942                 // closure for composites
8943                 var dom = this.dom;
8944                 var visMode = this.visibilityMode;
8945                 if(visible){
8946                     this.setOpacity(.01);
8947                     this.setVisible(true);
8948                 }
8949                 this.anim({opacity: { to: (visible?1:0) }},
8950                       this.preanim(arguments, 1),
8951                       null, .35, 'easeIn', function(){
8952                          if(!visible){
8953                              if(visMode == El.DISPLAY){
8954                                  dom.style.display = "none";
8955                              }else{
8956                                  dom.style.visibility = "hidden";
8957                              }
8958                              Roo.get(dom).setOpacity(1);
8959                          }
8960                      });
8961             }
8962             return this;
8963         },
8964
8965         /**
8966          * Returns true if display is not "none"
8967          * @return {Boolean}
8968          */
8969         isDisplayed : function() {
8970             return this.getStyle("display") != "none";
8971         },
8972
8973         /**
8974          * Toggles the element's visibility or display, depending on visibility mode.
8975          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8976          * @return {Roo.Element} this
8977          */
8978         toggle : function(animate){
8979             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
8980             return this;
8981         },
8982
8983         /**
8984          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
8985          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
8986          * @return {Roo.Element} this
8987          */
8988         setDisplayed : function(value) {
8989             if(typeof value == "boolean"){
8990                value = value ? this.originalDisplay : "none";
8991             }
8992             this.setStyle("display", value);
8993             return this;
8994         },
8995
8996         /**
8997          * Tries to focus the element. Any exceptions are caught and ignored.
8998          * @return {Roo.Element} this
8999          */
9000         focus : function() {
9001             try{
9002                 this.dom.focus();
9003             }catch(e){}
9004             return this;
9005         },
9006
9007         /**
9008          * Tries to blur the element. Any exceptions are caught and ignored.
9009          * @return {Roo.Element} this
9010          */
9011         blur : function() {
9012             try{
9013                 this.dom.blur();
9014             }catch(e){}
9015             return this;
9016         },
9017
9018         /**
9019          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
9020          * @param {String/Array} className The CSS class to add, or an array of classes
9021          * @return {Roo.Element} this
9022          */
9023         addClass : function(className){
9024             if(className instanceof Array){
9025                 for(var i = 0, len = className.length; i < len; i++) {
9026                     this.addClass(className[i]);
9027                 }
9028             }else{
9029                 if(className && !this.hasClass(className)){
9030                     if (this.dom instanceof SVGElement) {
9031                         this.dom.className.baseVal =this.dom.className.baseVal  + " " + className;
9032                     } else {
9033                         this.dom.className = this.dom.className + " " + className;
9034                     }
9035                 }
9036             }
9037             return this;
9038         },
9039
9040         /**
9041          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
9042          * @param {String/Array} className The CSS class to add, or an array of classes
9043          * @return {Roo.Element} this
9044          */
9045         radioClass : function(className){
9046             var siblings = this.dom.parentNode.childNodes;
9047             for(var i = 0; i < siblings.length; i++) {
9048                 var s = siblings[i];
9049                 if(s.nodeType == 1){
9050                     Roo.get(s).removeClass(className);
9051                 }
9052             }
9053             this.addClass(className);
9054             return this;
9055         },
9056
9057         /**
9058          * Removes one or more CSS classes from the element.
9059          * @param {String/Array} className The CSS class to remove, or an array of classes
9060          * @return {Roo.Element} this
9061          */
9062         removeClass : function(className){
9063             
9064             var cn = this.dom instanceof SVGElement ? this.dom.className.baseVal : this.dom.className;
9065             if(!className || !cn){
9066                 return this;
9067             }
9068             if(className instanceof Array){
9069                 for(var i = 0, len = className.length; i < len; i++) {
9070                     this.removeClass(className[i]);
9071                 }
9072             }else{
9073                 if(this.hasClass(className)){
9074                     var re = this.classReCache[className];
9075                     if (!re) {
9076                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
9077                        this.classReCache[className] = re;
9078                     }
9079                     if (this.dom instanceof SVGElement) {
9080                         this.dom.className.baseVal = cn.replace(re, " ");
9081                     } else {
9082                         this.dom.className = cn.replace(re, " ");
9083                     }
9084                 }
9085             }
9086             return this;
9087         },
9088
9089         // private
9090         classReCache: {},
9091
9092         /**
9093          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
9094          * @param {String} className The CSS class to toggle
9095          * @return {Roo.Element} this
9096          */
9097         toggleClass : function(className){
9098             if(this.hasClass(className)){
9099                 this.removeClass(className);
9100             }else{
9101                 this.addClass(className);
9102             }
9103             return this;
9104         },
9105
9106         /**
9107          * Checks if the specified CSS class exists on this element's DOM node.
9108          * @param {String} className The CSS class to check for
9109          * @return {Boolean} True if the class exists, else false
9110          */
9111         hasClass : function(className){
9112             if (this.dom instanceof SVGElement) {
9113                 return className && (' '+this.dom.className.baseVal +' ').indexOf(' '+className+' ') != -1; 
9114             } 
9115             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
9116         },
9117
9118         /**
9119          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
9120          * @param {String} oldClassName The CSS class to replace
9121          * @param {String} newClassName The replacement CSS class
9122          * @return {Roo.Element} this
9123          */
9124         replaceClass : function(oldClassName, newClassName){
9125             this.removeClass(oldClassName);
9126             this.addClass(newClassName);
9127             return this;
9128         },
9129
9130         /**
9131          * Returns an object with properties matching the styles requested.
9132          * For example, el.getStyles('color', 'font-size', 'width') might return
9133          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
9134          * @param {String} style1 A style name
9135          * @param {String} style2 A style name
9136          * @param {String} etc.
9137          * @return {Object} The style object
9138          */
9139         getStyles : function(){
9140             var a = arguments, len = a.length, r = {};
9141             for(var i = 0; i < len; i++){
9142                 r[a[i]] = this.getStyle(a[i]);
9143             }
9144             return r;
9145         },
9146
9147         /**
9148          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
9149          * @param {String} property The style property whose value is returned.
9150          * @return {String} The current value of the style property for this element.
9151          */
9152         getStyle : function(){
9153             return view && view.getComputedStyle ?
9154                 function(prop){
9155                     var el = this.dom, v, cs, camel;
9156                     if(prop == 'float'){
9157                         prop = "cssFloat";
9158                     }
9159                     if(el.style && (v = el.style[prop])){
9160                         return v;
9161                     }
9162                     if(cs = view.getComputedStyle(el, "")){
9163                         if(!(camel = propCache[prop])){
9164                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
9165                         }
9166                         return cs[camel];
9167                     }
9168                     return null;
9169                 } :
9170                 function(prop){
9171                     var el = this.dom, v, cs, camel;
9172                     if(prop == 'opacity'){
9173                         if(typeof el.style.filter == 'string'){
9174                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
9175                             if(m){
9176                                 var fv = parseFloat(m[1]);
9177                                 if(!isNaN(fv)){
9178                                     return fv ? fv / 100 : 0;
9179                                 }
9180                             }
9181                         }
9182                         return 1;
9183                     }else if(prop == 'float'){
9184                         prop = "styleFloat";
9185                     }
9186                     if(!(camel = propCache[prop])){
9187                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
9188                     }
9189                     if(v = el.style[camel]){
9190                         return v;
9191                     }
9192                     if(cs = el.currentStyle){
9193                         return cs[camel];
9194                     }
9195                     return null;
9196                 };
9197         }(),
9198
9199         /**
9200          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
9201          * @param {String/Object} property The style property to be set, or an object of multiple styles.
9202          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
9203          * @return {Roo.Element} this
9204          */
9205         setStyle : function(prop, value){
9206             if(typeof prop == "string"){
9207                 
9208                 if (prop == 'float') {
9209                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
9210                     return this;
9211                 }
9212                 
9213                 var camel;
9214                 if(!(camel = propCache[prop])){
9215                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
9216                 }
9217                 
9218                 if(camel == 'opacity') {
9219                     this.setOpacity(value);
9220                 }else{
9221                     this.dom.style[camel] = value;
9222                 }
9223             }else{
9224                 for(var style in prop){
9225                     if(typeof prop[style] != "function"){
9226                        this.setStyle(style, prop[style]);
9227                     }
9228                 }
9229             }
9230             return this;
9231         },
9232
9233         /**
9234          * More flexible version of {@link #setStyle} for setting style properties.
9235          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
9236          * a function which returns such a specification.
9237          * @return {Roo.Element} this
9238          */
9239         applyStyles : function(style){
9240             Roo.DomHelper.applyStyles(this.dom, style);
9241             return this;
9242         },
9243
9244         /**
9245           * 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).
9246           * @return {Number} The X position of the element
9247           */
9248         getX : function(){
9249             return D.getX(this.dom);
9250         },
9251
9252         /**
9253           * 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).
9254           * @return {Number} The Y position of the element
9255           */
9256         getY : function(){
9257             return D.getY(this.dom);
9258         },
9259
9260         /**
9261           * 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).
9262           * @return {Array} The XY position of the element
9263           */
9264         getXY : function(){
9265             return D.getXY(this.dom);
9266         },
9267
9268         /**
9269          * 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).
9270          * @param {Number} The X position of the element
9271          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9272          * @return {Roo.Element} this
9273          */
9274         setX : function(x, animate){
9275             if(!animate || !A){
9276                 D.setX(this.dom, x);
9277             }else{
9278                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
9279             }
9280             return this;
9281         },
9282
9283         /**
9284          * 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).
9285          * @param {Number} The Y position of the element
9286          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9287          * @return {Roo.Element} this
9288          */
9289         setY : function(y, animate){
9290             if(!animate || !A){
9291                 D.setY(this.dom, y);
9292             }else{
9293                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
9294             }
9295             return this;
9296         },
9297
9298         /**
9299          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
9300          * @param {String} left The left CSS property value
9301          * @return {Roo.Element} this
9302          */
9303         setLeft : function(left){
9304             this.setStyle("left", this.addUnits(left));
9305             return this;
9306         },
9307
9308         /**
9309          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
9310          * @param {String} top The top CSS property value
9311          * @return {Roo.Element} this
9312          */
9313         setTop : function(top){
9314             this.setStyle("top", this.addUnits(top));
9315             return this;
9316         },
9317
9318         /**
9319          * Sets the element's CSS right style.
9320          * @param {String} right The right CSS property value
9321          * @return {Roo.Element} this
9322          */
9323         setRight : function(right){
9324             this.setStyle("right", this.addUnits(right));
9325             return this;
9326         },
9327
9328         /**
9329          * Sets the element's CSS bottom style.
9330          * @param {String} bottom The bottom CSS property value
9331          * @return {Roo.Element} this
9332          */
9333         setBottom : function(bottom){
9334             this.setStyle("bottom", this.addUnits(bottom));
9335             return this;
9336         },
9337
9338         /**
9339          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
9340          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9341          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
9342          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9343          * @return {Roo.Element} this
9344          */
9345         setXY : function(pos, animate){
9346             if(!animate || !A){
9347                 D.setXY(this.dom, pos);
9348             }else{
9349                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
9350             }
9351             return this;
9352         },
9353
9354         /**
9355          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
9356          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9357          * @param {Number} x X value for new position (coordinates are page-based)
9358          * @param {Number} y Y value for new position (coordinates are page-based)
9359          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9360          * @return {Roo.Element} this
9361          */
9362         setLocation : function(x, y, animate){
9363             this.setXY([x, y], this.preanim(arguments, 2));
9364             return this;
9365         },
9366
9367         /**
9368          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
9369          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9370          * @param {Number} x X value for new position (coordinates are page-based)
9371          * @param {Number} y Y value for new position (coordinates are page-based)
9372          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9373          * @return {Roo.Element} this
9374          */
9375         moveTo : function(x, y, animate){
9376             this.setXY([x, y], this.preanim(arguments, 2));
9377             return this;
9378         },
9379
9380         /**
9381          * Returns the region of the given element.
9382          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
9383          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
9384          */
9385         getRegion : function(){
9386             return D.getRegion(this.dom);
9387         },
9388
9389         /**
9390          * Returns the offset height of the element
9391          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
9392          * @return {Number} The element's height
9393          */
9394         getHeight : function(contentHeight){
9395             var h = this.dom.offsetHeight || 0;
9396             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
9397         },
9398
9399         /**
9400          * Returns the offset width of the element
9401          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
9402          * @return {Number} The element's width
9403          */
9404         getWidth : function(contentWidth){
9405             var w = this.dom.offsetWidth || 0;
9406             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
9407         },
9408
9409         /**
9410          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
9411          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
9412          * if a height has not been set using CSS.
9413          * @return {Number}
9414          */
9415         getComputedHeight : function(){
9416             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
9417             if(!h){
9418                 h = parseInt(this.getStyle('height'), 10) || 0;
9419                 if(!this.isBorderBox()){
9420                     h += this.getFrameWidth('tb');
9421                 }
9422             }
9423             return h;
9424         },
9425
9426         /**
9427          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
9428          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
9429          * if a width has not been set using CSS.
9430          * @return {Number}
9431          */
9432         getComputedWidth : function(){
9433             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
9434             if(!w){
9435                 w = parseInt(this.getStyle('width'), 10) || 0;
9436                 if(!this.isBorderBox()){
9437                     w += this.getFrameWidth('lr');
9438                 }
9439             }
9440             return w;
9441         },
9442
9443         /**
9444          * Returns the size of the element.
9445          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
9446          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
9447          */
9448         getSize : function(contentSize){
9449             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
9450         },
9451
9452         /**
9453          * Returns the width and height of the viewport.
9454          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
9455          */
9456         getViewSize : function(){
9457             var d = this.dom, doc = document, aw = 0, ah = 0;
9458             if(d == doc || d == doc.body){
9459                 return {width : D.getViewWidth(), height: D.getViewHeight()};
9460             }else{
9461                 return {
9462                     width : d.clientWidth,
9463                     height: d.clientHeight
9464                 };
9465             }
9466         },
9467
9468         /**
9469          * Returns the value of the "value" attribute
9470          * @param {Boolean} asNumber true to parse the value as a number
9471          * @return {String/Number}
9472          */
9473         getValue : function(asNumber){
9474             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
9475         },
9476
9477         // private
9478         adjustWidth : function(width){
9479             if(typeof width == "number"){
9480                 if(this.autoBoxAdjust && !this.isBorderBox()){
9481                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9482                 }
9483                 if(width < 0){
9484                     width = 0;
9485                 }
9486             }
9487             return width;
9488         },
9489
9490         // private
9491         adjustHeight : function(height){
9492             if(typeof height == "number"){
9493                if(this.autoBoxAdjust && !this.isBorderBox()){
9494                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9495                }
9496                if(height < 0){
9497                    height = 0;
9498                }
9499             }
9500             return height;
9501         },
9502
9503         /**
9504          * Set the width of the element
9505          * @param {Number} width The new width
9506          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9507          * @return {Roo.Element} this
9508          */
9509         setWidth : function(width, animate){
9510             width = this.adjustWidth(width);
9511             if(!animate || !A){
9512                 this.dom.style.width = this.addUnits(width);
9513             }else{
9514                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
9515             }
9516             return this;
9517         },
9518
9519         /**
9520          * Set the height of the element
9521          * @param {Number} height The new height
9522          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9523          * @return {Roo.Element} this
9524          */
9525          setHeight : function(height, animate){
9526             height = this.adjustHeight(height);
9527             if(!animate || !A){
9528                 this.dom.style.height = this.addUnits(height);
9529             }else{
9530                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
9531             }
9532             return this;
9533         },
9534
9535         /**
9536          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
9537          * @param {Number} width The new width
9538          * @param {Number} height The new height
9539          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9540          * @return {Roo.Element} this
9541          */
9542          setSize : function(width, height, animate){
9543             if(typeof width == "object"){ // in case of object from getSize()
9544                 height = width.height; width = width.width;
9545             }
9546             width = this.adjustWidth(width); height = this.adjustHeight(height);
9547             if(!animate || !A){
9548                 this.dom.style.width = this.addUnits(width);
9549                 this.dom.style.height = this.addUnits(height);
9550             }else{
9551                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
9552             }
9553             return this;
9554         },
9555
9556         /**
9557          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
9558          * @param {Number} x X value for new position (coordinates are page-based)
9559          * @param {Number} y Y value for new position (coordinates are page-based)
9560          * @param {Number} width The new width
9561          * @param {Number} height The new height
9562          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9563          * @return {Roo.Element} this
9564          */
9565         setBounds : function(x, y, width, height, animate){
9566             if(!animate || !A){
9567                 this.setSize(width, height);
9568                 this.setLocation(x, y);
9569             }else{
9570                 width = this.adjustWidth(width); height = this.adjustHeight(height);
9571                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
9572                               this.preanim(arguments, 4), 'motion');
9573             }
9574             return this;
9575         },
9576
9577         /**
9578          * 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.
9579          * @param {Roo.lib.Region} region The region to fill
9580          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9581          * @return {Roo.Element} this
9582          */
9583         setRegion : function(region, animate){
9584             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
9585             return this;
9586         },
9587
9588         /**
9589          * Appends an event handler
9590          *
9591          * @param {String}   eventName     The type of event to append
9592          * @param {Function} fn        The method the event invokes
9593          * @param {Object} scope       (optional) The scope (this object) of the fn
9594          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9595          */
9596         addListener : function(eventName, fn, scope, options)
9597         {
9598             if (eventName == 'dblclick') { // doublclick (touchstart) - faked on touch.
9599                 this.addListener('touchstart', this.onTapHandler, this);
9600             }
9601             
9602             // we need to handle a special case where dom element is a svg element.
9603             // in this case we do not actua
9604             if (!this.dom) {
9605                 return;
9606             }
9607             
9608             if (this.dom instanceof SVGElement && !(this.dom instanceof SVGSVGElement)) {
9609                 if (typeof(this.listeners[eventName]) == 'undefined') {
9610                     this.listeners[eventName] =  new Roo.util.Event(this, eventName);
9611                 }
9612                 this.listeners[eventName].addListener(fn, scope, options);
9613                 return;
9614             }
9615             
9616                 
9617             Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
9618             
9619             
9620         },
9621         tapedTwice : false,
9622         onTapHandler : function(event)
9623         {
9624             if(!this.tapedTwice) {
9625                 this.tapedTwice = true;
9626                 var s = this;
9627                 setTimeout( function() {
9628                     s.tapedTwice = false;
9629                 }, 300 );
9630                 return;
9631             }
9632             event.preventDefault();
9633             var revent = new MouseEvent('dblclick',  {
9634                 view: window,
9635                 bubbles: true,
9636                 cancelable: true
9637             });
9638              
9639             this.dom.dispatchEvent(revent);
9640             //action on double tap goes below
9641              
9642         }, 
9643  
9644         /**
9645          * Removes an event handler from this element
9646          * @param {String} eventName the type of event to remove
9647          * @param {Function} fn the method the event invokes
9648          * @param {Function} scope (needed for svg fake listeners)
9649          * @return {Roo.Element} this
9650          */
9651         removeListener : function(eventName, fn, scope){
9652             Roo.EventManager.removeListener(this.dom,  eventName, fn);
9653             if (typeof(this.listeners) == 'undefined'  || typeof(this.listeners[eventName]) == 'undefined') {
9654                 return this;
9655             }
9656             this.listeners[eventName].removeListener(fn, scope);
9657             return this;
9658         },
9659
9660         /**
9661          * Removes all previous added listeners from this element
9662          * @return {Roo.Element} this
9663          */
9664         removeAllListeners : function(){
9665             E.purgeElement(this.dom);
9666             this.listeners = {};
9667             return this;
9668         },
9669
9670         relayEvent : function(eventName, observable){
9671             this.on(eventName, function(e){
9672                 observable.fireEvent(eventName, e);
9673             });
9674         },
9675
9676         
9677         /**
9678          * Set the opacity of the element
9679          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
9680          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9681          * @return {Roo.Element} this
9682          */
9683          setOpacity : function(opacity, animate){
9684             if(!animate || !A){
9685                 var s = this.dom.style;
9686                 if(Roo.isIE){
9687                     s.zoom = 1;
9688                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
9689                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
9690                 }else{
9691                     s.opacity = opacity;
9692                 }
9693             }else{
9694                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
9695             }
9696             return this;
9697         },
9698
9699         /**
9700          * Gets the left X coordinate
9701          * @param {Boolean} local True to get the local css position instead of page coordinate
9702          * @return {Number}
9703          */
9704         getLeft : function(local){
9705             if(!local){
9706                 return this.getX();
9707             }else{
9708                 return parseInt(this.getStyle("left"), 10) || 0;
9709             }
9710         },
9711
9712         /**
9713          * Gets the right X coordinate of the element (element X position + element width)
9714          * @param {Boolean} local True to get the local css position instead of page coordinate
9715          * @return {Number}
9716          */
9717         getRight : function(local){
9718             if(!local){
9719                 return this.getX() + this.getWidth();
9720             }else{
9721                 return (this.getLeft(true) + this.getWidth()) || 0;
9722             }
9723         },
9724
9725         /**
9726          * Gets the top Y coordinate
9727          * @param {Boolean} local True to get the local css position instead of page coordinate
9728          * @return {Number}
9729          */
9730         getTop : function(local) {
9731             if(!local){
9732                 return this.getY();
9733             }else{
9734                 return parseInt(this.getStyle("top"), 10) || 0;
9735             }
9736         },
9737
9738         /**
9739          * Gets the bottom Y coordinate of the element (element Y position + element height)
9740          * @param {Boolean} local True to get the local css position instead of page coordinate
9741          * @return {Number}
9742          */
9743         getBottom : function(local){
9744             if(!local){
9745                 return this.getY() + this.getHeight();
9746             }else{
9747                 return (this.getTop(true) + this.getHeight()) || 0;
9748             }
9749         },
9750
9751         /**
9752         * Initializes positioning on this element. If a desired position is not passed, it will make the
9753         * the element positioned relative IF it is not already positioned.
9754         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
9755         * @param {Number} zIndex (optional) The zIndex to apply
9756         * @param {Number} x (optional) Set the page X position
9757         * @param {Number} y (optional) Set the page Y position
9758         */
9759         position : function(pos, zIndex, x, y){
9760             if(!pos){
9761                if(this.getStyle('position') == 'static'){
9762                    this.setStyle('position', 'relative');
9763                }
9764             }else{
9765                 this.setStyle("position", pos);
9766             }
9767             if(zIndex){
9768                 this.setStyle("z-index", zIndex);
9769             }
9770             if(x !== undefined && y !== undefined){
9771                 this.setXY([x, y]);
9772             }else if(x !== undefined){
9773                 this.setX(x);
9774             }else if(y !== undefined){
9775                 this.setY(y);
9776             }
9777         },
9778
9779         /**
9780         * Clear positioning back to the default when the document was loaded
9781         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
9782         * @return {Roo.Element} this
9783          */
9784         clearPositioning : function(value){
9785             value = value ||'';
9786             this.setStyle({
9787                 "left": value,
9788                 "right": value,
9789                 "top": value,
9790                 "bottom": value,
9791                 "z-index": "",
9792                 "position" : "static"
9793             });
9794             return this;
9795         },
9796
9797         /**
9798         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
9799         * snapshot before performing an update and then restoring the element.
9800         * @return {Object}
9801         */
9802         getPositioning : function(){
9803             var l = this.getStyle("left");
9804             var t = this.getStyle("top");
9805             return {
9806                 "position" : this.getStyle("position"),
9807                 "left" : l,
9808                 "right" : l ? "" : this.getStyle("right"),
9809                 "top" : t,
9810                 "bottom" : t ? "" : this.getStyle("bottom"),
9811                 "z-index" : this.getStyle("z-index")
9812             };
9813         },
9814
9815         /**
9816          * Gets the width of the border(s) for the specified side(s)
9817          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9818          * passing lr would get the border (l)eft width + the border (r)ight width.
9819          * @return {Number} The width of the sides passed added together
9820          */
9821         getBorderWidth : function(side){
9822             return this.addStyles(side, El.borders);
9823         },
9824
9825         /**
9826          * Gets the width of the padding(s) for the specified side(s)
9827          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9828          * passing lr would get the padding (l)eft + the padding (r)ight.
9829          * @return {Number} The padding of the sides passed added together
9830          */
9831         getPadding : function(side){
9832             return this.addStyles(side, El.paddings);
9833         },
9834
9835         /**
9836         * Set positioning with an object returned by getPositioning().
9837         * @param {Object} posCfg
9838         * @return {Roo.Element} this
9839          */
9840         setPositioning : function(pc){
9841             this.applyStyles(pc);
9842             if(pc.right == "auto"){
9843                 this.dom.style.right = "";
9844             }
9845             if(pc.bottom == "auto"){
9846                 this.dom.style.bottom = "";
9847             }
9848             return this;
9849         },
9850
9851         // private
9852         fixDisplay : function(){
9853             if(this.getStyle("display") == "none"){
9854                 this.setStyle("visibility", "hidden");
9855                 this.setStyle("display", this.originalDisplay); // first try reverting to default
9856                 if(this.getStyle("display") == "none"){ // if that fails, default to block
9857                     this.setStyle("display", "block");
9858                 }
9859             }
9860         },
9861
9862         /**
9863          * Quick set left and top adding default units
9864          * @param {String} left The left CSS property value
9865          * @param {String} top The top CSS property value
9866          * @return {Roo.Element} this
9867          */
9868          setLeftTop : function(left, top){
9869             this.dom.style.left = this.addUnits(left);
9870             this.dom.style.top = this.addUnits(top);
9871             return this;
9872         },
9873
9874         /**
9875          * Move this element relative to its current position.
9876          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9877          * @param {Number} distance How far to move the element in pixels
9878          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9879          * @return {Roo.Element} this
9880          */
9881          move : function(direction, distance, animate){
9882             var xy = this.getXY();
9883             direction = direction.toLowerCase();
9884             switch(direction){
9885                 case "l":
9886                 case "left":
9887                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
9888                     break;
9889                case "r":
9890                case "right":
9891                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
9892                     break;
9893                case "t":
9894                case "top":
9895                case "up":
9896                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
9897                     break;
9898                case "b":
9899                case "bottom":
9900                case "down":
9901                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
9902                     break;
9903             }
9904             return this;
9905         },
9906
9907         /**
9908          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
9909          * @return {Roo.Element} this
9910          */
9911         clip : function(){
9912             if(!this.isClipped){
9913                this.isClipped = true;
9914                this.originalClip = {
9915                    "o": this.getStyle("overflow"),
9916                    "x": this.getStyle("overflow-x"),
9917                    "y": this.getStyle("overflow-y")
9918                };
9919                this.setStyle("overflow", "hidden");
9920                this.setStyle("overflow-x", "hidden");
9921                this.setStyle("overflow-y", "hidden");
9922             }
9923             return this;
9924         },
9925
9926         /**
9927          *  Return clipping (overflow) to original clipping before clip() was called
9928          * @return {Roo.Element} this
9929          */
9930         unclip : function(){
9931             if(this.isClipped){
9932                 this.isClipped = false;
9933                 var o = this.originalClip;
9934                 if(o.o){this.setStyle("overflow", o.o);}
9935                 if(o.x){this.setStyle("overflow-x", o.x);}
9936                 if(o.y){this.setStyle("overflow-y", o.y);}
9937             }
9938             return this;
9939         },
9940
9941
9942         /**
9943          * Gets the x,y coordinates specified by the anchor position on the element.
9944          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
9945          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
9946          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
9947          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
9948          * @return {Array} [x, y] An array containing the element's x and y coordinates
9949          */
9950         getAnchorXY : function(anchor, local, s){
9951             //Passing a different size is useful for pre-calculating anchors,
9952             //especially for anchored animations that change the el size.
9953
9954             var w, h, vp = false;
9955             if(!s){
9956                 var d = this.dom;
9957                 if(d == document.body || d == document){
9958                     vp = true;
9959                     w = D.getViewWidth(); h = D.getViewHeight();
9960                 }else{
9961                     w = this.getWidth(); h = this.getHeight();
9962                 }
9963             }else{
9964                 w = s.width;  h = s.height;
9965             }
9966             var x = 0, y = 0, r = Math.round;
9967             switch((anchor || "tl").toLowerCase()){
9968                 case "c":
9969                     x = r(w*.5);
9970                     y = r(h*.5);
9971                 break;
9972                 case "t":
9973                     x = r(w*.5);
9974                     y = 0;
9975                 break;
9976                 case "l":
9977                     x = 0;
9978                     y = r(h*.5);
9979                 break;
9980                 case "r":
9981                     x = w;
9982                     y = r(h*.5);
9983                 break;
9984                 case "b":
9985                     x = r(w*.5);
9986                     y = h;
9987                 break;
9988                 case "tl":
9989                     x = 0;
9990                     y = 0;
9991                 break;
9992                 case "bl":
9993                     x = 0;
9994                     y = h;
9995                 break;
9996                 case "br":
9997                     x = w;
9998                     y = h;
9999                 break;
10000                 case "tr":
10001                     x = w;
10002                     y = 0;
10003                 break;
10004             }
10005             if(local === true){
10006                 return [x, y];
10007             }
10008             if(vp){
10009                 var sc = this.getScroll();
10010                 return [x + sc.left, y + sc.top];
10011             }
10012             //Add the element's offset xy
10013             var o = this.getXY();
10014             return [x+o[0], y+o[1]];
10015         },
10016
10017         /**
10018          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
10019          * supported position values.
10020          * @param {String/HTMLElement/Roo.Element} element The element to align to.
10021          * @param {String} position The position to align to.
10022          * @param {Array} offsets (optional) Offset the positioning by [x, y]
10023          * @return {Array} [x, y]
10024          */
10025         getAlignToXY : function(el, p, o)
10026         {
10027             el = Roo.get(el);
10028             var d = this.dom;
10029             if(!el.dom){
10030                 throw "Element.alignTo with an element that doesn't exist";
10031             }
10032             var c = false; //constrain to viewport
10033             var p1 = "", p2 = "";
10034             o = o || [0,0];
10035
10036             if(!p){
10037                 p = "tl-bl";
10038             }else if(p == "?"){
10039                 p = "tl-bl?";
10040             }else if(p.indexOf("-") == -1){
10041                 p = "tl-" + p;
10042             }
10043             p = p.toLowerCase();
10044             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
10045             if(!m){
10046                throw "Element.alignTo with an invalid alignment " + p;
10047             }
10048             p1 = m[1]; p2 = m[2]; c = !!m[3];
10049
10050             //Subtract the aligned el's internal xy from the target's offset xy
10051             //plus custom offset to get the aligned el's new offset xy
10052             var a1 = this.getAnchorXY(p1, true);
10053             var a2 = el.getAnchorXY(p2, false);
10054             var x = a2[0] - a1[0] + o[0];
10055             var y = a2[1] - a1[1] + o[1];
10056             if(c){
10057                 //constrain the aligned el to viewport if necessary
10058                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
10059                 // 5px of margin for ie
10060                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
10061
10062                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
10063                 //perpendicular to the vp border, allow the aligned el to slide on that border,
10064                 //otherwise swap the aligned el to the opposite border of the target.
10065                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
10066                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
10067                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t")  );
10068                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
10069
10070                var doc = document;
10071                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
10072                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
10073
10074                if((x+w) > dw + scrollX){
10075                     x = swapX ? r.left-w : dw+scrollX-w;
10076                 }
10077                if(x < scrollX){
10078                    x = swapX ? r.right : scrollX;
10079                }
10080                if((y+h) > dh + scrollY){
10081                     y = swapY ? r.top-h : dh+scrollY-h;
10082                 }
10083                if (y < scrollY){
10084                    y = swapY ? r.bottom : scrollY;
10085                }
10086             }
10087             return [x,y];
10088         },
10089
10090         // private
10091         getConstrainToXY : function(){
10092             var os = {top:0, left:0, bottom:0, right: 0};
10093
10094             return function(el, local, offsets, proposedXY){
10095                 el = Roo.get(el);
10096                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
10097
10098                 var vw, vh, vx = 0, vy = 0;
10099                 if(el.dom == document.body || el.dom == document){
10100                     vw = Roo.lib.Dom.getViewWidth();
10101                     vh = Roo.lib.Dom.getViewHeight();
10102                 }else{
10103                     vw = el.dom.clientWidth;
10104                     vh = el.dom.clientHeight;
10105                     if(!local){
10106                         var vxy = el.getXY();
10107                         vx = vxy[0];
10108                         vy = vxy[1];
10109                     }
10110                 }
10111
10112                 var s = el.getScroll();
10113
10114                 vx += offsets.left + s.left;
10115                 vy += offsets.top + s.top;
10116
10117                 vw -= offsets.right;
10118                 vh -= offsets.bottom;
10119
10120                 var vr = vx+vw;
10121                 var vb = vy+vh;
10122
10123                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
10124                 var x = xy[0], y = xy[1];
10125                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
10126
10127                 // only move it if it needs it
10128                 var moved = false;
10129
10130                 // first validate right/bottom
10131                 if((x + w) > vr){
10132                     x = vr - w;
10133                     moved = true;
10134                 }
10135                 if((y + h) > vb){
10136                     y = vb - h;
10137                     moved = true;
10138                 }
10139                 // then make sure top/left isn't negative
10140                 if(x < vx){
10141                     x = vx;
10142                     moved = true;
10143                 }
10144                 if(y < vy){
10145                     y = vy;
10146                     moved = true;
10147                 }
10148                 return moved ? [x, y] : false;
10149             };
10150         }(),
10151
10152         // private
10153         adjustForConstraints : function(xy, parent, offsets){
10154             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
10155         },
10156
10157         /**
10158          * Aligns this element with another element relative to the specified anchor points. If the other element is the
10159          * document it aligns it to the viewport.
10160          * The position parameter is optional, and can be specified in any one of the following formats:
10161          * <ul>
10162          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
10163          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
10164          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
10165          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
10166          *   <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
10167          *       element's anchor point, and the second value is used as the target's anchor point.</li>
10168          * </ul>
10169          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
10170          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
10171          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
10172          * that specified in order to enforce the viewport constraints.
10173          * Following are all of the supported anchor positions:
10174     <pre>
10175     Value  Description
10176     -----  -----------------------------
10177     tl     The top left corner (default)
10178     t      The center of the top edge
10179     tr     The top right corner
10180     l      The center of the left edge
10181     c      In the center of the element
10182     r      The center of the right edge
10183     bl     The bottom left corner
10184     b      The center of the bottom edge
10185     br     The bottom right corner
10186     </pre>
10187     Example Usage:
10188     <pre><code>
10189     // align el to other-el using the default positioning ("tl-bl", non-constrained)
10190     el.alignTo("other-el");
10191
10192     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
10193     el.alignTo("other-el", "tr?");
10194
10195     // align the bottom right corner of el with the center left edge of other-el
10196     el.alignTo("other-el", "br-l?");
10197
10198     // align the center of el with the bottom left corner of other-el and
10199     // adjust the x position by -6 pixels (and the y position by 0)
10200     el.alignTo("other-el", "c-bl", [-6, 0]);
10201     </code></pre>
10202          * @param {String/HTMLElement/Roo.Element} element The element to align to.
10203          * @param {String} position The position to align to.
10204          * @param {Array} offsets (optional) Offset the positioning by [x, y]
10205          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10206          * @return {Roo.Element} this
10207          */
10208         alignTo : function(element, position, offsets, animate){
10209             var xy = this.getAlignToXY(element, position, offsets);
10210             this.setXY(xy, this.preanim(arguments, 3));
10211             return this;
10212         },
10213
10214         /**
10215          * Anchors an element to another element and realigns it when the window is resized.
10216          * @param {String/HTMLElement/Roo.Element} element The element to align to.
10217          * @param {String} position The position to align to.
10218          * @param {Array} offsets (optional) Offset the positioning by [x, y]
10219          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
10220          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
10221          * is a number, it is used as the buffer delay (defaults to 50ms).
10222          * @param {Function} callback The function to call after the animation finishes
10223          * @return {Roo.Element} this
10224          */
10225         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
10226             var action = function(){
10227                 this.alignTo(el, alignment, offsets, animate);
10228                 Roo.callback(callback, this);
10229             };
10230             Roo.EventManager.onWindowResize(action, this);
10231             var tm = typeof monitorScroll;
10232             if(tm != 'undefined'){
10233                 Roo.EventManager.on(window, 'scroll', action, this,
10234                     {buffer: tm == 'number' ? monitorScroll : 50});
10235             }
10236             action.call(this); // align immediately
10237             return this;
10238         },
10239         /**
10240          * Clears any opacity settings from this element. Required in some cases for IE.
10241          * @return {Roo.Element} this
10242          */
10243         clearOpacity : function(){
10244             if (window.ActiveXObject) {
10245                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
10246                     this.dom.style.filter = "";
10247                 }
10248             } else {
10249                 this.dom.style.opacity = "";
10250                 this.dom.style["-moz-opacity"] = "";
10251                 this.dom.style["-khtml-opacity"] = "";
10252             }
10253             return this;
10254         },
10255
10256         /**
10257          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
10258          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10259          * @return {Roo.Element} this
10260          */
10261         hide : function(animate){
10262             this.setVisible(false, this.preanim(arguments, 0));
10263             return this;
10264         },
10265
10266         /**
10267         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
10268         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10269          * @return {Roo.Element} this
10270          */
10271         show : function(animate){
10272             this.setVisible(true, this.preanim(arguments, 0));
10273             return this;
10274         },
10275
10276         /**
10277          * @private Test if size has a unit, otherwise appends the default
10278          */
10279         addUnits : function(size){
10280             return Roo.Element.addUnits(size, this.defaultUnit);
10281         },
10282
10283         /**
10284          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
10285          * @return {Roo.Element} this
10286          */
10287         beginMeasure : function(){
10288             var el = this.dom;
10289             if(el.offsetWidth || el.offsetHeight){
10290                 return this; // offsets work already
10291             }
10292             var changed = [];
10293             var p = this.dom, b = document.body; // start with this element
10294             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
10295                 var pe = Roo.get(p);
10296                 if(pe.getStyle('display') == 'none'){
10297                     changed.push({el: p, visibility: pe.getStyle("visibility")});
10298                     p.style.visibility = "hidden";
10299                     p.style.display = "block";
10300                 }
10301                 p = p.parentNode;
10302             }
10303             this._measureChanged = changed;
10304             return this;
10305
10306         },
10307
10308         /**
10309          * Restores displays to before beginMeasure was called
10310          * @return {Roo.Element} this
10311          */
10312         endMeasure : function(){
10313             var changed = this._measureChanged;
10314             if(changed){
10315                 for(var i = 0, len = changed.length; i < len; i++) {
10316                     var r = changed[i];
10317                     r.el.style.visibility = r.visibility;
10318                     r.el.style.display = "none";
10319                 }
10320                 this._measureChanged = null;
10321             }
10322             return this;
10323         },
10324
10325         /**
10326         * Update the innerHTML of this element, optionally searching for and processing scripts
10327         * @param {String} html The new HTML
10328         * @param {Boolean} loadScripts (optional) true to look for and process scripts
10329         * @param {Function} callback For async script loading you can be noticed when the update completes
10330         * @return {Roo.Element} this
10331          */
10332         update : function(html, loadScripts, callback){
10333             if(typeof html == "undefined"){
10334                 html = "";
10335             }
10336             if(loadScripts !== true){
10337                 this.dom.innerHTML = html;
10338                 if(typeof callback == "function"){
10339                     callback();
10340                 }
10341                 return this;
10342             }
10343             var id = Roo.id();
10344             var dom = this.dom;
10345
10346             html += '<span id="' + id + '"></span>';
10347
10348             E.onAvailable(id, function(){
10349                 var hd = document.getElementsByTagName("head")[0];
10350                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
10351                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
10352                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
10353
10354                 var match;
10355                 while(match = re.exec(html)){
10356                     var attrs = match[1];
10357                     var srcMatch = attrs ? attrs.match(srcRe) : false;
10358                     if(srcMatch && srcMatch[2]){
10359                        var s = document.createElement("script");
10360                        s.src = srcMatch[2];
10361                        var typeMatch = attrs.match(typeRe);
10362                        if(typeMatch && typeMatch[2]){
10363                            s.type = typeMatch[2];
10364                        }
10365                        hd.appendChild(s);
10366                     }else if(match[2] && match[2].length > 0){
10367                         if(window.execScript) {
10368                            window.execScript(match[2]);
10369                         } else {
10370                             /**
10371                              * eval:var:id
10372                              * eval:var:dom
10373                              * eval:var:html
10374                              * 
10375                              */
10376                            window.eval(match[2]);
10377                         }
10378                     }
10379                 }
10380                 var el = document.getElementById(id);
10381                 if(el){el.parentNode.removeChild(el);}
10382                 if(typeof callback == "function"){
10383                     callback();
10384                 }
10385             });
10386             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
10387             return this;
10388         },
10389
10390         /**
10391          * Direct access to the UpdateManager update() method (takes the same parameters).
10392          * @param {String/Function} url The url for this request or a function to call to get the url
10393          * @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}
10394          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
10395          * @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.
10396          * @return {Roo.Element} this
10397          */
10398         load : function(){
10399             var um = this.getUpdateManager();
10400             um.update.apply(um, arguments);
10401             return this;
10402         },
10403
10404         /**
10405         * Gets this element's UpdateManager
10406         * @return {Roo.UpdateManager} The UpdateManager
10407         */
10408         getUpdateManager : function(){
10409             if(!this.updateManager){
10410                 this.updateManager = new Roo.UpdateManager(this);
10411             }
10412             return this.updateManager;
10413         },
10414
10415         /**
10416          * Disables text selection for this element (normalized across browsers)
10417          * @return {Roo.Element} this
10418          */
10419         unselectable : function(){
10420             this.dom.unselectable = "on";
10421             this.swallowEvent("selectstart", true);
10422             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
10423             this.addClass("x-unselectable");
10424             return this;
10425         },
10426
10427         /**
10428         * Calculates the x, y to center this element on the screen
10429         * @return {Array} The x, y values [x, y]
10430         */
10431         getCenterXY : function(){
10432             return this.getAlignToXY(document, 'c-c');
10433         },
10434
10435         /**
10436         * Centers the Element in either the viewport, or another Element.
10437         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
10438         */
10439         center : function(centerIn){
10440             this.alignTo(centerIn || document, 'c-c');
10441             return this;
10442         },
10443
10444         /**
10445          * Tests various css rules/browsers to determine if this element uses a border box
10446          * @return {Boolean}
10447          */
10448         isBorderBox : function(){
10449             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
10450         },
10451
10452         /**
10453          * Return a box {x, y, width, height} that can be used to set another elements
10454          * size/location to match this element.
10455          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
10456          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
10457          * @return {Object} box An object in the format {x, y, width, height}
10458          */
10459         getBox : function(contentBox, local){
10460             var xy;
10461             if(!local){
10462                 xy = this.getXY();
10463             }else{
10464                 var left = parseInt(this.getStyle("left"), 10) || 0;
10465                 var top = parseInt(this.getStyle("top"), 10) || 0;
10466                 xy = [left, top];
10467             }
10468             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
10469             if(!contentBox){
10470                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
10471             }else{
10472                 var l = this.getBorderWidth("l")+this.getPadding("l");
10473                 var r = this.getBorderWidth("r")+this.getPadding("r");
10474                 var t = this.getBorderWidth("t")+this.getPadding("t");
10475                 var b = this.getBorderWidth("b")+this.getPadding("b");
10476                 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)};
10477             }
10478             bx.right = bx.x + bx.width;
10479             bx.bottom = bx.y + bx.height;
10480             return bx;
10481         },
10482
10483         /**
10484          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
10485          for more information about the sides.
10486          * @param {String} sides
10487          * @return {Number}
10488          */
10489         getFrameWidth : function(sides, onlyContentBox){
10490             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
10491         },
10492
10493         /**
10494          * 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.
10495          * @param {Object} box The box to fill {x, y, width, height}
10496          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
10497          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10498          * @return {Roo.Element} this
10499          */
10500         setBox : function(box, adjust, animate){
10501             var w = box.width, h = box.height;
10502             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
10503                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
10504                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
10505             }
10506             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
10507             return this;
10508         },
10509
10510         /**
10511          * Forces the browser to repaint this element
10512          * @return {Roo.Element} this
10513          */
10514          repaint : function(){
10515             var dom = this.dom;
10516             this.addClass("x-repaint");
10517             setTimeout(function(){
10518                 Roo.get(dom).removeClass("x-repaint");
10519             }, 1);
10520             return this;
10521         },
10522
10523         /**
10524          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
10525          * then it returns the calculated width of the sides (see getPadding)
10526          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
10527          * @return {Object/Number}
10528          */
10529         getMargins : function(side){
10530             if(!side){
10531                 return {
10532                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
10533                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
10534                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
10535                     right: parseInt(this.getStyle("margin-right"), 10) || 0
10536                 };
10537             }else{
10538                 return this.addStyles(side, El.margins);
10539              }
10540         },
10541
10542         // private
10543         addStyles : function(sides, styles){
10544             var val = 0, v, w;
10545             for(var i = 0, len = sides.length; i < len; i++){
10546                 v = this.getStyle(styles[sides.charAt(i)]);
10547                 if(v){
10548                      w = parseInt(v, 10);
10549                      if(w){ val += w; }
10550                 }
10551             }
10552             return val;
10553         },
10554
10555         /**
10556          * Creates a proxy element of this element
10557          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
10558          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
10559          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
10560          * @return {Roo.Element} The new proxy element
10561          */
10562         createProxy : function(config, renderTo, matchBox){
10563             if(renderTo){
10564                 renderTo = Roo.getDom(renderTo);
10565             }else{
10566                 renderTo = document.body;
10567             }
10568             config = typeof config == "object" ?
10569                 config : {tag : "div", cls: config};
10570             var proxy = Roo.DomHelper.append(renderTo, config, true);
10571             if(matchBox){
10572                proxy.setBox(this.getBox());
10573             }
10574             return proxy;
10575         },
10576
10577         /**
10578          * Puts a mask over this element to disable user interaction. Requires core.css.
10579          * This method can only be applied to elements which accept child nodes.
10580          * @param {String} msg (optional) A message to display in the mask
10581          * @param {String} msgCls (optional) A css class to apply to the msg element - use no-spinner to hide the spinner on bootstrap
10582          * @return {Element} The mask  element
10583          */
10584         mask : function(msg, msgCls)
10585         {
10586             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
10587                 this.setStyle("position", "relative");
10588             }
10589             if(!this._mask){
10590                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
10591             }
10592             
10593             this.addClass("x-masked");
10594             this._mask.setDisplayed(true);
10595             
10596             // we wander
10597             var z = 0;
10598             var dom = this.dom;
10599             while (dom && dom.style) {
10600                 if (!isNaN(parseInt(dom.style.zIndex))) {
10601                     z = Math.max(z, parseInt(dom.style.zIndex));
10602                 }
10603                 dom = dom.parentNode;
10604             }
10605             // if we are masking the body - then it hides everything..
10606             if (this.dom == document.body) {
10607                 z = 1000000;
10608                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
10609                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
10610             }
10611            
10612             if(typeof msg == 'string'){
10613                 if(!this._maskMsg){
10614                     this._maskMsg = Roo.DomHelper.append(this.dom, {
10615                         cls: "roo-el-mask-msg", 
10616                         cn: [
10617                             {
10618                                 tag: 'i',
10619                                 cls: 'fa fa-spinner fa-spin'
10620                             },
10621                             {
10622                                 tag: 'div'
10623                             }   
10624                         ]
10625                     }, true);
10626                 }
10627                 var mm = this._maskMsg;
10628                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
10629                 if (mm.dom.lastChild) { // weird IE issue?
10630                     mm.dom.lastChild.innerHTML = msg;
10631                 }
10632                 mm.setDisplayed(true);
10633                 mm.center(this);
10634                 mm.setStyle('z-index', z + 102);
10635             }
10636             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
10637                 this._mask.setHeight(this.getHeight());
10638             }
10639             this._mask.setStyle('z-index', z + 100);
10640             
10641             return this._mask;
10642         },
10643
10644         /**
10645          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
10646          * it is cached for reuse.
10647          */
10648         unmask : function(removeEl){
10649             if(this._mask){
10650                 if(removeEl === true){
10651                     this._mask.remove();
10652                     delete this._mask;
10653                     if(this._maskMsg){
10654                         this._maskMsg.remove();
10655                         delete this._maskMsg;
10656                     }
10657                 }else{
10658                     this._mask.setDisplayed(false);
10659                     if(this._maskMsg){
10660                         this._maskMsg.setDisplayed(false);
10661                     }
10662                 }
10663             }
10664             this.removeClass("x-masked");
10665         },
10666
10667         /**
10668          * Returns true if this element is masked
10669          * @return {Boolean}
10670          */
10671         isMasked : function(){
10672             return this._mask && this._mask.isVisible();
10673         },
10674
10675         /**
10676          * Creates an iframe shim for this element to keep selects and other windowed objects from
10677          * showing through.
10678          * @return {Roo.Element} The new shim element
10679          */
10680         createShim : function(){
10681             var el = document.createElement('iframe');
10682             el.frameBorder = 'no';
10683             el.className = 'roo-shim';
10684             if(Roo.isIE && Roo.isSecure){
10685                 el.src = Roo.SSL_SECURE_URL;
10686             }
10687             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
10688             shim.autoBoxAdjust = false;
10689             return shim;
10690         },
10691
10692         /**
10693          * Removes this element from the DOM and deletes it from the cache
10694          */
10695         remove : function(){
10696             if(this.dom.parentNode){
10697                 this.dom.parentNode.removeChild(this.dom);
10698             }
10699             delete El.cache[this.dom.id];
10700         },
10701
10702         /**
10703          * Sets up event handlers to add and remove a css class when the mouse is over this element
10704          * @param {String} className
10705          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
10706          * mouseout events for children elements
10707          * @return {Roo.Element} this
10708          */
10709         addClassOnOver : function(className, preventFlicker){
10710             this.on("mouseover", function(){
10711                 Roo.fly(this, '_internal').addClass(className);
10712             }, this.dom);
10713             var removeFn = function(e){
10714                 if(preventFlicker !== true || !e.within(this, true)){
10715                     Roo.fly(this, '_internal').removeClass(className);
10716                 }
10717             };
10718             this.on("mouseout", removeFn, this.dom);
10719             return this;
10720         },
10721
10722         /**
10723          * Sets up event handlers to add and remove a css class when this element has the focus
10724          * @param {String} className
10725          * @return {Roo.Element} this
10726          */
10727         addClassOnFocus : function(className){
10728             this.on("focus", function(){
10729                 Roo.fly(this, '_internal').addClass(className);
10730             }, this.dom);
10731             this.on("blur", function(){
10732                 Roo.fly(this, '_internal').removeClass(className);
10733             }, this.dom);
10734             return this;
10735         },
10736         /**
10737          * 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)
10738          * @param {String} className
10739          * @return {Roo.Element} this
10740          */
10741         addClassOnClick : function(className){
10742             var dom = this.dom;
10743             this.on("mousedown", function(){
10744                 Roo.fly(dom, '_internal').addClass(className);
10745                 var d = Roo.get(document);
10746                 var fn = function(){
10747                     Roo.fly(dom, '_internal').removeClass(className);
10748                     d.removeListener("mouseup", fn);
10749                 };
10750                 d.on("mouseup", fn);
10751             });
10752             return this;
10753         },
10754
10755         /**
10756          * Stops the specified event from bubbling and optionally prevents the default action
10757          * @param {String} eventName
10758          * @param {Boolean} preventDefault (optional) true to prevent the default action too
10759          * @return {Roo.Element} this
10760          */
10761         swallowEvent : function(eventName, preventDefault){
10762             var fn = function(e){
10763                 e.stopPropagation();
10764                 if(preventDefault){
10765                     e.preventDefault();
10766                 }
10767             };
10768             if(eventName instanceof Array){
10769                 for(var i = 0, len = eventName.length; i < len; i++){
10770                      this.on(eventName[i], fn);
10771                 }
10772                 return this;
10773             }
10774             this.on(eventName, fn);
10775             return this;
10776         },
10777
10778         /**
10779          * @private
10780          */
10781         fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
10782
10783         /**
10784          * Sizes this element to its parent element's dimensions performing
10785          * neccessary box adjustments.
10786          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
10787          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
10788          * @return {Roo.Element} this
10789          */
10790         fitToParent : function(monitorResize, targetParent) {
10791           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
10792           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
10793           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
10794             return this;
10795           }
10796           var p = Roo.get(targetParent || this.dom.parentNode);
10797           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
10798           if (monitorResize === true) {
10799             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
10800             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
10801           }
10802           return this;
10803         },
10804
10805         /**
10806          * Gets the next sibling, skipping text nodes
10807          * @return {HTMLElement} The next sibling or null
10808          */
10809         getNextSibling : function(){
10810             var n = this.dom.nextSibling;
10811             while(n && n.nodeType != 1){
10812                 n = n.nextSibling;
10813             }
10814             return n;
10815         },
10816
10817         /**
10818          * Gets the previous sibling, skipping text nodes
10819          * @return {HTMLElement} The previous sibling or null
10820          */
10821         getPrevSibling : function(){
10822             var n = this.dom.previousSibling;
10823             while(n && n.nodeType != 1){
10824                 n = n.previousSibling;
10825             }
10826             return n;
10827         },
10828
10829
10830         /**
10831          * Appends the passed element(s) to this element
10832          * @param {String/HTMLElement/Array/Element/CompositeElement} el
10833          * @return {Roo.Element} this
10834          */
10835         appendChild: function(el){
10836             el = Roo.get(el);
10837             el.appendTo(this);
10838             return this;
10839         },
10840
10841         /**
10842          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
10843          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
10844          * automatically generated with the specified attributes.
10845          * @param {HTMLElement} insertBefore (optional) a child element of this element
10846          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
10847          * @return {Roo.Element} The new child element
10848          */
10849         createChild: function(config, insertBefore, returnDom){
10850             config = config || {tag:'div'};
10851             if(insertBefore){
10852                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
10853             }
10854             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
10855         },
10856
10857         /**
10858          * Appends this element to the passed element
10859          * @param {String/HTMLElement/Element} el The new parent element
10860          * @return {Roo.Element} this
10861          */
10862         appendTo: function(el){
10863             el = Roo.getDom(el);
10864             el.appendChild(this.dom);
10865             return this;
10866         },
10867
10868         /**
10869          * Inserts this element before the passed element in the DOM
10870          * @param {String/HTMLElement/Element} el The element to insert before
10871          * @return {Roo.Element} this
10872          */
10873         insertBefore: function(el){
10874             el = Roo.getDom(el);
10875             el.parentNode.insertBefore(this.dom, el);
10876             return this;
10877         },
10878
10879         /**
10880          * Inserts this element after the passed element in the DOM
10881          * @param {String/HTMLElement/Element} el The element to insert after
10882          * @return {Roo.Element} this
10883          */
10884         insertAfter: function(el){
10885             el = Roo.getDom(el);
10886             el.parentNode.insertBefore(this.dom, el.nextSibling);
10887             return this;
10888         },
10889
10890         /**
10891          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
10892          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10893          * @return {Roo.Element} The new child
10894          */
10895         insertFirst: function(el, returnDom){
10896             el = el || {};
10897             if(typeof el == 'object' && !el.nodeType){ // dh config
10898                 return this.createChild(el, this.dom.firstChild, returnDom);
10899             }else{
10900                 el = Roo.getDom(el);
10901                 this.dom.insertBefore(el, this.dom.firstChild);
10902                 return !returnDom ? Roo.get(el) : el;
10903             }
10904         },
10905
10906         /**
10907          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
10908          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10909          * @param {String} where (optional) 'before' or 'after' defaults to before
10910          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10911          * @return {Roo.Element} the inserted Element
10912          */
10913         insertSibling: function(el, where, returnDom){
10914             where = where ? where.toLowerCase() : 'before';
10915             el = el || {};
10916             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
10917
10918             if(typeof el == 'object' && !el.nodeType){ // dh config
10919                 if(where == 'after' && !this.dom.nextSibling){
10920                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
10921                 }else{
10922                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
10923                 }
10924
10925             }else{
10926                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
10927                             where == 'before' ? this.dom : this.dom.nextSibling);
10928                 if(!returnDom){
10929                     rt = Roo.get(rt);
10930                 }
10931             }
10932             return rt;
10933         },
10934
10935         /**
10936          * Creates and wraps this element with another element
10937          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
10938          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10939          * @return {HTMLElement/Element} The newly created wrapper element
10940          */
10941         wrap: function(config, returnDom){
10942             if(!config){
10943                 config = {tag: "div"};
10944             }
10945             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
10946             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
10947             return newEl;
10948         },
10949
10950         /**
10951          * Replaces the passed element with this element
10952          * @param {String/HTMLElement/Element} el The element to replace
10953          * @return {Roo.Element} this
10954          */
10955         replace: function(el){
10956             el = Roo.get(el);
10957             this.insertBefore(el);
10958             el.remove();
10959             return this;
10960         },
10961
10962         /**
10963          * Inserts an html fragment into this element
10964          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
10965          * @param {String} html The HTML fragment
10966          * @param {Boolean} returnEl True to return an Roo.Element
10967          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
10968          */
10969         insertHtml : function(where, html, returnEl){
10970             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
10971             return returnEl ? Roo.get(el) : el;
10972         },
10973
10974         /**
10975          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
10976          * @param {Object} o The object with the attributes
10977          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
10978          * @return {Roo.Element} this
10979          */
10980         set : function(o, useSet){
10981             var el = this.dom;
10982             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
10983             for(var attr in o){
10984                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
10985                 if(attr=="cls"){
10986                     el.className = o["cls"];
10987                 }else{
10988                     if(useSet) {
10989                         el.setAttribute(attr, o[attr]);
10990                     } else {
10991                         el[attr] = o[attr];
10992                     }
10993                 }
10994             }
10995             if(o.style){
10996                 Roo.DomHelper.applyStyles(el, o.style);
10997             }
10998             return this;
10999         },
11000
11001         /**
11002          * Convenience method for constructing a KeyMap
11003          * @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:
11004          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
11005          * @param {Function} fn The function to call
11006          * @param {Object} scope (optional) The scope of the function
11007          * @return {Roo.KeyMap} The KeyMap created
11008          */
11009         addKeyListener : function(key, fn, scope){
11010             var config;
11011             if(typeof key != "object" || key instanceof Array){
11012                 config = {
11013                     key: key,
11014                     fn: fn,
11015                     scope: scope
11016                 };
11017             }else{
11018                 config = {
11019                     key : key.key,
11020                     shift : key.shift,
11021                     ctrl : key.ctrl,
11022                     alt : key.alt,
11023                     fn: fn,
11024                     scope: scope
11025                 };
11026             }
11027             return new Roo.KeyMap(this, config);
11028         },
11029
11030         /**
11031          * Creates a KeyMap for this element
11032          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
11033          * @return {Roo.KeyMap} The KeyMap created
11034          */
11035         addKeyMap : function(config){
11036             return new Roo.KeyMap(this, config);
11037         },
11038
11039         /**
11040          * Returns true if this element is scrollable.
11041          * @return {Boolean}
11042          */
11043          isScrollable : function(){
11044             var dom = this.dom;
11045             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
11046         },
11047
11048         /**
11049          * 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().
11050          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
11051          * @param {Number} value The new scroll value
11052          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
11053          * @return {Element} this
11054          */
11055
11056         scrollTo : function(side, value, animate){
11057             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
11058             if(!animate || !A){
11059                 this.dom[prop] = value;
11060             }else{
11061                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
11062                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
11063             }
11064             return this;
11065         },
11066
11067         /**
11068          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
11069          * within this element's scrollable range.
11070          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
11071          * @param {Number} distance How far to scroll the element in pixels
11072          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
11073          * @return {Boolean} Returns true if a scroll was triggered or false if the element
11074          * was scrolled as far as it could go.
11075          */
11076          scroll : function(direction, distance, animate){
11077              if(!this.isScrollable()){
11078                  return;
11079              }
11080              var el = this.dom;
11081              var l = el.scrollLeft, t = el.scrollTop;
11082              var w = el.scrollWidth, h = el.scrollHeight;
11083              var cw = el.clientWidth, ch = el.clientHeight;
11084              direction = direction.toLowerCase();
11085              var scrolled = false;
11086              var a = this.preanim(arguments, 2);
11087              switch(direction){
11088                  case "l":
11089                  case "left":
11090                      if(w - l > cw){
11091                          var v = Math.min(l + distance, w-cw);
11092                          this.scrollTo("left", v, a);
11093                          scrolled = true;
11094                      }
11095                      break;
11096                 case "r":
11097                 case "right":
11098                      if(l > 0){
11099                          var v = Math.max(l - distance, 0);
11100                          this.scrollTo("left", v, a);
11101                          scrolled = true;
11102                      }
11103                      break;
11104                 case "t":
11105                 case "top":
11106                 case "up":
11107                      if(t > 0){
11108                          var v = Math.max(t - distance, 0);
11109                          this.scrollTo("top", v, a);
11110                          scrolled = true;
11111                      }
11112                      break;
11113                 case "b":
11114                 case "bottom":
11115                 case "down":
11116                      if(h - t > ch){
11117                          var v = Math.min(t + distance, h-ch);
11118                          this.scrollTo("top", v, a);
11119                          scrolled = true;
11120                      }
11121                      break;
11122              }
11123              return scrolled;
11124         },
11125
11126         /**
11127          * Translates the passed page coordinates into left/top css values for this element
11128          * @param {Number/Array} x The page x or an array containing [x, y]
11129          * @param {Number} y The page y
11130          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
11131          */
11132         translatePoints : function(x, y){
11133             if(typeof x == 'object' || x instanceof Array){
11134                 y = x[1]; x = x[0];
11135             }
11136             var p = this.getStyle('position');
11137             var o = this.getXY();
11138
11139             var l = parseInt(this.getStyle('left'), 10);
11140             var t = parseInt(this.getStyle('top'), 10);
11141
11142             if(isNaN(l)){
11143                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
11144             }
11145             if(isNaN(t)){
11146                 t = (p == "relative") ? 0 : this.dom.offsetTop;
11147             }
11148
11149             return {left: (x - o[0] + l), top: (y - o[1] + t)};
11150         },
11151
11152         /**
11153          * Returns the current scroll position of the element.
11154          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
11155          */
11156         getScroll : function(){
11157             var d = this.dom, doc = document;
11158             if(d == doc || d == doc.body){
11159                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
11160                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
11161                 return {left: l, top: t};
11162             }else{
11163                 return {left: d.scrollLeft, top: d.scrollTop};
11164             }
11165         },
11166
11167         /**
11168          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
11169          * are convert to standard 6 digit hex color.
11170          * @param {String} attr The css attribute
11171          * @param {String} defaultValue The default value to use when a valid color isn't found
11172          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
11173          * YUI color anims.
11174          */
11175         getColor : function(attr, defaultValue, prefix){
11176             var v = this.getStyle(attr);
11177             if(!v || v == "transparent" || v == "inherit") {
11178                 return defaultValue;
11179             }
11180             var color = typeof prefix == "undefined" ? "#" : prefix;
11181             if(v.substr(0, 4) == "rgb("){
11182                 var rvs = v.slice(4, v.length -1).split(",");
11183                 for(var i = 0; i < 3; i++){
11184                     var h = parseInt(rvs[i]).toString(16);
11185                     if(h < 16){
11186                         h = "0" + h;
11187                     }
11188                     color += h;
11189                 }
11190             } else {
11191                 if(v.substr(0, 1) == "#"){
11192                     if(v.length == 4) {
11193                         for(var i = 1; i < 4; i++){
11194                             var c = v.charAt(i);
11195                             color +=  c + c;
11196                         }
11197                     }else if(v.length == 7){
11198                         color += v.substr(1);
11199                     }
11200                 }
11201             }
11202             return(color.length > 5 ? color.toLowerCase() : defaultValue);
11203         },
11204
11205         /**
11206          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
11207          * gradient background, rounded corners and a 4-way shadow.
11208          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
11209          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
11210          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
11211          * @return {Roo.Element} this
11212          */
11213         boxWrap : function(cls){
11214             cls = cls || 'x-box';
11215             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
11216             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
11217             return el;
11218         },
11219
11220         /**
11221          * Returns the value of a namespaced attribute from the element's underlying DOM node.
11222          * @param {String} namespace The namespace in which to look for the attribute
11223          * @param {String} name The attribute name
11224          * @return {String} The attribute value
11225          */
11226         getAttributeNS : Roo.isIE ? function(ns, name){
11227             var d = this.dom;
11228             var type = typeof d[ns+":"+name];
11229             if(type != 'undefined' && type != 'unknown'){
11230                 return d[ns+":"+name];
11231             }
11232             return d[name];
11233         } : function(ns, name){
11234             var d = this.dom;
11235             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
11236         },
11237         
11238         
11239         /**
11240          * Sets or Returns the value the dom attribute value
11241          * @param {String|Object} name The attribute name (or object to set multiple attributes)
11242          * @param {String} value (optional) The value to set the attribute to
11243          * @return {String} The attribute value
11244          */
11245         attr : function(name){
11246             if (arguments.length > 1) {
11247                 this.dom.setAttribute(name, arguments[1]);
11248                 return arguments[1];
11249             }
11250             if (typeof(name) == 'object') {
11251                 for(var i in name) {
11252                     this.attr(i, name[i]);
11253                 }
11254                 return name;
11255             }
11256             
11257             
11258             if (!this.dom.hasAttribute(name)) {
11259                 return undefined;
11260             }
11261             return this.dom.getAttribute(name);
11262         }
11263         
11264         
11265         
11266     };
11267
11268     var ep = El.prototype;
11269
11270     /**
11271      * Appends an event handler (Shorthand for addListener)
11272      * @param {String}   eventName     The type of event to append
11273      * @param {Function} fn        The method the event invokes
11274      * @param {Object} scope       (optional) The scope (this object) of the fn
11275      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
11276      * @method
11277      */
11278     ep.on = ep.addListener;
11279         // backwards compat
11280     ep.mon = ep.addListener;
11281
11282     /**
11283      * Removes an event handler from this element (shorthand for removeListener)
11284      * @param {String} eventName the type of event to remove
11285      * @param {Function} fn the method the event invokes
11286      * @return {Roo.Element} this
11287      * @method
11288      */
11289     ep.un = ep.removeListener;
11290
11291     /**
11292      * true to automatically adjust width and height settings for box-model issues (default to true)
11293      */
11294     ep.autoBoxAdjust = true;
11295
11296     // private
11297     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
11298
11299     // private
11300     El.addUnits = function(v, defaultUnit){
11301         if(v === "" || v == "auto"){
11302             return v;
11303         }
11304         if(v === undefined){
11305             return '';
11306         }
11307         if(typeof v == "number" || !El.unitPattern.test(v)){
11308             return v + (defaultUnit || 'px');
11309         }
11310         return v;
11311     };
11312
11313     // special markup used throughout Roo when box wrapping elements
11314     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>';
11315     /**
11316      * Visibility mode constant - Use visibility to hide element
11317      * @static
11318      * @type Number
11319      */
11320     El.VISIBILITY = 1;
11321     /**
11322      * Visibility mode constant - Use display to hide element
11323      * @static
11324      * @type Number
11325      */
11326     El.DISPLAY = 2;
11327
11328     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
11329     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
11330     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
11331
11332
11333
11334     /**
11335      * @private
11336      */
11337     El.cache = {};
11338
11339     var docEl;
11340
11341     /**
11342      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
11343      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
11344      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
11345      * @return {Element} The Element object
11346      * @static
11347      */
11348     El.get = function(el){
11349         var ex, elm, id;
11350         if(!el){ return null; }
11351         if(typeof el == "string"){ // element id
11352             if(!(elm = document.getElementById(el))){
11353                 return null;
11354             }
11355             if(ex = El.cache[el]){
11356                 ex.dom = elm;
11357             }else{
11358                 ex = El.cache[el] = new El(elm);
11359             }
11360             return ex;
11361         }else if(el.tagName){ // dom element
11362             if(!(id = el.id)){
11363                 id = Roo.id(el);
11364             }
11365             if(ex = El.cache[id]){
11366                 ex.dom = el;
11367             }else{
11368                 ex = El.cache[id] = new El(el);
11369             }
11370             return ex;
11371         }else if(el instanceof El){
11372             if(el != docEl){
11373                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
11374                                                               // catch case where it hasn't been appended
11375                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
11376             }
11377             return el;
11378         }else if(el.isComposite){
11379             return el;
11380         }else if(el instanceof Array){
11381             return El.select(el);
11382         }else if(el == document){
11383             // create a bogus element object representing the document object
11384             if(!docEl){
11385                 var f = function(){};
11386                 f.prototype = El.prototype;
11387                 docEl = new f();
11388                 docEl.dom = document;
11389             }
11390             return docEl;
11391         }
11392         return null;
11393     };
11394
11395     // private
11396     El.uncache = function(el){
11397         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
11398             if(a[i]){
11399                 delete El.cache[a[i].id || a[i]];
11400             }
11401         }
11402     };
11403
11404     // private
11405     // Garbage collection - uncache elements/purge listeners on orphaned elements
11406     // so we don't hold a reference and cause the browser to retain them
11407     El.garbageCollect = function(){
11408         if(!Roo.enableGarbageCollector){
11409             clearInterval(El.collectorThread);
11410             return;
11411         }
11412         for(var eid in El.cache){
11413             var el = El.cache[eid], d = el.dom;
11414             // -------------------------------------------------------
11415             // Determining what is garbage:
11416             // -------------------------------------------------------
11417             // !d
11418             // dom node is null, definitely garbage
11419             // -------------------------------------------------------
11420             // !d.parentNode
11421             // no parentNode == direct orphan, definitely garbage
11422             // -------------------------------------------------------
11423             // !d.offsetParent && !document.getElementById(eid)
11424             // display none elements have no offsetParent so we will
11425             // also try to look it up by it's id. However, check
11426             // offsetParent first so we don't do unneeded lookups.
11427             // This enables collection of elements that are not orphans
11428             // directly, but somewhere up the line they have an orphan
11429             // parent.
11430             // -------------------------------------------------------
11431             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
11432                 delete El.cache[eid];
11433                 if(d && Roo.enableListenerCollection){
11434                     E.purgeElement(d);
11435                 }
11436             }
11437         }
11438     }
11439     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
11440
11441
11442     // dom is optional
11443     El.Flyweight = function(dom){
11444         this.dom = dom;
11445     };
11446     El.Flyweight.prototype = El.prototype;
11447
11448     El._flyweights = {};
11449     /**
11450      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
11451      * the dom node can be overwritten by other code.
11452      * @param {String/HTMLElement} el The dom node or id
11453      * @param {String} named (optional) Allows for creation of named reusable flyweights to
11454      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
11455      * @static
11456      * @return {Element} The shared Element object
11457      */
11458     El.fly = function(el, named){
11459         named = named || '_global';
11460         el = Roo.getDom(el);
11461         if(!el){
11462             return null;
11463         }
11464         if(!El._flyweights[named]){
11465             El._flyweights[named] = new El.Flyweight();
11466         }
11467         El._flyweights[named].dom = el;
11468         return El._flyweights[named];
11469     };
11470
11471     /**
11472      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
11473      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
11474      * Shorthand of {@link Roo.Element#get}
11475      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
11476      * @return {Element} The Element object
11477      * @member Roo
11478      * @method get
11479      */
11480     Roo.get = El.get;
11481     /**
11482      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
11483      * the dom node can be overwritten by other code.
11484      * Shorthand of {@link Roo.Element#fly}
11485      * @param {String/HTMLElement} el The dom node or id
11486      * @param {String} named (optional) Allows for creation of named reusable flyweights to
11487      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
11488      * @static
11489      * @return {Element} The shared Element object
11490      * @member Roo
11491      * @method fly
11492      */
11493     Roo.fly = El.fly;
11494
11495     // speedy lookup for elements never to box adjust
11496     var noBoxAdjust = Roo.isStrict ? {
11497         select:1
11498     } : {
11499         input:1, select:1, textarea:1
11500     };
11501     if(Roo.isIE || Roo.isGecko){
11502         noBoxAdjust['button'] = 1;
11503     }
11504
11505
11506     Roo.EventManager.on(window, 'unload', function(){
11507         delete El.cache;
11508         delete El._flyweights;
11509     });
11510 })();
11511
11512
11513
11514
11515 if(Roo.DomQuery){
11516     Roo.Element.selectorFunction = Roo.DomQuery.select;
11517 }
11518
11519 Roo.Element.select = function(selector, unique, root){
11520     var els;
11521     if(typeof selector == "string"){
11522         els = Roo.Element.selectorFunction(selector, root);
11523     }else if(selector.length !== undefined){
11524         els = selector;
11525     }else{
11526         throw "Invalid selector";
11527     }
11528     if(unique === true){
11529         return new Roo.CompositeElement(els);
11530     }else{
11531         return new Roo.CompositeElementLite(els);
11532     }
11533 };
11534 /**
11535  * Selects elements based on the passed CSS selector to enable working on them as 1.
11536  * @param {String/Array} selector The CSS selector or an array of elements
11537  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
11538  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
11539  * @return {CompositeElementLite/CompositeElement}
11540  * @member Roo
11541  * @method select
11542  */
11543 Roo.select = Roo.Element.select;
11544
11545
11546
11547
11548
11549
11550
11551
11552
11553
11554
11555
11556
11557
11558 /*
11559  * Based on:
11560  * Ext JS Library 1.1.1
11561  * Copyright(c) 2006-2007, Ext JS, LLC.
11562  *
11563  * Originally Released Under LGPL - original licence link has changed is not relivant.
11564  *
11565  * Fork - LGPL
11566  * <script type="text/javascript">
11567  */
11568
11569
11570
11571 //Notifies Element that fx methods are available
11572 Roo.enableFx = true;
11573
11574 /**
11575  * @class Roo.Fx
11576  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
11577  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
11578  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
11579  * Element effects to work.</p><br/>
11580  *
11581  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
11582  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
11583  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
11584  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
11585  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
11586  * expected results and should be done with care.</p><br/>
11587  *
11588  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
11589  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
11590 <pre>
11591 Value  Description
11592 -----  -----------------------------
11593 tl     The top left corner
11594 t      The center of the top edge
11595 tr     The top right corner
11596 l      The center of the left edge
11597 r      The center of the right edge
11598 bl     The bottom left corner
11599 b      The center of the bottom edge
11600 br     The bottom right corner
11601 </pre>
11602  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
11603  * below are common options that can be passed to any Fx method.</b>
11604  * @cfg {Function} callback A function called when the effect is finished
11605  * @cfg {Object} scope The scope of the effect function
11606  * @cfg {String} easing A valid Easing value for the effect
11607  * @cfg {String} afterCls A css class to apply after the effect
11608  * @cfg {Number} duration The length of time (in seconds) that the effect should last
11609  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
11610  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
11611  * effects that end with the element being visually hidden, ignored otherwise)
11612  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
11613  * a function which returns such a specification that will be applied to the Element after the effect finishes
11614  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
11615  * @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
11616  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
11617  */
11618 Roo.Fx = {
11619         /**
11620          * Slides the element into view.  An anchor point can be optionally passed to set the point of
11621          * origin for the slide effect.  This function automatically handles wrapping the element with
11622          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
11623          * Usage:
11624          *<pre><code>
11625 // default: slide the element in from the top
11626 el.slideIn();
11627
11628 // custom: slide the element in from the right with a 2-second duration
11629 el.slideIn('r', { duration: 2 });
11630
11631 // common config options shown with default values
11632 el.slideIn('t', {
11633     easing: 'easeOut',
11634     duration: .5
11635 });
11636 </code></pre>
11637          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11638          * @param {Object} options (optional) Object literal with any of the Fx config options
11639          * @return {Roo.Element} The Element
11640          */
11641     slideIn : function(anchor, o){
11642         var el = this.getFxEl();
11643         o = o || {};
11644
11645         el.queueFx(o, function(){
11646
11647             anchor = anchor || "t";
11648
11649             // fix display to visibility
11650             this.fixDisplay();
11651
11652             // restore values after effect
11653             var r = this.getFxRestore();
11654             var b = this.getBox();
11655             // fixed size for slide
11656             this.setSize(b);
11657
11658             // wrap if needed
11659             var wrap = this.fxWrap(r.pos, o, "hidden");
11660
11661             var st = this.dom.style;
11662             st.visibility = "visible";
11663             st.position = "absolute";
11664
11665             // clear out temp styles after slide and unwrap
11666             var after = function(){
11667                 el.fxUnwrap(wrap, r.pos, o);
11668                 st.width = r.width;
11669                 st.height = r.height;
11670                 el.afterFx(o);
11671             };
11672             // time to calc the positions
11673             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
11674
11675             switch(anchor.toLowerCase()){
11676                 case "t":
11677                     wrap.setSize(b.width, 0);
11678                     st.left = st.bottom = "0";
11679                     a = {height: bh};
11680                 break;
11681                 case "l":
11682                     wrap.setSize(0, b.height);
11683                     st.right = st.top = "0";
11684                     a = {width: bw};
11685                 break;
11686                 case "r":
11687                     wrap.setSize(0, b.height);
11688                     wrap.setX(b.right);
11689                     st.left = st.top = "0";
11690                     a = {width: bw, points: pt};
11691                 break;
11692                 case "b":
11693                     wrap.setSize(b.width, 0);
11694                     wrap.setY(b.bottom);
11695                     st.left = st.top = "0";
11696                     a = {height: bh, points: pt};
11697                 break;
11698                 case "tl":
11699                     wrap.setSize(0, 0);
11700                     st.right = st.bottom = "0";
11701                     a = {width: bw, height: bh};
11702                 break;
11703                 case "bl":
11704                     wrap.setSize(0, 0);
11705                     wrap.setY(b.y+b.height);
11706                     st.right = st.top = "0";
11707                     a = {width: bw, height: bh, points: pt};
11708                 break;
11709                 case "br":
11710                     wrap.setSize(0, 0);
11711                     wrap.setXY([b.right, b.bottom]);
11712                     st.left = st.top = "0";
11713                     a = {width: bw, height: bh, points: pt};
11714                 break;
11715                 case "tr":
11716                     wrap.setSize(0, 0);
11717                     wrap.setX(b.x+b.width);
11718                     st.left = st.bottom = "0";
11719                     a = {width: bw, height: bh, points: pt};
11720                 break;
11721             }
11722             this.dom.style.visibility = "visible";
11723             wrap.show();
11724
11725             arguments.callee.anim = wrap.fxanim(a,
11726                 o,
11727                 'motion',
11728                 .5,
11729                 'easeOut', after);
11730         });
11731         return this;
11732     },
11733     
11734         /**
11735          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
11736          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
11737          * 'hidden') but block elements will still take up space in the document.  The element must be removed
11738          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
11739          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
11740          * Usage:
11741          *<pre><code>
11742 // default: slide the element out to the top
11743 el.slideOut();
11744
11745 // custom: slide the element out to the right with a 2-second duration
11746 el.slideOut('r', { duration: 2 });
11747
11748 // common config options shown with default values
11749 el.slideOut('t', {
11750     easing: 'easeOut',
11751     duration: .5,
11752     remove: false,
11753     useDisplay: false
11754 });
11755 </code></pre>
11756          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11757          * @param {Object} options (optional) Object literal with any of the Fx config options
11758          * @return {Roo.Element} The Element
11759          */
11760     slideOut : function(anchor, o){
11761         var el = this.getFxEl();
11762         o = o || {};
11763
11764         el.queueFx(o, function(){
11765
11766             anchor = anchor || "t";
11767
11768             // restore values after effect
11769             var r = this.getFxRestore();
11770             
11771             var b = this.getBox();
11772             // fixed size for slide
11773             this.setSize(b);
11774
11775             // wrap if needed
11776             var wrap = this.fxWrap(r.pos, o, "visible");
11777
11778             var st = this.dom.style;
11779             st.visibility = "visible";
11780             st.position = "absolute";
11781
11782             wrap.setSize(b);
11783
11784             var after = function(){
11785                 if(o.useDisplay){
11786                     el.setDisplayed(false);
11787                 }else{
11788                     el.hide();
11789                 }
11790
11791                 el.fxUnwrap(wrap, r.pos, o);
11792
11793                 st.width = r.width;
11794                 st.height = r.height;
11795
11796                 el.afterFx(o);
11797             };
11798
11799             var a, zero = {to: 0};
11800             switch(anchor.toLowerCase()){
11801                 case "t":
11802                     st.left = st.bottom = "0";
11803                     a = {height: zero};
11804                 break;
11805                 case "l":
11806                     st.right = st.top = "0";
11807                     a = {width: zero};
11808                 break;
11809                 case "r":
11810                     st.left = st.top = "0";
11811                     a = {width: zero, points: {to:[b.right, b.y]}};
11812                 break;
11813                 case "b":
11814                     st.left = st.top = "0";
11815                     a = {height: zero, points: {to:[b.x, b.bottom]}};
11816                 break;
11817                 case "tl":
11818                     st.right = st.bottom = "0";
11819                     a = {width: zero, height: zero};
11820                 break;
11821                 case "bl":
11822                     st.right = st.top = "0";
11823                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
11824                 break;
11825                 case "br":
11826                     st.left = st.top = "0";
11827                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
11828                 break;
11829                 case "tr":
11830                     st.left = st.bottom = "0";
11831                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
11832                 break;
11833             }
11834
11835             arguments.callee.anim = wrap.fxanim(a,
11836                 o,
11837                 'motion',
11838                 .5,
11839                 "easeOut", after);
11840         });
11841         return this;
11842     },
11843
11844         /**
11845          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
11846          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
11847          * The element must be removed from the DOM using the 'remove' config option if desired.
11848          * Usage:
11849          *<pre><code>
11850 // default
11851 el.puff();
11852
11853 // common config options shown with default values
11854 el.puff({
11855     easing: 'easeOut',
11856     duration: .5,
11857     remove: false,
11858     useDisplay: false
11859 });
11860 </code></pre>
11861          * @param {Object} options (optional) Object literal with any of the Fx config options
11862          * @return {Roo.Element} The Element
11863          */
11864     puff : function(o){
11865         var el = this.getFxEl();
11866         o = o || {};
11867
11868         el.queueFx(o, function(){
11869             this.clearOpacity();
11870             this.show();
11871
11872             // restore values after effect
11873             var r = this.getFxRestore();
11874             var st = this.dom.style;
11875
11876             var after = function(){
11877                 if(o.useDisplay){
11878                     el.setDisplayed(false);
11879                 }else{
11880                     el.hide();
11881                 }
11882
11883                 el.clearOpacity();
11884
11885                 el.setPositioning(r.pos);
11886                 st.width = r.width;
11887                 st.height = r.height;
11888                 st.fontSize = '';
11889                 el.afterFx(o);
11890             };
11891
11892             var width = this.getWidth();
11893             var height = this.getHeight();
11894
11895             arguments.callee.anim = this.fxanim({
11896                     width : {to: this.adjustWidth(width * 2)},
11897                     height : {to: this.adjustHeight(height * 2)},
11898                     points : {by: [-(width * .5), -(height * .5)]},
11899                     opacity : {to: 0},
11900                     fontSize: {to:200, unit: "%"}
11901                 },
11902                 o,
11903                 'motion',
11904                 .5,
11905                 "easeOut", after);
11906         });
11907         return this;
11908     },
11909
11910         /**
11911          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
11912          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
11913          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
11914          * Usage:
11915          *<pre><code>
11916 // default
11917 el.switchOff();
11918
11919 // all config options shown with default values
11920 el.switchOff({
11921     easing: 'easeIn',
11922     duration: .3,
11923     remove: false,
11924     useDisplay: false
11925 });
11926 </code></pre>
11927          * @param {Object} options (optional) Object literal with any of the Fx config options
11928          * @return {Roo.Element} The Element
11929          */
11930     switchOff : function(o){
11931         var el = this.getFxEl();
11932         o = o || {};
11933
11934         el.queueFx(o, function(){
11935             this.clearOpacity();
11936             this.clip();
11937
11938             // restore values after effect
11939             var r = this.getFxRestore();
11940             var st = this.dom.style;
11941
11942             var after = function(){
11943                 if(o.useDisplay){
11944                     el.setDisplayed(false);
11945                 }else{
11946                     el.hide();
11947                 }
11948
11949                 el.clearOpacity();
11950                 el.setPositioning(r.pos);
11951                 st.width = r.width;
11952                 st.height = r.height;
11953
11954                 el.afterFx(o);
11955             };
11956
11957             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
11958                 this.clearOpacity();
11959                 (function(){
11960                     this.fxanim({
11961                         height:{to:1},
11962                         points:{by:[0, this.getHeight() * .5]}
11963                     }, o, 'motion', 0.3, 'easeIn', after);
11964                 }).defer(100, this);
11965             });
11966         });
11967         return this;
11968     },
11969
11970     /**
11971      * Highlights the Element by setting a color (applies to the background-color by default, but can be
11972      * changed using the "attr" config option) and then fading back to the original color. If no original
11973      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
11974      * Usage:
11975 <pre><code>
11976 // default: highlight background to yellow
11977 el.highlight();
11978
11979 // custom: highlight foreground text to blue for 2 seconds
11980 el.highlight("0000ff", { attr: 'color', duration: 2 });
11981
11982 // common config options shown with default values
11983 el.highlight("ffff9c", {
11984     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
11985     endColor: (current color) or "ffffff",
11986     easing: 'easeIn',
11987     duration: 1
11988 });
11989 </code></pre>
11990      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
11991      * @param {Object} options (optional) Object literal with any of the Fx config options
11992      * @return {Roo.Element} The Element
11993      */ 
11994     highlight : function(color, o){
11995         var el = this.getFxEl();
11996         o = o || {};
11997
11998         el.queueFx(o, function(){
11999             color = color || "ffff9c";
12000             attr = o.attr || "backgroundColor";
12001
12002             this.clearOpacity();
12003             this.show();
12004
12005             var origColor = this.getColor(attr);
12006             var restoreColor = this.dom.style[attr];
12007             endColor = (o.endColor || origColor) || "ffffff";
12008
12009             var after = function(){
12010                 el.dom.style[attr] = restoreColor;
12011                 el.afterFx(o);
12012             };
12013
12014             var a = {};
12015             a[attr] = {from: color, to: endColor};
12016             arguments.callee.anim = this.fxanim(a,
12017                 o,
12018                 'color',
12019                 1,
12020                 'easeIn', after);
12021         });
12022         return this;
12023     },
12024
12025    /**
12026     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
12027     * Usage:
12028 <pre><code>
12029 // default: a single light blue ripple
12030 el.frame();
12031
12032 // custom: 3 red ripples lasting 3 seconds total
12033 el.frame("ff0000", 3, { duration: 3 });
12034
12035 // common config options shown with default values
12036 el.frame("C3DAF9", 1, {
12037     duration: 1 //duration of entire animation (not each individual ripple)
12038     // Note: Easing is not configurable and will be ignored if included
12039 });
12040 </code></pre>
12041     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
12042     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
12043     * @param {Object} options (optional) Object literal with any of the Fx config options
12044     * @return {Roo.Element} The Element
12045     */
12046     frame : function(color, count, o){
12047         var el = this.getFxEl();
12048         o = o || {};
12049
12050         el.queueFx(o, function(){
12051             color = color || "#C3DAF9";
12052             if(color.length == 6){
12053                 color = "#" + color;
12054             }
12055             count = count || 1;
12056             duration = o.duration || 1;
12057             this.show();
12058
12059             var b = this.getBox();
12060             var animFn = function(){
12061                 var proxy = this.createProxy({
12062
12063                      style:{
12064                         visbility:"hidden",
12065                         position:"absolute",
12066                         "z-index":"35000", // yee haw
12067                         border:"0px solid " + color
12068                      }
12069                   });
12070                 var scale = Roo.isBorderBox ? 2 : 1;
12071                 proxy.animate({
12072                     top:{from:b.y, to:b.y - 20},
12073                     left:{from:b.x, to:b.x - 20},
12074                     borderWidth:{from:0, to:10},
12075                     opacity:{from:1, to:0},
12076                     height:{from:b.height, to:(b.height + (20*scale))},
12077                     width:{from:b.width, to:(b.width + (20*scale))}
12078                 }, duration, function(){
12079                     proxy.remove();
12080                 });
12081                 if(--count > 0){
12082                      animFn.defer((duration/2)*1000, this);
12083                 }else{
12084                     el.afterFx(o);
12085                 }
12086             };
12087             animFn.call(this);
12088         });
12089         return this;
12090     },
12091
12092    /**
12093     * Creates a pause before any subsequent queued effects begin.  If there are
12094     * no effects queued after the pause it will have no effect.
12095     * Usage:
12096 <pre><code>
12097 el.pause(1);
12098 </code></pre>
12099     * @param {Number} seconds The length of time to pause (in seconds)
12100     * @return {Roo.Element} The Element
12101     */
12102     pause : function(seconds){
12103         var el = this.getFxEl();
12104         var o = {};
12105
12106         el.queueFx(o, function(){
12107             setTimeout(function(){
12108                 el.afterFx(o);
12109             }, seconds * 1000);
12110         });
12111         return this;
12112     },
12113
12114    /**
12115     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
12116     * using the "endOpacity" config option.
12117     * Usage:
12118 <pre><code>
12119 // default: fade in from opacity 0 to 100%
12120 el.fadeIn();
12121
12122 // custom: fade in from opacity 0 to 75% over 2 seconds
12123 el.fadeIn({ endOpacity: .75, duration: 2});
12124
12125 // common config options shown with default values
12126 el.fadeIn({
12127     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
12128     easing: 'easeOut',
12129     duration: .5
12130 });
12131 </code></pre>
12132     * @param {Object} options (optional) Object literal with any of the Fx config options
12133     * @return {Roo.Element} The Element
12134     */
12135     fadeIn : function(o){
12136         var el = this.getFxEl();
12137         o = o || {};
12138         el.queueFx(o, function(){
12139             this.setOpacity(0);
12140             this.fixDisplay();
12141             this.dom.style.visibility = 'visible';
12142             var to = o.endOpacity || 1;
12143             arguments.callee.anim = this.fxanim({opacity:{to:to}},
12144                 o, null, .5, "easeOut", function(){
12145                 if(to == 1){
12146                     this.clearOpacity();
12147                 }
12148                 el.afterFx(o);
12149             });
12150         });
12151         return this;
12152     },
12153
12154    /**
12155     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
12156     * using the "endOpacity" config option.
12157     * Usage:
12158 <pre><code>
12159 // default: fade out from the element's current opacity to 0
12160 el.fadeOut();
12161
12162 // custom: fade out from the element's current opacity to 25% over 2 seconds
12163 el.fadeOut({ endOpacity: .25, duration: 2});
12164
12165 // common config options shown with default values
12166 el.fadeOut({
12167     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
12168     easing: 'easeOut',
12169     duration: .5
12170     remove: false,
12171     useDisplay: false
12172 });
12173 </code></pre>
12174     * @param {Object} options (optional) Object literal with any of the Fx config options
12175     * @return {Roo.Element} The Element
12176     */
12177     fadeOut : function(o){
12178         var el = this.getFxEl();
12179         o = o || {};
12180         el.queueFx(o, function(){
12181             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
12182                 o, null, .5, "easeOut", function(){
12183                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
12184                      this.dom.style.display = "none";
12185                 }else{
12186                      this.dom.style.visibility = "hidden";
12187                 }
12188                 this.clearOpacity();
12189                 el.afterFx(o);
12190             });
12191         });
12192         return this;
12193     },
12194
12195    /**
12196     * Animates the transition of an element's dimensions from a starting height/width
12197     * to an ending height/width.
12198     * Usage:
12199 <pre><code>
12200 // change height and width to 100x100 pixels
12201 el.scale(100, 100);
12202
12203 // common config options shown with default values.  The height and width will default to
12204 // the element's existing values if passed as null.
12205 el.scale(
12206     [element's width],
12207     [element's height], {
12208     easing: 'easeOut',
12209     duration: .35
12210 });
12211 </code></pre>
12212     * @param {Number} width  The new width (pass undefined to keep the original width)
12213     * @param {Number} height  The new height (pass undefined to keep the original height)
12214     * @param {Object} options (optional) Object literal with any of the Fx config options
12215     * @return {Roo.Element} The Element
12216     */
12217     scale : function(w, h, o){
12218         this.shift(Roo.apply({}, o, {
12219             width: w,
12220             height: h
12221         }));
12222         return this;
12223     },
12224
12225    /**
12226     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
12227     * Any of these properties not specified in the config object will not be changed.  This effect 
12228     * requires that at least one new dimension, position or opacity setting must be passed in on
12229     * the config object in order for the function to have any effect.
12230     * Usage:
12231 <pre><code>
12232 // slide the element horizontally to x position 200 while changing the height and opacity
12233 el.shift({ x: 200, height: 50, opacity: .8 });
12234
12235 // common config options shown with default values.
12236 el.shift({
12237     width: [element's width],
12238     height: [element's height],
12239     x: [element's x position],
12240     y: [element's y position],
12241     opacity: [element's opacity],
12242     easing: 'easeOut',
12243     duration: .35
12244 });
12245 </code></pre>
12246     * @param {Object} options  Object literal with any of the Fx config options
12247     * @return {Roo.Element} The Element
12248     */
12249     shift : function(o){
12250         var el = this.getFxEl();
12251         o = o || {};
12252         el.queueFx(o, function(){
12253             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
12254             if(w !== undefined){
12255                 a.width = {to: this.adjustWidth(w)};
12256             }
12257             if(h !== undefined){
12258                 a.height = {to: this.adjustHeight(h)};
12259             }
12260             if(x !== undefined || y !== undefined){
12261                 a.points = {to: [
12262                     x !== undefined ? x : this.getX(),
12263                     y !== undefined ? y : this.getY()
12264                 ]};
12265             }
12266             if(op !== undefined){
12267                 a.opacity = {to: op};
12268             }
12269             if(o.xy !== undefined){
12270                 a.points = {to: o.xy};
12271             }
12272             arguments.callee.anim = this.fxanim(a,
12273                 o, 'motion', .35, "easeOut", function(){
12274                 el.afterFx(o);
12275             });
12276         });
12277         return this;
12278     },
12279
12280         /**
12281          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
12282          * ending point of the effect.
12283          * Usage:
12284          *<pre><code>
12285 // default: slide the element downward while fading out
12286 el.ghost();
12287
12288 // custom: slide the element out to the right with a 2-second duration
12289 el.ghost('r', { duration: 2 });
12290
12291 // common config options shown with default values
12292 el.ghost('b', {
12293     easing: 'easeOut',
12294     duration: .5
12295     remove: false,
12296     useDisplay: false
12297 });
12298 </code></pre>
12299          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
12300          * @param {Object} options (optional) Object literal with any of the Fx config options
12301          * @return {Roo.Element} The Element
12302          */
12303     ghost : function(anchor, o){
12304         var el = this.getFxEl();
12305         o = o || {};
12306
12307         el.queueFx(o, function(){
12308             anchor = anchor || "b";
12309
12310             // restore values after effect
12311             var r = this.getFxRestore();
12312             var w = this.getWidth(),
12313                 h = this.getHeight();
12314
12315             var st = this.dom.style;
12316
12317             var after = function(){
12318                 if(o.useDisplay){
12319                     el.setDisplayed(false);
12320                 }else{
12321                     el.hide();
12322                 }
12323
12324                 el.clearOpacity();
12325                 el.setPositioning(r.pos);
12326                 st.width = r.width;
12327                 st.height = r.height;
12328
12329                 el.afterFx(o);
12330             };
12331
12332             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
12333             switch(anchor.toLowerCase()){
12334                 case "t":
12335                     pt.by = [0, -h];
12336                 break;
12337                 case "l":
12338                     pt.by = [-w, 0];
12339                 break;
12340                 case "r":
12341                     pt.by = [w, 0];
12342                 break;
12343                 case "b":
12344                     pt.by = [0, h];
12345                 break;
12346                 case "tl":
12347                     pt.by = [-w, -h];
12348                 break;
12349                 case "bl":
12350                     pt.by = [-w, h];
12351                 break;
12352                 case "br":
12353                     pt.by = [w, h];
12354                 break;
12355                 case "tr":
12356                     pt.by = [w, -h];
12357                 break;
12358             }
12359
12360             arguments.callee.anim = this.fxanim(a,
12361                 o,
12362                 'motion',
12363                 .5,
12364                 "easeOut", after);
12365         });
12366         return this;
12367     },
12368
12369         /**
12370          * Ensures that all effects queued after syncFx is called on the element are
12371          * run concurrently.  This is the opposite of {@link #sequenceFx}.
12372          * @return {Roo.Element} The Element
12373          */
12374     syncFx : function(){
12375         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
12376             block : false,
12377             concurrent : true,
12378             stopFx : false
12379         });
12380         return this;
12381     },
12382
12383         /**
12384          * Ensures that all effects queued after sequenceFx is called on the element are
12385          * run in sequence.  This is the opposite of {@link #syncFx}.
12386          * @return {Roo.Element} The Element
12387          */
12388     sequenceFx : function(){
12389         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
12390             block : false,
12391             concurrent : false,
12392             stopFx : false
12393         });
12394         return this;
12395     },
12396
12397         /* @private */
12398     nextFx : function(){
12399         var ef = this.fxQueue[0];
12400         if(ef){
12401             ef.call(this);
12402         }
12403     },
12404
12405         /**
12406          * Returns true if the element has any effects actively running or queued, else returns false.
12407          * @return {Boolean} True if element has active effects, else false
12408          */
12409     hasActiveFx : function(){
12410         return this.fxQueue && this.fxQueue[0];
12411     },
12412
12413         /**
12414          * Stops any running effects and clears the element's internal effects queue if it contains
12415          * any additional effects that haven't started yet.
12416          * @return {Roo.Element} The Element
12417          */
12418     stopFx : function(){
12419         if(this.hasActiveFx()){
12420             var cur = this.fxQueue[0];
12421             if(cur && cur.anim && cur.anim.isAnimated()){
12422                 this.fxQueue = [cur]; // clear out others
12423                 cur.anim.stop(true);
12424             }
12425         }
12426         return this;
12427     },
12428
12429         /* @private */
12430     beforeFx : function(o){
12431         if(this.hasActiveFx() && !o.concurrent){
12432            if(o.stopFx){
12433                this.stopFx();
12434                return true;
12435            }
12436            return false;
12437         }
12438         return true;
12439     },
12440
12441         /**
12442          * Returns true if the element is currently blocking so that no other effect can be queued
12443          * until this effect is finished, else returns false if blocking is not set.  This is commonly
12444          * used to ensure that an effect initiated by a user action runs to completion prior to the
12445          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
12446          * @return {Boolean} True if blocking, else false
12447          */
12448     hasFxBlock : function(){
12449         var q = this.fxQueue;
12450         return q && q[0] && q[0].block;
12451     },
12452
12453         /* @private */
12454     queueFx : function(o, fn){
12455         if(!this.fxQueue){
12456             this.fxQueue = [];
12457         }
12458         if(!this.hasFxBlock()){
12459             Roo.applyIf(o, this.fxDefaults);
12460             if(!o.concurrent){
12461                 var run = this.beforeFx(o);
12462                 fn.block = o.block;
12463                 this.fxQueue.push(fn);
12464                 if(run){
12465                     this.nextFx();
12466                 }
12467             }else{
12468                 fn.call(this);
12469             }
12470         }
12471         return this;
12472     },
12473
12474         /* @private */
12475     fxWrap : function(pos, o, vis){
12476         var wrap;
12477         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
12478             var wrapXY;
12479             if(o.fixPosition){
12480                 wrapXY = this.getXY();
12481             }
12482             var div = document.createElement("div");
12483             div.style.visibility = vis;
12484             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
12485             wrap.setPositioning(pos);
12486             if(wrap.getStyle("position") == "static"){
12487                 wrap.position("relative");
12488             }
12489             this.clearPositioning('auto');
12490             wrap.clip();
12491             wrap.dom.appendChild(this.dom);
12492             if(wrapXY){
12493                 wrap.setXY(wrapXY);
12494             }
12495         }
12496         return wrap;
12497     },
12498
12499         /* @private */
12500     fxUnwrap : function(wrap, pos, o){
12501         this.clearPositioning();
12502         this.setPositioning(pos);
12503         if(!o.wrap){
12504             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
12505             wrap.remove();
12506         }
12507     },
12508
12509         /* @private */
12510     getFxRestore : function(){
12511         var st = this.dom.style;
12512         return {pos: this.getPositioning(), width: st.width, height : st.height};
12513     },
12514
12515         /* @private */
12516     afterFx : function(o){
12517         if(o.afterStyle){
12518             this.applyStyles(o.afterStyle);
12519         }
12520         if(o.afterCls){
12521             this.addClass(o.afterCls);
12522         }
12523         if(o.remove === true){
12524             this.remove();
12525         }
12526         Roo.callback(o.callback, o.scope, [this]);
12527         if(!o.concurrent){
12528             this.fxQueue.shift();
12529             this.nextFx();
12530         }
12531     },
12532
12533         /* @private */
12534     getFxEl : function(){ // support for composite element fx
12535         return Roo.get(this.dom);
12536     },
12537
12538         /* @private */
12539     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
12540         animType = animType || 'run';
12541         opt = opt || {};
12542         var anim = Roo.lib.Anim[animType](
12543             this.dom, args,
12544             (opt.duration || defaultDur) || .35,
12545             (opt.easing || defaultEase) || 'easeOut',
12546             function(){
12547                 Roo.callback(cb, this);
12548             },
12549             this
12550         );
12551         opt.anim = anim;
12552         return anim;
12553     }
12554 };
12555
12556 // backwords compat
12557 Roo.Fx.resize = Roo.Fx.scale;
12558
12559 //When included, Roo.Fx is automatically applied to Element so that all basic
12560 //effects are available directly via the Element API
12561 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
12562  * Based on:
12563  * Ext JS Library 1.1.1
12564  * Copyright(c) 2006-2007, Ext JS, LLC.
12565  *
12566  * Originally Released Under LGPL - original licence link has changed is not relivant.
12567  *
12568  * Fork - LGPL
12569  * <script type="text/javascript">
12570  */
12571
12572
12573 /**
12574  * @class Roo.CompositeElement
12575  * Standard composite class. Creates a Roo.Element for every element in the collection.
12576  * <br><br>
12577  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12578  * actions will be performed on all the elements in this collection.</b>
12579  * <br><br>
12580  * All methods return <i>this</i> and can be chained.
12581  <pre><code>
12582  var els = Roo.select("#some-el div.some-class", true);
12583  // or select directly from an existing element
12584  var el = Roo.get('some-el');
12585  el.select('div.some-class', true);
12586
12587  els.setWidth(100); // all elements become 100 width
12588  els.hide(true); // all elements fade out and hide
12589  // or
12590  els.setWidth(100).hide(true);
12591  </code></pre>
12592  */
12593 Roo.CompositeElement = function(els){
12594     this.elements = [];
12595     this.addElements(els);
12596 };
12597 Roo.CompositeElement.prototype = {
12598     isComposite: true,
12599     addElements : function(els){
12600         if(!els) {
12601             return this;
12602         }
12603         if(typeof els == "string"){
12604             els = Roo.Element.selectorFunction(els);
12605         }
12606         var yels = this.elements;
12607         var index = yels.length-1;
12608         for(var i = 0, len = els.length; i < len; i++) {
12609                 yels[++index] = Roo.get(els[i]);
12610         }
12611         return this;
12612     },
12613
12614     /**
12615     * Clears this composite and adds the elements returned by the passed selector.
12616     * @param {String/Array} els A string CSS selector, an array of elements or an element
12617     * @return {CompositeElement} this
12618     */
12619     fill : function(els){
12620         this.elements = [];
12621         this.add(els);
12622         return this;
12623     },
12624
12625     /**
12626     * Filters this composite to only elements that match the passed selector.
12627     * @param {String} selector A string CSS selector
12628     * @param {Boolean} inverse return inverse filter (not matches)
12629     * @return {CompositeElement} this
12630     */
12631     filter : function(selector, inverse){
12632         var els = [];
12633         inverse = inverse || false;
12634         this.each(function(el){
12635             var match = inverse ? !el.is(selector) : el.is(selector);
12636             if(match){
12637                 els[els.length] = el.dom;
12638             }
12639         });
12640         this.fill(els);
12641         return this;
12642     },
12643
12644     invoke : function(fn, args){
12645         var els = this.elements;
12646         for(var i = 0, len = els.length; i < len; i++) {
12647                 Roo.Element.prototype[fn].apply(els[i], args);
12648         }
12649         return this;
12650     },
12651     /**
12652     * Adds elements to this composite.
12653     * @param {String/Array} els A string CSS selector, an array of elements or an element
12654     * @return {CompositeElement} this
12655     */
12656     add : function(els){
12657         if(typeof els == "string"){
12658             this.addElements(Roo.Element.selectorFunction(els));
12659         }else if(els.length !== undefined){
12660             this.addElements(els);
12661         }else{
12662             this.addElements([els]);
12663         }
12664         return this;
12665     },
12666     /**
12667     * Calls the passed function passing (el, this, index) for each element in this composite.
12668     * @param {Function} fn The function to call
12669     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12670     * @return {CompositeElement} this
12671     */
12672     each : function(fn, scope){
12673         var els = this.elements;
12674         for(var i = 0, len = els.length; i < len; i++){
12675             if(fn.call(scope || els[i], els[i], this, i) === false) {
12676                 break;
12677             }
12678         }
12679         return this;
12680     },
12681
12682     /**
12683      * Returns the Element object at the specified index
12684      * @param {Number} index
12685      * @return {Roo.Element}
12686      */
12687     item : function(index){
12688         return this.elements[index] || null;
12689     },
12690
12691     /**
12692      * Returns the first Element
12693      * @return {Roo.Element}
12694      */
12695     first : function(){
12696         return this.item(0);
12697     },
12698
12699     /**
12700      * Returns the last Element
12701      * @return {Roo.Element}
12702      */
12703     last : function(){
12704         return this.item(this.elements.length-1);
12705     },
12706
12707     /**
12708      * Returns the number of elements in this composite
12709      * @return Number
12710      */
12711     getCount : function(){
12712         return this.elements.length;
12713     },
12714
12715     /**
12716      * Returns true if this composite contains the passed element
12717      * @return Boolean
12718      */
12719     contains : function(el){
12720         return this.indexOf(el) !== -1;
12721     },
12722
12723     /**
12724      * Returns true if this composite contains the passed element
12725      * @return Boolean
12726      */
12727     indexOf : function(el){
12728         return this.elements.indexOf(Roo.get(el));
12729     },
12730
12731
12732     /**
12733     * Removes the specified element(s).
12734     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
12735     * or an array of any of those.
12736     * @param {Boolean} removeDom (optional) True to also remove the element from the document
12737     * @return {CompositeElement} this
12738     */
12739     removeElement : function(el, removeDom){
12740         if(el instanceof Array){
12741             for(var i = 0, len = el.length; i < len; i++){
12742                 this.removeElement(el[i]);
12743             }
12744             return this;
12745         }
12746         var index = typeof el == 'number' ? el : this.indexOf(el);
12747         if(index !== -1){
12748             if(removeDom){
12749                 var d = this.elements[index];
12750                 if(d.dom){
12751                     d.remove();
12752                 }else{
12753                     d.parentNode.removeChild(d);
12754                 }
12755             }
12756             this.elements.splice(index, 1);
12757         }
12758         return this;
12759     },
12760
12761     /**
12762     * Replaces the specified element with the passed element.
12763     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
12764     * to replace.
12765     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
12766     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
12767     * @return {CompositeElement} this
12768     */
12769     replaceElement : function(el, replacement, domReplace){
12770         var index = typeof el == 'number' ? el : this.indexOf(el);
12771         if(index !== -1){
12772             if(domReplace){
12773                 this.elements[index].replaceWith(replacement);
12774             }else{
12775                 this.elements.splice(index, 1, Roo.get(replacement))
12776             }
12777         }
12778         return this;
12779     },
12780
12781     /**
12782      * Removes all elements.
12783      */
12784     clear : function(){
12785         this.elements = [];
12786     }
12787 };
12788 (function(){
12789     Roo.CompositeElement.createCall = function(proto, fnName){
12790         if(!proto[fnName]){
12791             proto[fnName] = function(){
12792                 return this.invoke(fnName, arguments);
12793             };
12794         }
12795     };
12796     for(var fnName in Roo.Element.prototype){
12797         if(typeof Roo.Element.prototype[fnName] == "function"){
12798             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
12799         }
12800     };
12801 })();
12802 /*
12803  * Based on:
12804  * Ext JS Library 1.1.1
12805  * Copyright(c) 2006-2007, Ext JS, LLC.
12806  *
12807  * Originally Released Under LGPL - original licence link has changed is not relivant.
12808  *
12809  * Fork - LGPL
12810  * <script type="text/javascript">
12811  */
12812
12813 /**
12814  * @class Roo.CompositeElementLite
12815  * @extends Roo.CompositeElement
12816  * Flyweight composite class. Reuses the same Roo.Element for element operations.
12817  <pre><code>
12818  var els = Roo.select("#some-el div.some-class");
12819  // or select directly from an existing element
12820  var el = Roo.get('some-el');
12821  el.select('div.some-class');
12822
12823  els.setWidth(100); // all elements become 100 width
12824  els.hide(true); // all elements fade out and hide
12825  // or
12826  els.setWidth(100).hide(true);
12827  </code></pre><br><br>
12828  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12829  * actions will be performed on all the elements in this collection.</b>
12830  */
12831 Roo.CompositeElementLite = function(els){
12832     Roo.CompositeElementLite.superclass.constructor.call(this, els);
12833     this.el = new Roo.Element.Flyweight();
12834 };
12835 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
12836     addElements : function(els){
12837         if(els){
12838             if(els instanceof Array){
12839                 this.elements = this.elements.concat(els);
12840             }else{
12841                 var yels = this.elements;
12842                 var index = yels.length-1;
12843                 for(var i = 0, len = els.length; i < len; i++) {
12844                     yels[++index] = els[i];
12845                 }
12846             }
12847         }
12848         return this;
12849     },
12850     invoke : function(fn, args){
12851         var els = this.elements;
12852         var el = this.el;
12853         for(var i = 0, len = els.length; i < len; i++) {
12854             el.dom = els[i];
12855                 Roo.Element.prototype[fn].apply(el, args);
12856         }
12857         return this;
12858     },
12859     /**
12860      * Returns a flyweight Element of the dom element object at the specified index
12861      * @param {Number} index
12862      * @return {Roo.Element}
12863      */
12864     item : function(index){
12865         if(!this.elements[index]){
12866             return null;
12867         }
12868         this.el.dom = this.elements[index];
12869         return this.el;
12870     },
12871
12872     // fixes scope with flyweight
12873     addListener : function(eventName, handler, scope, opt){
12874         var els = this.elements;
12875         for(var i = 0, len = els.length; i < len; i++) {
12876             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
12877         }
12878         return this;
12879     },
12880
12881     /**
12882     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
12883     * passed is the flyweight (shared) Roo.Element instance, so if you require a
12884     * a reference to the dom node, use el.dom.</b>
12885     * @param {Function} fn The function to call
12886     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12887     * @return {CompositeElement} this
12888     */
12889     each : function(fn, scope){
12890         var els = this.elements;
12891         var el = this.el;
12892         for(var i = 0, len = els.length; i < len; i++){
12893             el.dom = els[i];
12894                 if(fn.call(scope || el, el, this, i) === false){
12895                 break;
12896             }
12897         }
12898         return this;
12899     },
12900
12901     indexOf : function(el){
12902         return this.elements.indexOf(Roo.getDom(el));
12903     },
12904
12905     replaceElement : function(el, replacement, domReplace){
12906         var index = typeof el == 'number' ? el : this.indexOf(el);
12907         if(index !== -1){
12908             replacement = Roo.getDom(replacement);
12909             if(domReplace){
12910                 var d = this.elements[index];
12911                 d.parentNode.insertBefore(replacement, d);
12912                 d.parentNode.removeChild(d);
12913             }
12914             this.elements.splice(index, 1, replacement);
12915         }
12916         return this;
12917     }
12918 });
12919 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
12920
12921 /*
12922  * Based on:
12923  * Ext JS Library 1.1.1
12924  * Copyright(c) 2006-2007, Ext JS, LLC.
12925  *
12926  * Originally Released Under LGPL - original licence link has changed is not relivant.
12927  *
12928  * Fork - LGPL
12929  * <script type="text/javascript">
12930  */
12931
12932  
12933
12934 /**
12935  * @class Roo.data.Connection
12936  * @extends Roo.util.Observable
12937  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
12938  * either to a configured URL, or to a URL specified at request time. 
12939  * 
12940  * Requests made by this class are asynchronous, and will return immediately. No data from
12941  * the server will be available to the statement immediately following the {@link #request} call.
12942  * To process returned data, use a callback in the request options object, or an event listener.
12943  * 
12944  * Note: If you are doing a file upload, you will not get a normal response object sent back to
12945  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
12946  * The response object is created using the innerHTML of the IFRAME's document as the responseText
12947  * property and, if present, the IFRAME's XML document as the responseXML property.
12948  * 
12949  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
12950  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
12951  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
12952  * standard DOM methods.
12953  * @constructor
12954  * @param {Object} config a configuration object.
12955  */
12956 Roo.data.Connection = function(config){
12957     Roo.apply(this, config);
12958     this.addEvents({
12959         /**
12960          * @event beforerequest
12961          * Fires before a network request is made to retrieve a data object.
12962          * @param {Connection} conn This Connection object.
12963          * @param {Object} options The options config object passed to the {@link #request} method.
12964          */
12965         "beforerequest" : true,
12966         /**
12967          * @event requestcomplete
12968          * Fires if the request was successfully completed.
12969          * @param {Connection} conn This Connection object.
12970          * @param {Object} response The XHR object containing the response data.
12971          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12972          * @param {Object} options The options config object passed to the {@link #request} method.
12973          */
12974         "requestcomplete" : true,
12975         /**
12976          * @event requestexception
12977          * Fires if an error HTTP status was returned from the server.
12978          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
12979          * @param {Connection} conn This Connection object.
12980          * @param {Object} response The XHR object containing the response data.
12981          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12982          * @param {Object} options The options config object passed to the {@link #request} method.
12983          */
12984         "requestexception" : true
12985     });
12986     Roo.data.Connection.superclass.constructor.call(this);
12987 };
12988
12989 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
12990     /**
12991      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12992      */
12993     /**
12994      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12995      * extra parameters to each request made by this object. (defaults to undefined)
12996      */
12997     /**
12998      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12999      *  to each request made by this object. (defaults to undefined)
13000      */
13001     /**
13002      * @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)
13003      */
13004     /**
13005      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
13006      */
13007     timeout : 30000,
13008     /**
13009      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
13010      * @type Boolean
13011      */
13012     autoAbort:false,
13013
13014     /**
13015      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
13016      * @type Boolean
13017      */
13018     disableCaching: true,
13019
13020     /**
13021      * Sends an HTTP request to a remote server.
13022      * @param {Object} options An object which may contain the following properties:<ul>
13023      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
13024      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
13025      * request, a url encoded string or a function to call to get either.</li>
13026      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
13027      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
13028      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
13029      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
13030      * <li>options {Object} The parameter to the request call.</li>
13031      * <li>success {Boolean} True if the request succeeded.</li>
13032      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
13033      * </ul></li>
13034      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
13035      * The callback is passed the following parameters:<ul>
13036      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
13037      * <li>options {Object} The parameter to the request call.</li>
13038      * </ul></li>
13039      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
13040      * The callback is passed the following parameters:<ul>
13041      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
13042      * <li>options {Object} The parameter to the request call.</li>
13043      * </ul></li>
13044      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
13045      * for the callback function. Defaults to the browser window.</li>
13046      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
13047      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
13048      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
13049      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
13050      * params for the post data. Any params will be appended to the URL.</li>
13051      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
13052      * </ul>
13053      * @return {Number} transactionId
13054      */
13055     request : function(o){
13056         if(this.fireEvent("beforerequest", this, o) !== false){
13057             var p = o.params;
13058
13059             if(typeof p == "function"){
13060                 p = p.call(o.scope||window, o);
13061             }
13062             if(typeof p == "object"){
13063                 p = Roo.urlEncode(o.params);
13064             }
13065             if(this.extraParams){
13066                 var extras = Roo.urlEncode(this.extraParams);
13067                 p = p ? (p + '&' + extras) : extras;
13068             }
13069
13070             var url = o.url || this.url;
13071             if(typeof url == 'function'){
13072                 url = url.call(o.scope||window, o);
13073             }
13074
13075             if(o.form){
13076                 var form = Roo.getDom(o.form);
13077                 url = url || form.action;
13078
13079                 var enctype = form.getAttribute("enctype");
13080                 
13081                 if (o.formData) {
13082                     return this.doFormDataUpload(o, url);
13083                 }
13084                 
13085                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
13086                     return this.doFormUpload(o, p, url);
13087                 }
13088                 var f = Roo.lib.Ajax.serializeForm(form);
13089                 p = p ? (p + '&' + f) : f;
13090             }
13091             
13092             if (!o.form && o.formData) {
13093                 o.formData = o.formData === true ? new FormData() : o.formData;
13094                 for (var k in o.params) {
13095                     o.formData.append(k,o.params[k]);
13096                 }
13097                     
13098                 return this.doFormDataUpload(o, url);
13099             }
13100             
13101
13102             var hs = o.headers;
13103             if(this.defaultHeaders){
13104                 hs = Roo.apply(hs || {}, this.defaultHeaders);
13105                 if(!o.headers){
13106                     o.headers = hs;
13107                 }
13108             }
13109
13110             var cb = {
13111                 success: this.handleResponse,
13112                 failure: this.handleFailure,
13113                 scope: this,
13114                 argument: {options: o},
13115                 timeout : o.timeout || this.timeout
13116             };
13117
13118             var method = o.method||this.method||(p ? "POST" : "GET");
13119
13120             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
13121                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
13122             }
13123
13124             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
13125                 if(o.autoAbort){
13126                     this.abort();
13127                 }
13128             }else if(this.autoAbort !== false){
13129                 this.abort();
13130             }
13131
13132             if((method == 'GET' && p) || o.xmlData){
13133                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
13134                 p = '';
13135             }
13136             Roo.lib.Ajax.useDefaultHeader = typeof(o.headers) == 'undefined' || typeof(o.headers['Content-Type']) == 'undefined';
13137             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
13138             Roo.lib.Ajax.useDefaultHeader == true;
13139             return this.transId;
13140         }else{
13141             Roo.callback(o.callback, o.scope, [o, null, null]);
13142             return null;
13143         }
13144     },
13145
13146     /**
13147      * Determine whether this object has a request outstanding.
13148      * @param {Number} transactionId (Optional) defaults to the last transaction
13149      * @return {Boolean} True if there is an outstanding request.
13150      */
13151     isLoading : function(transId){
13152         if(transId){
13153             return Roo.lib.Ajax.isCallInProgress(transId);
13154         }else{
13155             return this.transId ? true : false;
13156         }
13157     },
13158
13159     /**
13160      * Aborts any outstanding request.
13161      * @param {Number} transactionId (Optional) defaults to the last transaction
13162      */
13163     abort : function(transId){
13164         if(transId || this.isLoading()){
13165             Roo.lib.Ajax.abort(transId || this.transId);
13166         }
13167     },
13168
13169     // private
13170     handleResponse : function(response){
13171         this.transId = false;
13172         var options = response.argument.options;
13173         response.argument = options ? options.argument : null;
13174         this.fireEvent("requestcomplete", this, response, options);
13175         Roo.callback(options.success, options.scope, [response, options]);
13176         Roo.callback(options.callback, options.scope, [options, true, response]);
13177     },
13178
13179     // private
13180     handleFailure : function(response, e){
13181         this.transId = false;
13182         var options = response.argument.options;
13183         response.argument = options ? options.argument : null;
13184         this.fireEvent("requestexception", this, response, options, e);
13185         Roo.callback(options.failure, options.scope, [response, options]);
13186         Roo.callback(options.callback, options.scope, [options, false, response]);
13187     },
13188
13189     // private
13190     doFormUpload : function(o, ps, url){
13191         var id = Roo.id();
13192         var frame = document.createElement('iframe');
13193         frame.id = id;
13194         frame.name = id;
13195         frame.className = 'x-hidden';
13196         if(Roo.isIE){
13197             frame.src = Roo.SSL_SECURE_URL;
13198         }
13199         document.body.appendChild(frame);
13200
13201         if(Roo.isIE){
13202            document.frames[id].name = id;
13203         }
13204
13205         var form = Roo.getDom(o.form);
13206         form.target = id;
13207         form.method = 'POST';
13208         form.enctype = form.encoding = 'multipart/form-data';
13209         if(url){
13210             form.action = url;
13211         }
13212
13213         var hiddens, hd;
13214         if(ps){ // add dynamic params
13215             hiddens = [];
13216             ps = Roo.urlDecode(ps, false);
13217             for(var k in ps){
13218                 if(ps.hasOwnProperty(k)){
13219                     hd = document.createElement('input');
13220                     hd.type = 'hidden';
13221                     hd.name = k;
13222                     hd.value = ps[k];
13223                     form.appendChild(hd);
13224                     hiddens.push(hd);
13225                 }
13226             }
13227         }
13228
13229         function cb(){
13230             var r = {  // bogus response object
13231                 responseText : '',
13232                 responseXML : null
13233             };
13234
13235             r.argument = o ? o.argument : null;
13236
13237             try { //
13238                 var doc;
13239                 if(Roo.isIE){
13240                     doc = frame.contentWindow.document;
13241                 }else {
13242                     doc = (frame.contentDocument || window.frames[id].document);
13243                 }
13244                 if(doc && doc.body){
13245                     r.responseText = doc.body.innerHTML;
13246                 }
13247                 if(doc && doc.XMLDocument){
13248                     r.responseXML = doc.XMLDocument;
13249                 }else {
13250                     r.responseXML = doc;
13251                 }
13252             }
13253             catch(e) {
13254                 // ignore
13255             }
13256
13257             Roo.EventManager.removeListener(frame, 'load', cb, this);
13258
13259             this.fireEvent("requestcomplete", this, r, o);
13260             Roo.callback(o.success, o.scope, [r, o]);
13261             Roo.callback(o.callback, o.scope, [o, true, r]);
13262
13263             setTimeout(function(){document.body.removeChild(frame);}, 100);
13264         }
13265
13266         Roo.EventManager.on(frame, 'load', cb, this);
13267         form.submit();
13268
13269         if(hiddens){ // remove dynamic params
13270             for(var i = 0, len = hiddens.length; i < len; i++){
13271                 form.removeChild(hiddens[i]);
13272             }
13273         }
13274     },
13275     // this is a 'formdata version???'
13276     
13277     
13278     doFormDataUpload : function(o,  url)
13279     {
13280         var formData;
13281         if (o.form) {
13282             var form =  Roo.getDom(o.form);
13283             form.enctype = form.encoding = 'multipart/form-data';
13284             formData = o.formData === true ? new FormData(form) : o.formData;
13285         } else {
13286             formData = o.formData === true ? new FormData() : o.formData;
13287         }
13288         
13289       
13290         var cb = {
13291             success: this.handleResponse,
13292             failure: this.handleFailure,
13293             scope: this,
13294             argument: {options: o},
13295             timeout : o.timeout || this.timeout
13296         };
13297  
13298         if(typeof o.autoAbort == 'boolean'){ // options gets top priority
13299             if(o.autoAbort){
13300                 this.abort();
13301             }
13302         }else if(this.autoAbort !== false){
13303             this.abort();
13304         }
13305
13306         //Roo.lib.Ajax.defaultPostHeader = null;
13307         Roo.lib.Ajax.useDefaultHeader = false;
13308         this.transId = Roo.lib.Ajax.request( "POST", url, cb,  formData, o);
13309         Roo.lib.Ajax.useDefaultHeader = true;
13310  
13311          
13312     }
13313     
13314 });
13315 /*
13316  * Based on:
13317  * Ext JS Library 1.1.1
13318  * Copyright(c) 2006-2007, Ext JS, LLC.
13319  *
13320  * Originally Released Under LGPL - original licence link has changed is not relivant.
13321  *
13322  * Fork - LGPL
13323  * <script type="text/javascript">
13324  */
13325  
13326 /**
13327  * Global Ajax request class.
13328  * 
13329  * @class Roo.Ajax
13330  * @extends Roo.data.Connection
13331  * @static
13332  * 
13333  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
13334  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
13335  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
13336  * @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)
13337  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
13338  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
13339  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
13340  */
13341 Roo.Ajax = new Roo.data.Connection({
13342     // fix up the docs
13343     /**
13344      * @scope Roo.Ajax
13345      * @type {Boolear} 
13346      */
13347     autoAbort : false,
13348
13349     /**
13350      * Serialize the passed form into a url encoded string
13351      * @scope Roo.Ajax
13352      * @param {String/HTMLElement} form
13353      * @return {String}
13354      */
13355     serializeForm : function(form){
13356         return Roo.lib.Ajax.serializeForm(form);
13357     }
13358 });/*
13359  * Based on:
13360  * Ext JS Library 1.1.1
13361  * Copyright(c) 2006-2007, Ext JS, LLC.
13362  *
13363  * Originally Released Under LGPL - original licence link has changed is not relivant.
13364  *
13365  * Fork - LGPL
13366  * <script type="text/javascript">
13367  */
13368
13369  
13370 /**
13371  * @class Roo.UpdateManager
13372  * @extends Roo.util.Observable
13373  * Provides AJAX-style update for Element object.<br><br>
13374  * Usage:<br>
13375  * <pre><code>
13376  * // Get it from a Roo.Element object
13377  * var el = Roo.get("foo");
13378  * var mgr = el.getUpdateManager();
13379  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
13380  * ...
13381  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
13382  * <br>
13383  * // or directly (returns the same UpdateManager instance)
13384  * var mgr = new Roo.UpdateManager("myElementId");
13385  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
13386  * mgr.on("update", myFcnNeedsToKnow);
13387  * <br>
13388    // short handed call directly from the element object
13389    Roo.get("foo").load({
13390         url: "bar.php",
13391         scripts:true,
13392         params: "for=bar",
13393         text: "Loading Foo..."
13394    });
13395  * </code></pre>
13396  * @constructor
13397  * Create new UpdateManager directly.
13398  * @param {String/HTMLElement/Roo.Element} el The element to update
13399  * @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).
13400  */
13401 Roo.UpdateManager = function(el, forceNew){
13402     el = Roo.get(el);
13403     if(!forceNew && el.updateManager){
13404         return el.updateManager;
13405     }
13406     /**
13407      * The Element object
13408      * @type Roo.Element
13409      */
13410     this.el = el;
13411     /**
13412      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
13413      * @type String
13414      */
13415     this.defaultUrl = null;
13416
13417     this.addEvents({
13418         /**
13419          * @event beforeupdate
13420          * Fired before an update is made, return false from your handler and the update is cancelled.
13421          * @param {Roo.Element} el
13422          * @param {String/Object/Function} url
13423          * @param {String/Object} params
13424          */
13425         "beforeupdate": true,
13426         /**
13427          * @event update
13428          * Fired after successful update is made.
13429          * @param {Roo.Element} el
13430          * @param {Object} oResponseObject The response Object
13431          */
13432         "update": true,
13433         /**
13434          * @event failure
13435          * Fired on update failure.
13436          * @param {Roo.Element} el
13437          * @param {Object} oResponseObject The response Object
13438          */
13439         "failure": true
13440     });
13441     var d = Roo.UpdateManager.defaults;
13442     /**
13443      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
13444      * @type String
13445      */
13446     this.sslBlankUrl = d.sslBlankUrl;
13447     /**
13448      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
13449      * @type Boolean
13450      */
13451     this.disableCaching = d.disableCaching;
13452     /**
13453      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
13454      * @type String
13455      */
13456     this.indicatorText = d.indicatorText;
13457     /**
13458      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
13459      * @type String
13460      */
13461     this.showLoadIndicator = d.showLoadIndicator;
13462     /**
13463      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
13464      * @type Number
13465      */
13466     this.timeout = d.timeout;
13467
13468     /**
13469      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
13470      * @type Boolean
13471      */
13472     this.loadScripts = d.loadScripts;
13473
13474     /**
13475      * Transaction object of current executing transaction
13476      */
13477     this.transaction = null;
13478
13479     /**
13480      * @private
13481      */
13482     this.autoRefreshProcId = null;
13483     /**
13484      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
13485      * @type Function
13486      */
13487     this.refreshDelegate = this.refresh.createDelegate(this);
13488     /**
13489      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
13490      * @type Function
13491      */
13492     this.updateDelegate = this.update.createDelegate(this);
13493     /**
13494      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
13495      * @type Function
13496      */
13497     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
13498     /**
13499      * @private
13500      */
13501     this.successDelegate = this.processSuccess.createDelegate(this);
13502     /**
13503      * @private
13504      */
13505     this.failureDelegate = this.processFailure.createDelegate(this);
13506
13507     if(!this.renderer){
13508      /**
13509       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
13510       */
13511     this.renderer = new Roo.UpdateManager.BasicRenderer();
13512     }
13513     
13514     Roo.UpdateManager.superclass.constructor.call(this);
13515 };
13516
13517 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
13518     /**
13519      * Get the Element this UpdateManager is bound to
13520      * @return {Roo.Element} The element
13521      */
13522     getEl : function(){
13523         return this.el;
13524     },
13525     /**
13526      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
13527      * @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:
13528 <pre><code>
13529 um.update({<br/>
13530     url: "your-url.php",<br/>
13531     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
13532     callback: yourFunction,<br/>
13533     scope: yourObject, //(optional scope)  <br/>
13534     discardUrl: false, <br/>
13535     nocache: false,<br/>
13536     text: "Loading...",<br/>
13537     timeout: 30,<br/>
13538     scripts: false<br/>
13539 });
13540 </code></pre>
13541      * The only required property is url. The optional properties nocache, text and scripts
13542      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
13543      * @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}
13544      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
13545      * @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.
13546      */
13547     update : function(url, params, callback, discardUrl){
13548         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
13549             var method = this.method,
13550                 cfg;
13551             if(typeof url == "object"){ // must be config object
13552                 cfg = url;
13553                 url = cfg.url;
13554                 params = params || cfg.params;
13555                 callback = callback || cfg.callback;
13556                 discardUrl = discardUrl || cfg.discardUrl;
13557                 if(callback && cfg.scope){
13558                     callback = callback.createDelegate(cfg.scope);
13559                 }
13560                 if(typeof cfg.method != "undefined"){method = cfg.method;};
13561                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
13562                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
13563                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
13564                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
13565             }
13566             this.showLoading();
13567             if(!discardUrl){
13568                 this.defaultUrl = url;
13569             }
13570             if(typeof url == "function"){
13571                 url = url.call(this);
13572             }
13573
13574             method = method || (params ? "POST" : "GET");
13575             if(method == "GET"){
13576                 url = this.prepareUrl(url);
13577             }
13578
13579             var o = Roo.apply(cfg ||{}, {
13580                 url : url,
13581                 params: params,
13582                 success: this.successDelegate,
13583                 failure: this.failureDelegate,
13584                 callback: undefined,
13585                 timeout: (this.timeout*1000),
13586                 argument: {"url": url, "form": null, "callback": callback, "params": params}
13587             });
13588             Roo.log("updated manager called with timeout of " + o.timeout);
13589             this.transaction = Roo.Ajax.request(o);
13590         }
13591     },
13592
13593     /**
13594      * 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.
13595      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
13596      * @param {String/HTMLElement} form The form Id or form element
13597      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
13598      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
13599      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
13600      */
13601     formUpdate : function(form, url, reset, callback){
13602         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
13603             if(typeof url == "function"){
13604                 url = url.call(this);
13605             }
13606             form = Roo.getDom(form);
13607             this.transaction = Roo.Ajax.request({
13608                 form: form,
13609                 url:url,
13610                 success: this.successDelegate,
13611                 failure: this.failureDelegate,
13612                 timeout: (this.timeout*1000),
13613                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
13614             });
13615             this.showLoading.defer(1, this);
13616         }
13617     },
13618
13619     /**
13620      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
13621      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13622      */
13623     refresh : function(callback){
13624         if(this.defaultUrl == null){
13625             return;
13626         }
13627         this.update(this.defaultUrl, null, callback, true);
13628     },
13629
13630     /**
13631      * Set this element to auto refresh.
13632      * @param {Number} interval How often to update (in seconds).
13633      * @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)
13634      * @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}
13635      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13636      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
13637      */
13638     startAutoRefresh : function(interval, url, params, callback, refreshNow){
13639         if(refreshNow){
13640             this.update(url || this.defaultUrl, params, callback, true);
13641         }
13642         if(this.autoRefreshProcId){
13643             clearInterval(this.autoRefreshProcId);
13644         }
13645         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
13646     },
13647
13648     /**
13649      * Stop auto refresh on this element.
13650      */
13651      stopAutoRefresh : function(){
13652         if(this.autoRefreshProcId){
13653             clearInterval(this.autoRefreshProcId);
13654             delete this.autoRefreshProcId;
13655         }
13656     },
13657
13658     isAutoRefreshing : function(){
13659        return this.autoRefreshProcId ? true : false;
13660     },
13661     /**
13662      * Called to update the element to "Loading" state. Override to perform custom action.
13663      */
13664     showLoading : function(){
13665         if(this.showLoadIndicator){
13666             this.el.update(this.indicatorText);
13667         }
13668     },
13669
13670     /**
13671      * Adds unique parameter to query string if disableCaching = true
13672      * @private
13673      */
13674     prepareUrl : function(url){
13675         if(this.disableCaching){
13676             var append = "_dc=" + (new Date().getTime());
13677             if(url.indexOf("?") !== -1){
13678                 url += "&" + append;
13679             }else{
13680                 url += "?" + append;
13681             }
13682         }
13683         return url;
13684     },
13685
13686     /**
13687      * @private
13688      */
13689     processSuccess : function(response){
13690         this.transaction = null;
13691         if(response.argument.form && response.argument.reset){
13692             try{ // put in try/catch since some older FF releases had problems with this
13693                 response.argument.form.reset();
13694             }catch(e){}
13695         }
13696         if(this.loadScripts){
13697             this.renderer.render(this.el, response, this,
13698                 this.updateComplete.createDelegate(this, [response]));
13699         }else{
13700             this.renderer.render(this.el, response, this);
13701             this.updateComplete(response);
13702         }
13703     },
13704
13705     updateComplete : function(response){
13706         this.fireEvent("update", this.el, response);
13707         if(typeof response.argument.callback == "function"){
13708             response.argument.callback(this.el, true, response);
13709         }
13710     },
13711
13712     /**
13713      * @private
13714      */
13715     processFailure : function(response){
13716         this.transaction = null;
13717         this.fireEvent("failure", this.el, response);
13718         if(typeof response.argument.callback == "function"){
13719             response.argument.callback(this.el, false, response);
13720         }
13721     },
13722
13723     /**
13724      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
13725      * @param {Object} renderer The object implementing the render() method
13726      */
13727     setRenderer : function(renderer){
13728         this.renderer = renderer;
13729     },
13730
13731     getRenderer : function(){
13732        return this.renderer;
13733     },
13734
13735     /**
13736      * Set the defaultUrl used for updates
13737      * @param {String/Function} defaultUrl The url or a function to call to get the url
13738      */
13739     setDefaultUrl : function(defaultUrl){
13740         this.defaultUrl = defaultUrl;
13741     },
13742
13743     /**
13744      * Aborts the executing transaction
13745      */
13746     abort : function(){
13747         if(this.transaction){
13748             Roo.Ajax.abort(this.transaction);
13749         }
13750     },
13751
13752     /**
13753      * Returns true if an update is in progress
13754      * @return {Boolean}
13755      */
13756     isUpdating : function(){
13757         if(this.transaction){
13758             return Roo.Ajax.isLoading(this.transaction);
13759         }
13760         return false;
13761     }
13762 });
13763
13764 /**
13765  * @class Roo.UpdateManager.defaults
13766  * @static (not really - but it helps the doc tool)
13767  * The defaults collection enables customizing the default properties of UpdateManager
13768  */
13769    Roo.UpdateManager.defaults = {
13770        /**
13771          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
13772          * @type Number
13773          */
13774          timeout : 30,
13775
13776          /**
13777          * True to process scripts by default (Defaults to false).
13778          * @type Boolean
13779          */
13780         loadScripts : false,
13781
13782         /**
13783         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
13784         * @type String
13785         */
13786         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
13787         /**
13788          * Whether to append unique parameter on get request to disable caching (Defaults to false).
13789          * @type Boolean
13790          */
13791         disableCaching : false,
13792         /**
13793          * Whether to show indicatorText when loading (Defaults to true).
13794          * @type Boolean
13795          */
13796         showLoadIndicator : true,
13797         /**
13798          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
13799          * @type String
13800          */
13801         indicatorText : '<div class="loading-indicator">Loading...</div>'
13802    };
13803
13804 /**
13805  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
13806  *Usage:
13807  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
13808  * @param {String/HTMLElement/Roo.Element} el The element to update
13809  * @param {String} url The url
13810  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
13811  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
13812  * @static
13813  * @deprecated
13814  * @member Roo.UpdateManager
13815  */
13816 Roo.UpdateManager.updateElement = function(el, url, params, options){
13817     var um = Roo.get(el, true).getUpdateManager();
13818     Roo.apply(um, options);
13819     um.update(url, params, options ? options.callback : null);
13820 };
13821 // alias for backwards compat
13822 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
13823 /**
13824  * @class Roo.UpdateManager.BasicRenderer
13825  * Default Content renderer. Updates the elements innerHTML with the responseText.
13826  */
13827 Roo.UpdateManager.BasicRenderer = function(){};
13828
13829 Roo.UpdateManager.BasicRenderer.prototype = {
13830     /**
13831      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
13832      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
13833      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
13834      * @param {Roo.Element} el The element being rendered
13835      * @param {Object} response The YUI Connect response object
13836      * @param {UpdateManager} updateManager The calling update manager
13837      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
13838      */
13839      render : function(el, response, updateManager, callback){
13840         el.update(response.responseText, updateManager.loadScripts, callback);
13841     }
13842 };
13843 /*
13844  * Based on:
13845  * Roo JS
13846  * (c)) Alan Knowles
13847  * Licence : LGPL
13848  */
13849
13850
13851 /**
13852  * @class Roo.DomTemplate
13853  * @extends Roo.Template
13854  * An effort at a dom based template engine..
13855  *
13856  * Similar to XTemplate, except it uses dom parsing to create the template..
13857  *
13858  * Supported features:
13859  *
13860  *  Tags:
13861
13862 <pre><code>
13863       {a_variable} - output encoded.
13864       {a_variable.format:("Y-m-d")} - call a method on the variable
13865       {a_variable:raw} - unencoded output
13866       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
13867       {a_variable:this.method_on_template(...)} - call a method on the template object.
13868  
13869 </code></pre>
13870  *  The tpl tag:
13871 <pre><code>
13872         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
13873         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
13874         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
13875         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
13876   
13877 </code></pre>
13878  *      
13879  */
13880 Roo.DomTemplate = function()
13881 {
13882      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
13883      if (this.html) {
13884         this.compile();
13885      }
13886 };
13887
13888
13889 Roo.extend(Roo.DomTemplate, Roo.Template, {
13890     /**
13891      * id counter for sub templates.
13892      */
13893     id : 0,
13894     /**
13895      * flag to indicate if dom parser is inside a pre,
13896      * it will strip whitespace if not.
13897      */
13898     inPre : false,
13899     
13900     /**
13901      * The various sub templates
13902      */
13903     tpls : false,
13904     
13905     
13906     
13907     /**
13908      *
13909      * basic tag replacing syntax
13910      * WORD:WORD()
13911      *
13912      * // you can fake an object call by doing this
13913      *  x.t:(test,tesT) 
13914      * 
13915      */
13916     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
13917     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
13918     
13919     iterChild : function (node, method) {
13920         
13921         var oldPre = this.inPre;
13922         if (node.tagName == 'PRE') {
13923             this.inPre = true;
13924         }
13925         for( var i = 0; i < node.childNodes.length; i++) {
13926             method.call(this, node.childNodes[i]);
13927         }
13928         this.inPre = oldPre;
13929     },
13930     
13931     
13932     
13933     /**
13934      * compile the template
13935      *
13936      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
13937      *
13938      */
13939     compile: function()
13940     {
13941         var s = this.html;
13942         
13943         // covert the html into DOM...
13944         var doc = false;
13945         var div =false;
13946         try {
13947             doc = document.implementation.createHTMLDocument("");
13948             doc.documentElement.innerHTML =   this.html  ;
13949             div = doc.documentElement;
13950         } catch (e) {
13951             // old IE... - nasty -- it causes all sorts of issues.. with
13952             // images getting pulled from server..
13953             div = document.createElement('div');
13954             div.innerHTML = this.html;
13955         }
13956         //doc.documentElement.innerHTML = htmlBody
13957          
13958         
13959         
13960         this.tpls = [];
13961         var _t = this;
13962         this.iterChild(div, function(n) {_t.compileNode(n, true); });
13963         
13964         var tpls = this.tpls;
13965         
13966         // create a top level template from the snippet..
13967         
13968         //Roo.log(div.innerHTML);
13969         
13970         var tpl = {
13971             uid : 'master',
13972             id : this.id++,
13973             attr : false,
13974             value : false,
13975             body : div.innerHTML,
13976             
13977             forCall : false,
13978             execCall : false,
13979             dom : div,
13980             isTop : true
13981             
13982         };
13983         tpls.unshift(tpl);
13984         
13985         
13986         // compile them...
13987         this.tpls = [];
13988         Roo.each(tpls, function(tp){
13989             this.compileTpl(tp);
13990             this.tpls[tp.id] = tp;
13991         }, this);
13992         
13993         this.master = tpls[0];
13994         return this;
13995         
13996         
13997     },
13998     
13999     compileNode : function(node, istop) {
14000         // test for
14001         //Roo.log(node);
14002         
14003         
14004         // skip anything not a tag..
14005         if (node.nodeType != 1) {
14006             if (node.nodeType == 3 && !this.inPre) {
14007                 // reduce white space..
14008                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
14009                 
14010             }
14011             return;
14012         }
14013         
14014         var tpl = {
14015             uid : false,
14016             id : false,
14017             attr : false,
14018             value : false,
14019             body : '',
14020             
14021             forCall : false,
14022             execCall : false,
14023             dom : false,
14024             isTop : istop
14025             
14026             
14027         };
14028         
14029         
14030         switch(true) {
14031             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
14032             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
14033             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
14034             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
14035             // no default..
14036         }
14037         
14038         
14039         if (!tpl.attr) {
14040             // just itterate children..
14041             this.iterChild(node,this.compileNode);
14042             return;
14043         }
14044         tpl.uid = this.id++;
14045         tpl.value = node.getAttribute('roo-' +  tpl.attr);
14046         node.removeAttribute('roo-'+ tpl.attr);
14047         if (tpl.attr != 'name') {
14048             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
14049             node.parentNode.replaceChild(placeholder,  node);
14050         } else {
14051             
14052             var placeholder =  document.createElement('span');
14053             placeholder.className = 'roo-tpl-' + tpl.value;
14054             node.parentNode.replaceChild(placeholder,  node);
14055         }
14056         
14057         // parent now sees '{domtplXXXX}
14058         this.iterChild(node,this.compileNode);
14059         
14060         // we should now have node body...
14061         var div = document.createElement('div');
14062         div.appendChild(node);
14063         tpl.dom = node;
14064         // this has the unfortunate side effect of converting tagged attributes
14065         // eg. href="{...}" into %7C...%7D
14066         // this has been fixed by searching for those combo's although it's a bit hacky..
14067         
14068         
14069         tpl.body = div.innerHTML;
14070         
14071         
14072          
14073         tpl.id = tpl.uid;
14074         switch(tpl.attr) {
14075             case 'for' :
14076                 switch (tpl.value) {
14077                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
14078                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
14079                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
14080                 }
14081                 break;
14082             
14083             case 'exec':
14084                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
14085                 break;
14086             
14087             case 'if':     
14088                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
14089                 break;
14090             
14091             case 'name':
14092                 tpl.id  = tpl.value; // replace non characters???
14093                 break;
14094             
14095         }
14096         
14097         
14098         this.tpls.push(tpl);
14099         
14100         
14101         
14102     },
14103     
14104     
14105     
14106     
14107     /**
14108      * Compile a segment of the template into a 'sub-template'
14109      *
14110      * 
14111      * 
14112      *
14113      */
14114     compileTpl : function(tpl)
14115     {
14116         var fm = Roo.util.Format;
14117         var useF = this.disableFormats !== true;
14118         
14119         var sep = Roo.isGecko ? "+\n" : ",\n";
14120         
14121         var undef = function(str) {
14122             Roo.debug && Roo.log("Property not found :"  + str);
14123             return '';
14124         };
14125           
14126         //Roo.log(tpl.body);
14127         
14128         
14129         
14130         var fn = function(m, lbrace, name, format, args)
14131         {
14132             //Roo.log("ARGS");
14133             //Roo.log(arguments);
14134             args = args ? args.replace(/\\'/g,"'") : args;
14135             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
14136             if (typeof(format) == 'undefined') {
14137                 format =  'htmlEncode'; 
14138             }
14139             if (format == 'raw' ) {
14140                 format = false;
14141             }
14142             
14143             if(name.substr(0, 6) == 'domtpl'){
14144                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
14145             }
14146             
14147             // build an array of options to determine if value is undefined..
14148             
14149             // basically get 'xxxx.yyyy' then do
14150             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
14151             //    (function () { Roo.log("Property not found"); return ''; })() :
14152             //    ......
14153             
14154             var udef_ar = [];
14155             var lookfor = '';
14156             Roo.each(name.split('.'), function(st) {
14157                 lookfor += (lookfor.length ? '.': '') + st;
14158                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
14159             });
14160             
14161             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
14162             
14163             
14164             if(format && useF){
14165                 
14166                 args = args ? ',' + args : "";
14167                  
14168                 if(format.substr(0, 5) != "this."){
14169                     format = "fm." + format + '(';
14170                 }else{
14171                     format = 'this.call("'+ format.substr(5) + '", ';
14172                     args = ", values";
14173                 }
14174                 
14175                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
14176             }
14177              
14178             if (args && args.length) {
14179                 // called with xxyx.yuu:(test,test)
14180                 // change to ()
14181                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
14182             }
14183             // raw.. - :raw modifier..
14184             return "'"+ sep + udef_st  + name + ")"+sep+"'";
14185             
14186         };
14187         var body;
14188         // branched to use + in gecko and [].join() in others
14189         if(Roo.isGecko){
14190             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
14191                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
14192                     "';};};";
14193         }else{
14194             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
14195             body.push(tpl.body.replace(/(\r\n|\n)/g,
14196                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
14197             body.push("'].join('');};};");
14198             body = body.join('');
14199         }
14200         
14201         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
14202        
14203         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
14204         eval(body);
14205         
14206         return this;
14207     },
14208      
14209     /**
14210      * same as applyTemplate, except it's done to one of the subTemplates
14211      * when using named templates, you can do:
14212      *
14213      * var str = pl.applySubTemplate('your-name', values);
14214      *
14215      * 
14216      * @param {Number} id of the template
14217      * @param {Object} values to apply to template
14218      * @param {Object} parent (normaly the instance of this object)
14219      */
14220     applySubTemplate : function(id, values, parent)
14221     {
14222         
14223         
14224         var t = this.tpls[id];
14225         
14226         
14227         try { 
14228             if(t.ifCall && !t.ifCall.call(this, values, parent)){
14229                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
14230                 return '';
14231             }
14232         } catch(e) {
14233             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
14234             Roo.log(values);
14235           
14236             return '';
14237         }
14238         try { 
14239             
14240             if(t.execCall && t.execCall.call(this, values, parent)){
14241                 return '';
14242             }
14243         } catch(e) {
14244             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
14245             Roo.log(values);
14246             return '';
14247         }
14248         
14249         try {
14250             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
14251             parent = t.target ? values : parent;
14252             if(t.forCall && vs instanceof Array){
14253                 var buf = [];
14254                 for(var i = 0, len = vs.length; i < len; i++){
14255                     try {
14256                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
14257                     } catch (e) {
14258                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
14259                         Roo.log(e.body);
14260                         //Roo.log(t.compiled);
14261                         Roo.log(vs[i]);
14262                     }   
14263                 }
14264                 return buf.join('');
14265             }
14266         } catch (e) {
14267             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
14268             Roo.log(values);
14269             return '';
14270         }
14271         try {
14272             return t.compiled.call(this, vs, parent);
14273         } catch (e) {
14274             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
14275             Roo.log(e.body);
14276             //Roo.log(t.compiled);
14277             Roo.log(values);
14278             return '';
14279         }
14280     },
14281
14282    
14283
14284     applyTemplate : function(values){
14285         return this.master.compiled.call(this, values, {});
14286         //var s = this.subs;
14287     },
14288
14289     apply : function(){
14290         return this.applyTemplate.apply(this, arguments);
14291     }
14292
14293  });
14294
14295 Roo.DomTemplate.from = function(el){
14296     el = Roo.getDom(el);
14297     return new Roo.Domtemplate(el.value || el.innerHTML);
14298 };/*
14299  * Based on:
14300  * Ext JS Library 1.1.1
14301  * Copyright(c) 2006-2007, Ext JS, LLC.
14302  *
14303  * Originally Released Under LGPL - original licence link has changed is not relivant.
14304  *
14305  * Fork - LGPL
14306  * <script type="text/javascript">
14307  */
14308
14309 /**
14310  * @class Roo.util.DelayedTask
14311  * Provides a convenient method of performing setTimeout where a new
14312  * timeout cancels the old timeout. An example would be performing validation on a keypress.
14313  * You can use this class to buffer
14314  * the keypress events for a certain number of milliseconds, and perform only if they stop
14315  * for that amount of time.
14316  * @constructor The parameters to this constructor serve as defaults and are not required.
14317  * @param {Function} fn (optional) The default function to timeout
14318  * @param {Object} scope (optional) The default scope of that timeout
14319  * @param {Array} args (optional) The default Array of arguments
14320  */
14321 Roo.util.DelayedTask = function(fn, scope, args){
14322     var id = null, d, t;
14323
14324     var call = function(){
14325         var now = new Date().getTime();
14326         if(now - t >= d){
14327             clearInterval(id);
14328             id = null;
14329             fn.apply(scope, args || []);
14330         }
14331     };
14332     /**
14333      * Cancels any pending timeout and queues a new one
14334      * @param {Number} delay The milliseconds to delay
14335      * @param {Function} newFn (optional) Overrides function passed to constructor
14336      * @param {Object} newScope (optional) Overrides scope passed to constructor
14337      * @param {Array} newArgs (optional) Overrides args passed to constructor
14338      */
14339     this.delay = function(delay, newFn, newScope, newArgs){
14340         if(id && delay != d){
14341             this.cancel();
14342         }
14343         d = delay;
14344         t = new Date().getTime();
14345         fn = newFn || fn;
14346         scope = newScope || scope;
14347         args = newArgs || args;
14348         if(!id){
14349             id = setInterval(call, d);
14350         }
14351     };
14352
14353     /**
14354      * Cancel the last queued timeout
14355      */
14356     this.cancel = function(){
14357         if(id){
14358             clearInterval(id);
14359             id = null;
14360         }
14361     };
14362 };/*
14363  * Based on:
14364  * Ext JS Library 1.1.1
14365  * Copyright(c) 2006-2007, Ext JS, LLC.
14366  *
14367  * Originally Released Under LGPL - original licence link has changed is not relivant.
14368  *
14369  * Fork - LGPL
14370  * <script type="text/javascript">
14371  */
14372 /**
14373  * @class Roo.util.TaskRunner
14374  * Manage background tasks - not sure why this is better that setInterval?
14375  * @static
14376  *
14377  */
14378  
14379 Roo.util.TaskRunner = function(interval){
14380     interval = interval || 10;
14381     var tasks = [], removeQueue = [];
14382     var id = 0;
14383     var running = false;
14384
14385     var stopThread = function(){
14386         running = false;
14387         clearInterval(id);
14388         id = 0;
14389     };
14390
14391     var startThread = function(){
14392         if(!running){
14393             running = true;
14394             id = setInterval(runTasks, interval);
14395         }
14396     };
14397
14398     var removeTask = function(task){
14399         removeQueue.push(task);
14400         if(task.onStop){
14401             task.onStop();
14402         }
14403     };
14404
14405     var runTasks = function(){
14406         if(removeQueue.length > 0){
14407             for(var i = 0, len = removeQueue.length; i < len; i++){
14408                 tasks.remove(removeQueue[i]);
14409             }
14410             removeQueue = [];
14411             if(tasks.length < 1){
14412                 stopThread();
14413                 return;
14414             }
14415         }
14416         var now = new Date().getTime();
14417         for(var i = 0, len = tasks.length; i < len; ++i){
14418             var t = tasks[i];
14419             var itime = now - t.taskRunTime;
14420             if(t.interval <= itime){
14421                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
14422                 t.taskRunTime = now;
14423                 if(rt === false || t.taskRunCount === t.repeat){
14424                     removeTask(t);
14425                     return;
14426                 }
14427             }
14428             if(t.duration && t.duration <= (now - t.taskStartTime)){
14429                 removeTask(t);
14430             }
14431         }
14432     };
14433
14434     /**
14435      * Queues a new task.
14436      * @param {Object} task
14437      *
14438      * Task property : interval = how frequent to run.
14439      * Task object should implement
14440      * function run()
14441      * Task object may implement
14442      * function onStop()
14443      */
14444     this.start = function(task){
14445         tasks.push(task);
14446         task.taskStartTime = new Date().getTime();
14447         task.taskRunTime = 0;
14448         task.taskRunCount = 0;
14449         startThread();
14450         return task;
14451     };
14452     /**
14453      * Stop  new task.
14454      * @param {Object} task
14455      */
14456     this.stop = function(task){
14457         removeTask(task);
14458         return task;
14459     };
14460     /**
14461      * Stop all Tasks
14462      */
14463     this.stopAll = function(){
14464         stopThread();
14465         for(var i = 0, len = tasks.length; i < len; i++){
14466             if(tasks[i].onStop){
14467                 tasks[i].onStop();
14468             }
14469         }
14470         tasks = [];
14471         removeQueue = [];
14472     };
14473 };
14474
14475 Roo.TaskMgr = new Roo.util.TaskRunner();/*
14476  * Based on:
14477  * Ext JS Library 1.1.1
14478  * Copyright(c) 2006-2007, Ext JS, LLC.
14479  *
14480  * Originally Released Under LGPL - original licence link has changed is not relivant.
14481  *
14482  * Fork - LGPL
14483  * <script type="text/javascript">
14484  */
14485
14486  
14487 /**
14488  * @class Roo.util.MixedCollection
14489  * @extends Roo.util.Observable
14490  * A Collection class that maintains both numeric indexes and keys and exposes events.
14491  * @constructor
14492  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
14493  * collection (defaults to false)
14494  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
14495  * and return the key value for that item.  This is used when available to look up the key on items that
14496  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
14497  * equivalent to providing an implementation for the {@link #getKey} method.
14498  */
14499 Roo.util.MixedCollection = function(allowFunctions, keyFn){
14500     this.items = [];
14501     this.map = {};
14502     this.keys = [];
14503     this.length = 0;
14504     this.addEvents({
14505         /**
14506          * @event clear
14507          * Fires when the collection is cleared.
14508          */
14509         "clear" : true,
14510         /**
14511          * @event add
14512          * Fires when an item is added to the collection.
14513          * @param {Number} index The index at which the item was added.
14514          * @param {Object} o The item added.
14515          * @param {String} key The key associated with the added item.
14516          */
14517         "add" : true,
14518         /**
14519          * @event replace
14520          * Fires when an item is replaced in the collection.
14521          * @param {String} key he key associated with the new added.
14522          * @param {Object} old The item being replaced.
14523          * @param {Object} new The new item.
14524          */
14525         "replace" : true,
14526         /**
14527          * @event remove
14528          * Fires when an item is removed from the collection.
14529          * @param {Object} o The item being removed.
14530          * @param {String} key (optional) The key associated with the removed item.
14531          */
14532         "remove" : true,
14533         "sort" : true
14534     });
14535     this.allowFunctions = allowFunctions === true;
14536     if(keyFn){
14537         this.getKey = keyFn;
14538     }
14539     Roo.util.MixedCollection.superclass.constructor.call(this);
14540 };
14541
14542 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
14543     allowFunctions : false,
14544     
14545 /**
14546  * Adds an item to the collection.
14547  * @param {String} key The key to associate with the item
14548  * @param {Object} o The item to add.
14549  * @return {Object} The item added.
14550  */
14551     add : function(key, o){
14552         if(arguments.length == 1){
14553             o = arguments[0];
14554             key = this.getKey(o);
14555         }
14556         if(typeof key == "undefined" || key === null){
14557             this.length++;
14558             this.items.push(o);
14559             this.keys.push(null);
14560         }else{
14561             var old = this.map[key];
14562             if(old){
14563                 return this.replace(key, o);
14564             }
14565             this.length++;
14566             this.items.push(o);
14567             this.map[key] = o;
14568             this.keys.push(key);
14569         }
14570         this.fireEvent("add", this.length-1, o, key);
14571         return o;
14572     },
14573        
14574 /**
14575   * MixedCollection has a generic way to fetch keys if you implement getKey.
14576 <pre><code>
14577 // normal way
14578 var mc = new Roo.util.MixedCollection();
14579 mc.add(someEl.dom.id, someEl);
14580 mc.add(otherEl.dom.id, otherEl);
14581 //and so on
14582
14583 // using getKey
14584 var mc = new Roo.util.MixedCollection();
14585 mc.getKey = function(el){
14586    return el.dom.id;
14587 };
14588 mc.add(someEl);
14589 mc.add(otherEl);
14590
14591 // or via the constructor
14592 var mc = new Roo.util.MixedCollection(false, function(el){
14593    return el.dom.id;
14594 });
14595 mc.add(someEl);
14596 mc.add(otherEl);
14597 </code></pre>
14598  * @param o {Object} The item for which to find the key.
14599  * @return {Object} The key for the passed item.
14600  */
14601     getKey : function(o){
14602          return o.id; 
14603     },
14604    
14605 /**
14606  * Replaces an item in the collection.
14607  * @param {String} key The key associated with the item to replace, or the item to replace.
14608  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
14609  * @return {Object}  The new item.
14610  */
14611     replace : function(key, o){
14612         if(arguments.length == 1){
14613             o = arguments[0];
14614             key = this.getKey(o);
14615         }
14616         var old = this.item(key);
14617         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
14618              return this.add(key, o);
14619         }
14620         var index = this.indexOfKey(key);
14621         this.items[index] = o;
14622         this.map[key] = o;
14623         this.fireEvent("replace", key, old, o);
14624         return o;
14625     },
14626    
14627 /**
14628  * Adds all elements of an Array or an Object to the collection.
14629  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
14630  * an Array of values, each of which are added to the collection.
14631  */
14632     addAll : function(objs){
14633         if(arguments.length > 1 || objs instanceof Array){
14634             var args = arguments.length > 1 ? arguments : objs;
14635             for(var i = 0, len = args.length; i < len; i++){
14636                 this.add(args[i]);
14637             }
14638         }else{
14639             for(var key in objs){
14640                 if(this.allowFunctions || typeof objs[key] != "function"){
14641                     this.add(key, objs[key]);
14642                 }
14643             }
14644         }
14645     },
14646    
14647 /**
14648  * Executes the specified function once for every item in the collection, passing each
14649  * item as the first and only parameter. returning false from the function will stop the iteration.
14650  * @param {Function} fn The function to execute for each item.
14651  * @param {Object} scope (optional) The scope in which to execute the function.
14652  */
14653     each : function(fn, scope){
14654         var items = [].concat(this.items); // each safe for removal
14655         for(var i = 0, len = items.length; i < len; i++){
14656             if(fn.call(scope || items[i], items[i], i, len) === false){
14657                 break;
14658             }
14659         }
14660     },
14661    
14662 /**
14663  * Executes the specified function once for every key in the collection, passing each
14664  * key, and its associated item as the first two parameters.
14665  * @param {Function} fn The function to execute for each item.
14666  * @param {Object} scope (optional) The scope in which to execute the function.
14667  */
14668     eachKey : function(fn, scope){
14669         for(var i = 0, len = this.keys.length; i < len; i++){
14670             fn.call(scope || window, this.keys[i], this.items[i], i, len);
14671         }
14672     },
14673    
14674 /**
14675  * Returns the first item in the collection which elicits a true return value from the
14676  * passed selection function.
14677  * @param {Function} fn The selection function to execute for each item.
14678  * @param {Object} scope (optional) The scope in which to execute the function.
14679  * @return {Object} The first item in the collection which returned true from the selection function.
14680  */
14681     find : function(fn, scope){
14682         for(var i = 0, len = this.items.length; i < len; i++){
14683             if(fn.call(scope || window, this.items[i], this.keys[i])){
14684                 return this.items[i];
14685             }
14686         }
14687         return null;
14688     },
14689    
14690 /**
14691  * Inserts an item at the specified index in the collection.
14692  * @param {Number} index The index to insert the item at.
14693  * @param {String} key The key to associate with the new item, or the item itself.
14694  * @param {Object} o  (optional) If the second parameter was a key, the new item.
14695  * @return {Object} The item inserted.
14696  */
14697     insert : function(index, key, o){
14698         if(arguments.length == 2){
14699             o = arguments[1];
14700             key = this.getKey(o);
14701         }
14702         if(index >= this.length){
14703             return this.add(key, o);
14704         }
14705         this.length++;
14706         this.items.splice(index, 0, o);
14707         if(typeof key != "undefined" && key != null){
14708             this.map[key] = o;
14709         }
14710         this.keys.splice(index, 0, key);
14711         this.fireEvent("add", index, o, key);
14712         return o;
14713     },
14714    
14715 /**
14716  * Removed an item from the collection.
14717  * @param {Object} o The item to remove.
14718  * @return {Object} The item removed.
14719  */
14720     remove : function(o){
14721         return this.removeAt(this.indexOf(o));
14722     },
14723    
14724 /**
14725  * Remove an item from a specified index in the collection.
14726  * @param {Number} index The index within the collection of the item to remove.
14727  */
14728     removeAt : function(index){
14729         if(index < this.length && index >= 0){
14730             this.length--;
14731             var o = this.items[index];
14732             this.items.splice(index, 1);
14733             var key = this.keys[index];
14734             if(typeof key != "undefined"){
14735                 delete this.map[key];
14736             }
14737             this.keys.splice(index, 1);
14738             this.fireEvent("remove", o, key);
14739         }
14740     },
14741    
14742 /**
14743  * Removed an item associated with the passed key fom the collection.
14744  * @param {String} key The key of the item to remove.
14745  */
14746     removeKey : function(key){
14747         return this.removeAt(this.indexOfKey(key));
14748     },
14749    
14750 /**
14751  * Returns the number of items in the collection.
14752  * @return {Number} the number of items in the collection.
14753  */
14754     getCount : function(){
14755         return this.length; 
14756     },
14757    
14758 /**
14759  * Returns index within the collection of the passed Object.
14760  * @param {Object} o The item to find the index of.
14761  * @return {Number} index of the item.
14762  */
14763     indexOf : function(o){
14764         if(!this.items.indexOf){
14765             for(var i = 0, len = this.items.length; i < len; i++){
14766                 if(this.items[i] == o) {
14767                     return i;
14768                 }
14769             }
14770             return -1;
14771         }else{
14772             return this.items.indexOf(o);
14773         }
14774     },
14775    
14776 /**
14777  * Returns index within the collection of the passed key.
14778  * @param {String} key The key to find the index of.
14779  * @return {Number} index of the key.
14780  */
14781     indexOfKey : function(key){
14782         if(!this.keys.indexOf){
14783             for(var i = 0, len = this.keys.length; i < len; i++){
14784                 if(this.keys[i] == key) {
14785                     return i;
14786                 }
14787             }
14788             return -1;
14789         }else{
14790             return this.keys.indexOf(key);
14791         }
14792     },
14793    
14794 /**
14795  * Returns the item associated with the passed key OR index. Key has priority over index.
14796  * @param {String/Number} key The key or index of the item.
14797  * @return {Object} The item associated with the passed key.
14798  */
14799     item : function(key){
14800         if (key === 'length') {
14801             return null;
14802         }
14803         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
14804         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
14805     },
14806     
14807 /**
14808  * Returns the item at the specified index.
14809  * @param {Number} index The index of the item.
14810  * @return {Object}
14811  */
14812     itemAt : function(index){
14813         return this.items[index];
14814     },
14815     
14816 /**
14817  * Returns the item associated with the passed key.
14818  * @param {String/Number} key The key of the item.
14819  * @return {Object} The item associated with the passed key.
14820  */
14821     key : function(key){
14822         return this.map[key];
14823     },
14824    
14825 /**
14826  * Returns true if the collection contains the passed Object as an item.
14827  * @param {Object} o  The Object to look for in the collection.
14828  * @return {Boolean} True if the collection contains the Object as an item.
14829  */
14830     contains : function(o){
14831         return this.indexOf(o) != -1;
14832     },
14833    
14834 /**
14835  * Returns true if the collection contains the passed Object as a key.
14836  * @param {String} key The key to look for in the collection.
14837  * @return {Boolean} True if the collection contains the Object as a key.
14838  */
14839     containsKey : function(key){
14840         return typeof this.map[key] != "undefined";
14841     },
14842    
14843 /**
14844  * Removes all items from the collection.
14845  */
14846     clear : function(){
14847         this.length = 0;
14848         this.items = [];
14849         this.keys = [];
14850         this.map = {};
14851         this.fireEvent("clear");
14852     },
14853    
14854 /**
14855  * Returns the first item in the collection.
14856  * @return {Object} the first item in the collection..
14857  */
14858     first : function(){
14859         return this.items[0]; 
14860     },
14861    
14862 /**
14863  * Returns the last item in the collection.
14864  * @return {Object} the last item in the collection..
14865  */
14866     last : function(){
14867         return this.items[this.length-1];   
14868     },
14869     
14870     _sort : function(property, dir, fn){
14871         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
14872         fn = fn || function(a, b){
14873             return a-b;
14874         };
14875         var c = [], k = this.keys, items = this.items;
14876         for(var i = 0, len = items.length; i < len; i++){
14877             c[c.length] = {key: k[i], value: items[i], index: i};
14878         }
14879         c.sort(function(a, b){
14880             var v = fn(a[property], b[property]) * dsc;
14881             if(v == 0){
14882                 v = (a.index < b.index ? -1 : 1);
14883             }
14884             return v;
14885         });
14886         for(var i = 0, len = c.length; i < len; i++){
14887             items[i] = c[i].value;
14888             k[i] = c[i].key;
14889         }
14890         this.fireEvent("sort", this);
14891     },
14892     
14893     /**
14894      * Sorts this collection with the passed comparison function
14895      * @param {String} direction (optional) "ASC" or "DESC"
14896      * @param {Function} fn (optional) comparison function
14897      */
14898     sort : function(dir, fn){
14899         this._sort("value", dir, fn);
14900     },
14901     
14902     /**
14903      * Sorts this collection by keys
14904      * @param {String} direction (optional) "ASC" or "DESC"
14905      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
14906      */
14907     keySort : function(dir, fn){
14908         this._sort("key", dir, fn || function(a, b){
14909             return String(a).toUpperCase()-String(b).toUpperCase();
14910         });
14911     },
14912     
14913     /**
14914      * Returns a range of items in this collection
14915      * @param {Number} startIndex (optional) defaults to 0
14916      * @param {Number} endIndex (optional) default to the last item
14917      * @return {Array} An array of items
14918      */
14919     getRange : function(start, end){
14920         var items = this.items;
14921         if(items.length < 1){
14922             return [];
14923         }
14924         start = start || 0;
14925         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
14926         var r = [];
14927         if(start <= end){
14928             for(var i = start; i <= end; i++) {
14929                     r[r.length] = items[i];
14930             }
14931         }else{
14932             for(var i = start; i >= end; i--) {
14933                     r[r.length] = items[i];
14934             }
14935         }
14936         return r;
14937     },
14938         
14939     /**
14940      * Filter the <i>objects</i> in this collection by a specific property. 
14941      * Returns a new collection that has been filtered.
14942      * @param {String} property A property on your objects
14943      * @param {String/RegExp} value Either string that the property values 
14944      * should start with or a RegExp to test against the property
14945      * @return {MixedCollection} The new filtered collection
14946      */
14947     filter : function(property, value){
14948         if(!value.exec){ // not a regex
14949             value = String(value);
14950             if(value.length == 0){
14951                 return this.clone();
14952             }
14953             value = new RegExp("^" + Roo.escapeRe(value), "i");
14954         }
14955         return this.filterBy(function(o){
14956             return o && value.test(o[property]);
14957         });
14958         },
14959     
14960     /**
14961      * Filter by a function. * Returns a new collection that has been filtered.
14962      * The passed function will be called with each 
14963      * object in the collection. If the function returns true, the value is included 
14964      * otherwise it is filtered.
14965      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
14966      * @param {Object} scope (optional) The scope of the function (defaults to this) 
14967      * @return {MixedCollection} The new filtered collection
14968      */
14969     filterBy : function(fn, scope){
14970         var r = new Roo.util.MixedCollection();
14971         r.getKey = this.getKey;
14972         var k = this.keys, it = this.items;
14973         for(var i = 0, len = it.length; i < len; i++){
14974             if(fn.call(scope||this, it[i], k[i])){
14975                                 r.add(k[i], it[i]);
14976                         }
14977         }
14978         return r;
14979     },
14980     
14981     /**
14982      * Creates a duplicate of this collection
14983      * @return {MixedCollection}
14984      */
14985     clone : function(){
14986         var r = new Roo.util.MixedCollection();
14987         var k = this.keys, it = this.items;
14988         for(var i = 0, len = it.length; i < len; i++){
14989             r.add(k[i], it[i]);
14990         }
14991         r.getKey = this.getKey;
14992         return r;
14993     }
14994 });
14995 /**
14996  * Returns the item associated with the passed key or index.
14997  * @method
14998  * @param {String/Number} key The key or index of the item.
14999  * @return {Object} The item associated with the passed key.
15000  */
15001 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
15002  * Based on:
15003  * Ext JS Library 1.1.1
15004  * Copyright(c) 2006-2007, Ext JS, LLC.
15005  *
15006  * Originally Released Under LGPL - original licence link has changed is not relivant.
15007  *
15008  * Fork - LGPL
15009  * <script type="text/javascript">
15010  */
15011 /**
15012  * @class Roo.util.JSON
15013  * Modified version of Douglas Crockford"s json.js that doesn"t
15014  * mess with the Object prototype 
15015  * http://www.json.org/js.html
15016  * @static
15017  */
15018 Roo.util.JSON = new (function(){
15019     var useHasOwn = {}.hasOwnProperty ? true : false;
15020     
15021     // crashes Safari in some instances
15022     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
15023     
15024     var pad = function(n) {
15025         return n < 10 ? "0" + n : n;
15026     };
15027     
15028     var m = {
15029         "\b": '\\b',
15030         "\t": '\\t',
15031         "\n": '\\n',
15032         "\f": '\\f',
15033         "\r": '\\r',
15034         '"' : '\\"',
15035         "\\": '\\\\'
15036     };
15037
15038     var encodeString = function(s){
15039         if (/["\\\x00-\x1f]/.test(s)) {
15040             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
15041                 var c = m[b];
15042                 if(c){
15043                     return c;
15044                 }
15045                 c = b.charCodeAt();
15046                 return "\\u00" +
15047                     Math.floor(c / 16).toString(16) +
15048                     (c % 16).toString(16);
15049             }) + '"';
15050         }
15051         return '"' + s + '"';
15052     };
15053     
15054     var encodeArray = function(o){
15055         var a = ["["], b, i, l = o.length, v;
15056             for (i = 0; i < l; i += 1) {
15057                 v = o[i];
15058                 switch (typeof v) {
15059                     case "undefined":
15060                     case "function":
15061                     case "unknown":
15062                         break;
15063                     default:
15064                         if (b) {
15065                             a.push(',');
15066                         }
15067                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
15068                         b = true;
15069                 }
15070             }
15071             a.push("]");
15072             return a.join("");
15073     };
15074     
15075     var encodeDate = function(o){
15076         return '"' + o.getFullYear() + "-" +
15077                 pad(o.getMonth() + 1) + "-" +
15078                 pad(o.getDate()) + "T" +
15079                 pad(o.getHours()) + ":" +
15080                 pad(o.getMinutes()) + ":" +
15081                 pad(o.getSeconds()) + '"';
15082     };
15083     
15084     /**
15085      * Encodes an Object, Array or other value
15086      * @param {Mixed} o The variable to encode
15087      * @return {String} The JSON string
15088      */
15089     this.encode = function(o)
15090     {
15091         // should this be extended to fully wrap stringify..
15092         
15093         if(typeof o == "undefined" || o === null){
15094             return "null";
15095         }else if(o instanceof Array){
15096             return encodeArray(o);
15097         }else if(o instanceof Date){
15098             return encodeDate(o);
15099         }else if(typeof o == "string"){
15100             return encodeString(o);
15101         }else if(typeof o == "number"){
15102             return isFinite(o) ? String(o) : "null";
15103         }else if(typeof o == "boolean"){
15104             return String(o);
15105         }else {
15106             var a = ["{"], b, i, v;
15107             for (i in o) {
15108                 if(!useHasOwn || o.hasOwnProperty(i)) {
15109                     v = o[i];
15110                     switch (typeof v) {
15111                     case "undefined":
15112                     case "function":
15113                     case "unknown":
15114                         break;
15115                     default:
15116                         if(b){
15117                             a.push(',');
15118                         }
15119                         a.push(this.encode(i), ":",
15120                                 v === null ? "null" : this.encode(v));
15121                         b = true;
15122                     }
15123                 }
15124             }
15125             a.push("}");
15126             return a.join("");
15127         }
15128     };
15129     
15130     /**
15131      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
15132      * @param {String} json The JSON string
15133      * @return {Object} The resulting object
15134      */
15135     this.decode = function(json){
15136         
15137         return  /** eval:var:json */ eval("(" + json + ')');
15138     };
15139 })();
15140 /** 
15141  * Shorthand for {@link Roo.util.JSON#encode}
15142  * @member Roo encode 
15143  * @method */
15144 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
15145 /** 
15146  * Shorthand for {@link Roo.util.JSON#decode}
15147  * @member Roo decode 
15148  * @method */
15149 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
15150 /*
15151  * Based on:
15152  * Ext JS Library 1.1.1
15153  * Copyright(c) 2006-2007, Ext JS, LLC.
15154  *
15155  * Originally Released Under LGPL - original licence link has changed is not relivant.
15156  *
15157  * Fork - LGPL
15158  * <script type="text/javascript">
15159  */
15160  
15161 /**
15162  * @class Roo.util.Format
15163  * Reusable data formatting functions
15164  * @static
15165  */
15166 Roo.util.Format = function(){
15167     var trimRe = /^\s+|\s+$/g;
15168     return {
15169         /**
15170          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
15171          * @param {String} value The string to truncate
15172          * @param {Number} length The maximum length to allow before truncating
15173          * @return {String} The converted text
15174          */
15175         ellipsis : function(value, len){
15176             if(value && value.length > len){
15177                 return value.substr(0, len-3)+"...";
15178             }
15179             return value;
15180         },
15181
15182         /**
15183          * Checks a reference and converts it to empty string if it is undefined
15184          * @param {Mixed} value Reference to check
15185          * @return {Mixed} Empty string if converted, otherwise the original value
15186          */
15187         undef : function(value){
15188             return typeof value != "undefined" ? value : "";
15189         },
15190
15191         /**
15192          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
15193          * @param {String} value The string to encode
15194          * @return {String} The encoded text
15195          */
15196         htmlEncode : function(value){
15197             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
15198         },
15199
15200         /**
15201          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
15202          * @param {String} value The string to decode
15203          * @return {String} The decoded text
15204          */
15205         htmlDecode : function(value){
15206             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
15207         },
15208
15209         /**
15210          * Trims any whitespace from either side of a string
15211          * @param {String} value The text to trim
15212          * @return {String} The trimmed text
15213          */
15214         trim : function(value){
15215             return String(value).replace(trimRe, "");
15216         },
15217
15218         /**
15219          * Returns a substring from within an original string
15220          * @param {String} value The original text
15221          * @param {Number} start The start index of the substring
15222          * @param {Number} length The length of the substring
15223          * @return {String} The substring
15224          */
15225         substr : function(value, start, length){
15226             return String(value).substr(start, length);
15227         },
15228
15229         /**
15230          * Converts a string to all lower case letters
15231          * @param {String} value The text to convert
15232          * @return {String} The converted text
15233          */
15234         lowercase : function(value){
15235             return String(value).toLowerCase();
15236         },
15237
15238         /**
15239          * Converts a string to all upper case letters
15240          * @param {String} value The text to convert
15241          * @return {String} The converted text
15242          */
15243         uppercase : function(value){
15244             return String(value).toUpperCase();
15245         },
15246
15247         /**
15248          * Converts the first character only of a string to upper case
15249          * @param {String} value The text to convert
15250          * @return {String} The converted text
15251          */
15252         capitalize : function(value){
15253             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
15254         },
15255
15256         // private
15257         call : function(value, fn){
15258             if(arguments.length > 2){
15259                 var args = Array.prototype.slice.call(arguments, 2);
15260                 args.unshift(value);
15261                  
15262                 return /** eval:var:value */  eval(fn).apply(window, args);
15263             }else{
15264                 /** eval:var:value */
15265                 return /** eval:var:value */ eval(fn).call(window, value);
15266             }
15267         },
15268
15269        
15270         /**
15271          * safer version of Math.toFixed..??/
15272          * @param {Number/String} value The numeric value to format
15273          * @param {Number/String} value Decimal places 
15274          * @return {String} The formatted currency string
15275          */
15276         toFixed : function(v, n)
15277         {
15278             // why not use to fixed - precision is buggered???
15279             if (!n) {
15280                 return Math.round(v-0);
15281             }
15282             var fact = Math.pow(10,n+1);
15283             v = (Math.round((v-0)*fact))/fact;
15284             var z = (''+fact).substring(2);
15285             if (v == Math.floor(v)) {
15286                 return Math.floor(v) + '.' + z;
15287             }
15288             
15289             // now just padd decimals..
15290             var ps = String(v).split('.');
15291             var fd = (ps[1] + z);
15292             var r = fd.substring(0,n); 
15293             var rm = fd.substring(n); 
15294             if (rm < 5) {
15295                 return ps[0] + '.' + r;
15296             }
15297             r*=1; // turn it into a number;
15298             r++;
15299             if (String(r).length != n) {
15300                 ps[0]*=1;
15301                 ps[0]++;
15302                 r = String(r).substring(1); // chop the end off.
15303             }
15304             
15305             return ps[0] + '.' + r;
15306              
15307         },
15308         
15309         /**
15310          * Format a number as US currency
15311          * @param {Number/String} value The numeric value to format
15312          * @return {String} The formatted currency string
15313          */
15314         usMoney : function(v){
15315             return '$' + Roo.util.Format.number(v);
15316         },
15317         
15318         /**
15319          * Format a number
15320          * eventually this should probably emulate php's number_format
15321          * @param {Number/String} value The numeric value to format
15322          * @param {Number} decimals number of decimal places
15323          * @param {String} delimiter for thousands (default comma)
15324          * @return {String} The formatted currency string
15325          */
15326         number : function(v, decimals, thousandsDelimiter)
15327         {
15328             // multiply and round.
15329             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
15330             thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
15331             
15332             var mul = Math.pow(10, decimals);
15333             var zero = String(mul).substring(1);
15334             v = (Math.round((v-0)*mul))/mul;
15335             
15336             // if it's '0' number.. then
15337             
15338             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
15339             v = String(v);
15340             var ps = v.split('.');
15341             var whole = ps[0];
15342             
15343             var r = /(\d+)(\d{3})/;
15344             // add comma's
15345             
15346             if(thousandsDelimiter.length != 0) {
15347                 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
15348             } 
15349             
15350             var sub = ps[1] ?
15351                     // has decimals..
15352                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
15353                     // does not have decimals
15354                     (decimals ? ('.' + zero) : '');
15355             
15356             
15357             return whole + sub ;
15358         },
15359         
15360         /**
15361          * Parse a value into a formatted date using the specified format pattern.
15362          * @param {Mixed} value The value to format
15363          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
15364          * @return {String} The formatted date string
15365          */
15366         date : function(v, format){
15367             if(!v){
15368                 return "";
15369             }
15370             if(!(v instanceof Date)){
15371                 v = new Date(Date.parse(v));
15372             }
15373             return v.dateFormat(format || Roo.util.Format.defaults.date);
15374         },
15375
15376         /**
15377          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
15378          * @param {String} format Any valid date format string
15379          * @return {Function} The date formatting function
15380          */
15381         dateRenderer : function(format){
15382             return function(v){
15383                 return Roo.util.Format.date(v, format);  
15384             };
15385         },
15386
15387         // private
15388         stripTagsRE : /<\/?[^>]+>/gi,
15389         
15390         /**
15391          * Strips all HTML tags
15392          * @param {Mixed} value The text from which to strip tags
15393          * @return {String} The stripped text
15394          */
15395         stripTags : function(v){
15396             return !v ? v : String(v).replace(this.stripTagsRE, "");
15397         },
15398         
15399         /**
15400          * Size in Mb,Gb etc.
15401          * @param {Number} value The number to be formated
15402          * @param {number} decimals how many decimal places
15403          * @return {String} the formated string
15404          */
15405         size : function(value, decimals)
15406         {
15407             var sizes = ['b', 'k', 'M', 'G', 'T'];
15408             if (value == 0) {
15409                 return 0;
15410             }
15411             var i = parseInt(Math.floor(Math.log(value) / Math.log(1024)));
15412             return Roo.util.Format.number(value/ Math.pow(1024, i) ,decimals)   + sizes[i];
15413         }
15414         
15415         
15416         
15417     };
15418 }();
15419 Roo.util.Format.defaults = {
15420     date : 'd/M/Y'
15421 };/*
15422  * Based on:
15423  * Ext JS Library 1.1.1
15424  * Copyright(c) 2006-2007, Ext JS, LLC.
15425  *
15426  * Originally Released Under LGPL - original licence link has changed is not relivant.
15427  *
15428  * Fork - LGPL
15429  * <script type="text/javascript">
15430  */
15431
15432
15433  
15434
15435 /**
15436  * @class Roo.MasterTemplate
15437  * @extends Roo.Template
15438  * Provides a template that can have child templates. The syntax is:
15439 <pre><code>
15440 var t = new Roo.MasterTemplate(
15441         '&lt;select name="{name}"&gt;',
15442                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
15443         '&lt;/select&gt;'
15444 );
15445 t.add('options', {value: 'foo', text: 'bar'});
15446 // or you can add multiple child elements in one shot
15447 t.addAll('options', [
15448     {value: 'foo', text: 'bar'},
15449     {value: 'foo2', text: 'bar2'},
15450     {value: 'foo3', text: 'bar3'}
15451 ]);
15452 // then append, applying the master template values
15453 t.append('my-form', {name: 'my-select'});
15454 </code></pre>
15455 * A name attribute for the child template is not required if you have only one child
15456 * template or you want to refer to them by index.
15457  */
15458 Roo.MasterTemplate = function(){
15459     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
15460     this.originalHtml = this.html;
15461     var st = {};
15462     var m, re = this.subTemplateRe;
15463     re.lastIndex = 0;
15464     var subIndex = 0;
15465     while(m = re.exec(this.html)){
15466         var name = m[1], content = m[2];
15467         st[subIndex] = {
15468             name: name,
15469             index: subIndex,
15470             buffer: [],
15471             tpl : new Roo.Template(content)
15472         };
15473         if(name){
15474             st[name] = st[subIndex];
15475         }
15476         st[subIndex].tpl.compile();
15477         st[subIndex].tpl.call = this.call.createDelegate(this);
15478         subIndex++;
15479     }
15480     this.subCount = subIndex;
15481     this.subs = st;
15482 };
15483 Roo.extend(Roo.MasterTemplate, Roo.Template, {
15484     /**
15485     * The regular expression used to match sub templates
15486     * @type RegExp
15487     * @property
15488     */
15489     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
15490
15491     /**
15492      * Applies the passed values to a child template.
15493      * @param {String/Number} name (optional) The name or index of the child template
15494      * @param {Array/Object} values The values to be applied to the template
15495      * @return {MasterTemplate} this
15496      */
15497      add : function(name, values){
15498         if(arguments.length == 1){
15499             values = arguments[0];
15500             name = 0;
15501         }
15502         var s = this.subs[name];
15503         s.buffer[s.buffer.length] = s.tpl.apply(values);
15504         return this;
15505     },
15506
15507     /**
15508      * Applies all the passed values to a child template.
15509      * @param {String/Number} name (optional) The name or index of the child template
15510      * @param {Array} values The values to be applied to the template, this should be an array of objects.
15511      * @param {Boolean} reset (optional) True to reset the template first
15512      * @return {MasterTemplate} this
15513      */
15514     fill : function(name, values, reset){
15515         var a = arguments;
15516         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
15517             values = a[0];
15518             name = 0;
15519             reset = a[1];
15520         }
15521         if(reset){
15522             this.reset();
15523         }
15524         for(var i = 0, len = values.length; i < len; i++){
15525             this.add(name, values[i]);
15526         }
15527         return this;
15528     },
15529
15530     /**
15531      * Resets the template for reuse
15532      * @return {MasterTemplate} this
15533      */
15534      reset : function(){
15535         var s = this.subs;
15536         for(var i = 0; i < this.subCount; i++){
15537             s[i].buffer = [];
15538         }
15539         return this;
15540     },
15541
15542     applyTemplate : function(values){
15543         var s = this.subs;
15544         var replaceIndex = -1;
15545         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
15546             return s[++replaceIndex].buffer.join("");
15547         });
15548         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
15549     },
15550
15551     apply : function(){
15552         return this.applyTemplate.apply(this, arguments);
15553     },
15554
15555     compile : function(){return this;}
15556 });
15557
15558 /**
15559  * Alias for fill().
15560  * @method
15561  */
15562 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
15563  /**
15564  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
15565  * var tpl = Roo.MasterTemplate.from('element-id');
15566  * @param {String/HTMLElement} el
15567  * @param {Object} config
15568  * @static
15569  */
15570 Roo.MasterTemplate.from = function(el, config){
15571     el = Roo.getDom(el);
15572     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
15573 };/*
15574  * Based on:
15575  * Ext JS Library 1.1.1
15576  * Copyright(c) 2006-2007, Ext JS, LLC.
15577  *
15578  * Originally Released Under LGPL - original licence link has changed is not relivant.
15579  *
15580  * Fork - LGPL
15581  * <script type="text/javascript">
15582  */
15583
15584  
15585 /**
15586  * @class Roo.util.CSS
15587  * Utility class for manipulating CSS rules
15588  * @static
15589
15590  */
15591 Roo.util.CSS = function(){
15592         var rules = null;
15593         var doc = document;
15594
15595     var camelRe = /(-[a-z])/gi;
15596     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
15597
15598    return {
15599    /**
15600     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
15601     * tag and appended to the HEAD of the document.
15602     * @param {String|Object} cssText The text containing the css rules
15603     * @param {String} id An id to add to the stylesheet for later removal
15604     * @return {StyleSheet}
15605     */
15606     createStyleSheet : function(cssText, id){
15607         var ss;
15608         var head = doc.getElementsByTagName("head")[0];
15609         var nrules = doc.createElement("style");
15610         nrules.setAttribute("type", "text/css");
15611         if(id){
15612             nrules.setAttribute("id", id);
15613         }
15614         if (typeof(cssText) != 'string') {
15615             // support object maps..
15616             // not sure if this a good idea.. 
15617             // perhaps it should be merged with the general css handling
15618             // and handle js style props.
15619             var cssTextNew = [];
15620             for(var n in cssText) {
15621                 var citems = [];
15622                 for(var k in cssText[n]) {
15623                     citems.push( k + ' : ' +cssText[n][k] + ';' );
15624                 }
15625                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
15626                 
15627             }
15628             cssText = cssTextNew.join("\n");
15629             
15630         }
15631        
15632        
15633        if(Roo.isIE){
15634            head.appendChild(nrules);
15635            ss = nrules.styleSheet;
15636            ss.cssText = cssText;
15637        }else{
15638            try{
15639                 nrules.appendChild(doc.createTextNode(cssText));
15640            }catch(e){
15641                nrules.cssText = cssText; 
15642            }
15643            head.appendChild(nrules);
15644            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
15645        }
15646        this.cacheStyleSheet(ss);
15647        return ss;
15648    },
15649
15650    /**
15651     * Removes a style or link tag by id
15652     * @param {String} id The id of the tag
15653     */
15654    removeStyleSheet : function(id){
15655        var existing = doc.getElementById(id);
15656        if(existing){
15657            existing.parentNode.removeChild(existing);
15658        }
15659    },
15660
15661    /**
15662     * Dynamically swaps an existing stylesheet reference for a new one
15663     * @param {String} id The id of an existing link tag to remove
15664     * @param {String} url The href of the new stylesheet to include
15665     */
15666    swapStyleSheet : function(id, url){
15667        this.removeStyleSheet(id);
15668        var ss = doc.createElement("link");
15669        ss.setAttribute("rel", "stylesheet");
15670        ss.setAttribute("type", "text/css");
15671        ss.setAttribute("id", id);
15672        ss.setAttribute("href", url);
15673        doc.getElementsByTagName("head")[0].appendChild(ss);
15674    },
15675    
15676    /**
15677     * Refresh the rule cache if you have dynamically added stylesheets
15678     * @return {Object} An object (hash) of rules indexed by selector
15679     */
15680    refreshCache : function(){
15681        return this.getRules(true);
15682    },
15683
15684    // private
15685    cacheStyleSheet : function(stylesheet){
15686        if(!rules){
15687            rules = {};
15688        }
15689        try{// try catch for cross domain access issue
15690            var ssRules = stylesheet.cssRules || stylesheet.rules;
15691            for(var j = ssRules.length-1; j >= 0; --j){
15692                rules[ssRules[j].selectorText] = ssRules[j];
15693            }
15694        }catch(e){}
15695    },
15696    
15697    /**
15698     * Gets all css rules for the document
15699     * @param {Boolean} refreshCache true to refresh the internal cache
15700     * @return {Object} An object (hash) of rules indexed by selector
15701     */
15702    getRules : function(refreshCache){
15703                 if(rules == null || refreshCache){
15704                         rules = {};
15705                         var ds = doc.styleSheets;
15706                         for(var i =0, len = ds.length; i < len; i++){
15707                             try{
15708                         this.cacheStyleSheet(ds[i]);
15709                     }catch(e){} 
15710                 }
15711                 }
15712                 return rules;
15713         },
15714         
15715         /**
15716     * Gets an an individual CSS rule by selector(s)
15717     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
15718     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
15719     * @return {CSSRule} The CSS rule or null if one is not found
15720     */
15721    getRule : function(selector, refreshCache){
15722                 var rs = this.getRules(refreshCache);
15723                 if(!(selector instanceof Array)){
15724                     return rs[selector];
15725                 }
15726                 for(var i = 0; i < selector.length; i++){
15727                         if(rs[selector[i]]){
15728                                 return rs[selector[i]];
15729                         }
15730                 }
15731                 return null;
15732         },
15733         
15734         
15735         /**
15736     * Updates a rule property
15737     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
15738     * @param {String} property The css property
15739     * @param {String} value The new value for the property
15740     * @return {Boolean} true If a rule was found and updated
15741     */
15742    updateRule : function(selector, property, value){
15743                 if(!(selector instanceof Array)){
15744                         var rule = this.getRule(selector);
15745                         if(rule){
15746                                 rule.style[property.replace(camelRe, camelFn)] = value;
15747                                 return true;
15748                         }
15749                 }else{
15750                         for(var i = 0; i < selector.length; i++){
15751                                 if(this.updateRule(selector[i], property, value)){
15752                                         return true;
15753                                 }
15754                         }
15755                 }
15756                 return false;
15757         }
15758    };   
15759 }();/*
15760  * Based on:
15761  * Ext JS Library 1.1.1
15762  * Copyright(c) 2006-2007, Ext JS, LLC.
15763  *
15764  * Originally Released Under LGPL - original licence link has changed is not relivant.
15765  *
15766  * Fork - LGPL
15767  * <script type="text/javascript">
15768  */
15769
15770  
15771
15772 /**
15773  * @class Roo.util.ClickRepeater
15774  * @extends Roo.util.Observable
15775  * 
15776  * A wrapper class which can be applied to any element. Fires a "click" event while the
15777  * mouse is pressed. The interval between firings may be specified in the config but
15778  * defaults to 10 milliseconds.
15779  * 
15780  * Optionally, a CSS class may be applied to the element during the time it is pressed.
15781  * 
15782  * @cfg {String/HTMLElement/Element} el The element to act as a button.
15783  * @cfg {Number} delay The initial delay before the repeating event begins firing.
15784  * Similar to an autorepeat key delay.
15785  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
15786  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
15787  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
15788  *           "interval" and "delay" are ignored. "immediate" is honored.
15789  * @cfg {Boolean} preventDefault True to prevent the default click event
15790  * @cfg {Boolean} stopDefault True to stop the default click event
15791  * 
15792  * @history
15793  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
15794  *     2007-02-02 jvs Renamed to ClickRepeater
15795  *   2007-02-03 jvs Modifications for FF Mac and Safari 
15796  *
15797  *  @constructor
15798  * @param {String/HTMLElement/Element} el The element to listen on
15799  * @param {Object} config
15800  **/
15801 Roo.util.ClickRepeater = function(el, config)
15802 {
15803     this.el = Roo.get(el);
15804     this.el.unselectable();
15805
15806     Roo.apply(this, config);
15807
15808     this.addEvents({
15809     /**
15810      * @event mousedown
15811      * Fires when the mouse button is depressed.
15812      * @param {Roo.util.ClickRepeater} this
15813      */
15814         "mousedown" : true,
15815     /**
15816      * @event click
15817      * Fires on a specified interval during the time the element is pressed.
15818      * @param {Roo.util.ClickRepeater} this
15819      */
15820         "click" : true,
15821     /**
15822      * @event mouseup
15823      * Fires when the mouse key is released.
15824      * @param {Roo.util.ClickRepeater} this
15825      */
15826         "mouseup" : true
15827     });
15828
15829     this.el.on("mousedown", this.handleMouseDown, this);
15830     if(this.preventDefault || this.stopDefault){
15831         this.el.on("click", function(e){
15832             if(this.preventDefault){
15833                 e.preventDefault();
15834             }
15835             if(this.stopDefault){
15836                 e.stopEvent();
15837             }
15838         }, this);
15839     }
15840
15841     // allow inline handler
15842     if(this.handler){
15843         this.on("click", this.handler,  this.scope || this);
15844     }
15845
15846     Roo.util.ClickRepeater.superclass.constructor.call(this);
15847 };
15848
15849 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
15850     interval : 20,
15851     delay: 250,
15852     preventDefault : true,
15853     stopDefault : false,
15854     timer : 0,
15855
15856     // private
15857     handleMouseDown : function(){
15858         clearTimeout(this.timer);
15859         this.el.blur();
15860         if(this.pressClass){
15861             this.el.addClass(this.pressClass);
15862         }
15863         this.mousedownTime = new Date();
15864
15865         Roo.get(document).on("mouseup", this.handleMouseUp, this);
15866         this.el.on("mouseout", this.handleMouseOut, this);
15867
15868         this.fireEvent("mousedown", this);
15869         this.fireEvent("click", this);
15870         
15871         this.timer = this.click.defer(this.delay || this.interval, this);
15872     },
15873
15874     // private
15875     click : function(){
15876         this.fireEvent("click", this);
15877         this.timer = this.click.defer(this.getInterval(), this);
15878     },
15879
15880     // private
15881     getInterval: function(){
15882         if(!this.accelerate){
15883             return this.interval;
15884         }
15885         var pressTime = this.mousedownTime.getElapsed();
15886         if(pressTime < 500){
15887             return 400;
15888         }else if(pressTime < 1700){
15889             return 320;
15890         }else if(pressTime < 2600){
15891             return 250;
15892         }else if(pressTime < 3500){
15893             return 180;
15894         }else if(pressTime < 4400){
15895             return 140;
15896         }else if(pressTime < 5300){
15897             return 80;
15898         }else if(pressTime < 6200){
15899             return 50;
15900         }else{
15901             return 10;
15902         }
15903     },
15904
15905     // private
15906     handleMouseOut : function(){
15907         clearTimeout(this.timer);
15908         if(this.pressClass){
15909             this.el.removeClass(this.pressClass);
15910         }
15911         this.el.on("mouseover", this.handleMouseReturn, this);
15912     },
15913
15914     // private
15915     handleMouseReturn : function(){
15916         this.el.un("mouseover", this.handleMouseReturn);
15917         if(this.pressClass){
15918             this.el.addClass(this.pressClass);
15919         }
15920         this.click();
15921     },
15922
15923     // private
15924     handleMouseUp : function(){
15925         clearTimeout(this.timer);
15926         this.el.un("mouseover", this.handleMouseReturn);
15927         this.el.un("mouseout", this.handleMouseOut);
15928         Roo.get(document).un("mouseup", this.handleMouseUp);
15929         this.el.removeClass(this.pressClass);
15930         this.fireEvent("mouseup", this);
15931     }
15932 });/**
15933  * @class Roo.util.Clipboard
15934  * @static
15935  * 
15936  * Clipboard UTILS
15937  * 
15938  **/
15939 Roo.util.Clipboard = {
15940     /**
15941      * Writes a string to the clipboard - using the Clipboard API if https, otherwise using text area.
15942      * @param {String} text to copy to clipboard
15943      */
15944     write : function(text) {
15945         // navigator clipboard api needs a secure context (https)
15946         if (navigator.clipboard && window.isSecureContext) {
15947             // navigator clipboard api method'
15948             navigator.clipboard.writeText(text);
15949             return ;
15950         } 
15951         // text area method
15952         var ta = document.createElement("textarea");
15953         ta.value = text;
15954         // make the textarea out of viewport
15955         ta.style.position = "fixed";
15956         ta.style.left = "-999999px";
15957         ta.style.top = "-999999px";
15958         document.body.appendChild(ta);
15959         ta.focus();
15960         ta.select();
15961         document.execCommand('copy');
15962         (function() {
15963             ta.remove();
15964         }).defer(100);
15965         
15966     }
15967         
15968 }
15969     /*
15970  * Based on:
15971  * Ext JS Library 1.1.1
15972  * Copyright(c) 2006-2007, Ext JS, LLC.
15973  *
15974  * Originally Released Under LGPL - original licence link has changed is not relivant.
15975  *
15976  * Fork - LGPL
15977  * <script type="text/javascript">
15978  */
15979
15980  
15981 /**
15982  * @class Roo.KeyNav
15983  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
15984  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
15985  * way to implement custom navigation schemes for any UI component.</p>
15986  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
15987  * pageUp, pageDown, del, home, end.  Usage:</p>
15988  <pre><code>
15989 var nav = new Roo.KeyNav("my-element", {
15990     "left" : function(e){
15991         this.moveLeft(e.ctrlKey);
15992     },
15993     "right" : function(e){
15994         this.moveRight(e.ctrlKey);
15995     },
15996     "enter" : function(e){
15997         this.save();
15998     },
15999     scope : this
16000 });
16001 </code></pre>
16002  * @constructor
16003  * @param {String/HTMLElement/Roo.Element} el The element to bind to
16004  * @param {Object} config The config
16005  */
16006 Roo.KeyNav = function(el, config){
16007     this.el = Roo.get(el);
16008     Roo.apply(this, config);
16009     if(!this.disabled){
16010         this.disabled = true;
16011         this.enable();
16012     }
16013 };
16014
16015 Roo.KeyNav.prototype = {
16016     /**
16017      * @cfg {Boolean} disabled
16018      * True to disable this KeyNav instance (defaults to false)
16019      */
16020     disabled : false,
16021     /**
16022      * @cfg {String} defaultEventAction
16023      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
16024      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
16025      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
16026      */
16027     defaultEventAction: "stopEvent",
16028     /**
16029      * @cfg {Boolean} forceKeyDown
16030      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
16031      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
16032      * handle keydown instead of keypress.
16033      */
16034     forceKeyDown : false,
16035
16036     // private
16037     prepareEvent : function(e){
16038         var k = e.getKey();
16039         var h = this.keyToHandler[k];
16040         //if(h && this[h]){
16041         //    e.stopPropagation();
16042         //}
16043         if(Roo.isSafari && h && k >= 37 && k <= 40){
16044             e.stopEvent();
16045         }
16046     },
16047
16048     // private
16049     relay : function(e){
16050         var k = e.getKey();
16051         var h = this.keyToHandler[k];
16052         if(h && this[h]){
16053             if(this.doRelay(e, this[h], h) !== true){
16054                 e[this.defaultEventAction]();
16055             }
16056         }
16057     },
16058
16059     // private
16060     doRelay : function(e, h, hname){
16061         return h.call(this.scope || this, e);
16062     },
16063
16064     // possible handlers
16065     enter : false,
16066     left : false,
16067     right : false,
16068     up : false,
16069     down : false,
16070     tab : false,
16071     esc : false,
16072     pageUp : false,
16073     pageDown : false,
16074     del : false,
16075     home : false,
16076     end : false,
16077
16078     // quick lookup hash
16079     keyToHandler : {
16080         37 : "left",
16081         39 : "right",
16082         38 : "up",
16083         40 : "down",
16084         33 : "pageUp",
16085         34 : "pageDown",
16086         46 : "del",
16087         36 : "home",
16088         35 : "end",
16089         13 : "enter",
16090         27 : "esc",
16091         9  : "tab"
16092     },
16093
16094         /**
16095          * Enable this KeyNav
16096          */
16097         enable: function(){
16098                 if(this.disabled){
16099             // ie won't do special keys on keypress, no one else will repeat keys with keydown
16100             // the EventObject will normalize Safari automatically
16101             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
16102                 this.el.on("keydown", this.relay,  this);
16103             }else{
16104                 this.el.on("keydown", this.prepareEvent,  this);
16105                 this.el.on("keypress", this.relay,  this);
16106             }
16107                     this.disabled = false;
16108                 }
16109         },
16110
16111         /**
16112          * Disable this KeyNav
16113          */
16114         disable: function(){
16115                 if(!this.disabled){
16116                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
16117                 this.el.un("keydown", this.relay);
16118             }else{
16119                 this.el.un("keydown", this.prepareEvent);
16120                 this.el.un("keypress", this.relay);
16121             }
16122                     this.disabled = true;
16123                 }
16124         }
16125 };/*
16126  * Based on:
16127  * Ext JS Library 1.1.1
16128  * Copyright(c) 2006-2007, Ext JS, LLC.
16129  *
16130  * Originally Released Under LGPL - original licence link has changed is not relivant.
16131  *
16132  * Fork - LGPL
16133  * <script type="text/javascript">
16134  */
16135
16136  
16137 /**
16138  * @class Roo.KeyMap
16139  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
16140  * The constructor accepts the same config object as defined by {@link #addBinding}.
16141  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
16142  * combination it will call the function with this signature (if the match is a multi-key
16143  * combination the callback will still be called only once): (String key, Roo.EventObject e)
16144  * A KeyMap can also handle a string representation of keys.<br />
16145  * Usage:
16146  <pre><code>
16147 // map one key by key code
16148 var map = new Roo.KeyMap("my-element", {
16149     key: 13, // or Roo.EventObject.ENTER
16150     fn: myHandler,
16151     scope: myObject
16152 });
16153
16154 // map multiple keys to one action by string
16155 var map = new Roo.KeyMap("my-element", {
16156     key: "a\r\n\t",
16157     fn: myHandler,
16158     scope: myObject
16159 });
16160
16161 // map multiple keys to multiple actions by strings and array of codes
16162 var map = new Roo.KeyMap("my-element", [
16163     {
16164         key: [10,13],
16165         fn: function(){ alert("Return was pressed"); }
16166     }, {
16167         key: "abc",
16168         fn: function(){ alert('a, b or c was pressed'); }
16169     }, {
16170         key: "\t",
16171         ctrl:true,
16172         shift:true,
16173         fn: function(){ alert('Control + shift + tab was pressed.'); }
16174     }
16175 ]);
16176 </code></pre>
16177  * <b>Note: A KeyMap starts enabled</b>
16178  * @constructor
16179  * @param {String/HTMLElement/Roo.Element} el The element to bind to
16180  * @param {Object} config The config (see {@link #addBinding})
16181  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
16182  */
16183 Roo.KeyMap = function(el, config, eventName){
16184     this.el  = Roo.get(el);
16185     this.eventName = eventName || "keydown";
16186     this.bindings = [];
16187     if(config){
16188         this.addBinding(config);
16189     }
16190     this.enable();
16191 };
16192
16193 Roo.KeyMap.prototype = {
16194     /**
16195      * True to stop the event from bubbling and prevent the default browser action if the
16196      * key was handled by the KeyMap (defaults to false)
16197      * @type Boolean
16198      */
16199     stopEvent : false,
16200
16201     /**
16202      * Add a new binding to this KeyMap. The following config object properties are supported:
16203      * <pre>
16204 Property    Type             Description
16205 ----------  ---------------  ----------------------------------------------------------------------
16206 key         String/Array     A single keycode or an array of keycodes to handle
16207 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
16208 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
16209 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
16210 fn          Function         The function to call when KeyMap finds the expected key combination
16211 scope       Object           The scope of the callback function
16212 </pre>
16213      *
16214      * Usage:
16215      * <pre><code>
16216 // Create a KeyMap
16217 var map = new Roo.KeyMap(document, {
16218     key: Roo.EventObject.ENTER,
16219     fn: handleKey,
16220     scope: this
16221 });
16222
16223 //Add a new binding to the existing KeyMap later
16224 map.addBinding({
16225     key: 'abc',
16226     shift: true,
16227     fn: handleKey,
16228     scope: this
16229 });
16230 </code></pre>
16231      * @param {Object/Array} config A single KeyMap config or an array of configs
16232      */
16233         addBinding : function(config){
16234         if(config instanceof Array){
16235             for(var i = 0, len = config.length; i < len; i++){
16236                 this.addBinding(config[i]);
16237             }
16238             return;
16239         }
16240         var keyCode = config.key,
16241             shift = config.shift, 
16242             ctrl = config.ctrl, 
16243             alt = config.alt,
16244             fn = config.fn,
16245             scope = config.scope;
16246         if(typeof keyCode == "string"){
16247             var ks = [];
16248             var keyString = keyCode.toUpperCase();
16249             for(var j = 0, len = keyString.length; j < len; j++){
16250                 ks.push(keyString.charCodeAt(j));
16251             }
16252             keyCode = ks;
16253         }
16254         var keyArray = keyCode instanceof Array;
16255         var handler = function(e){
16256             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
16257                 var k = e.getKey();
16258                 if(keyArray){
16259                     for(var i = 0, len = keyCode.length; i < len; i++){
16260                         if(keyCode[i] == k){
16261                           if(this.stopEvent){
16262                               e.stopEvent();
16263                           }
16264                           fn.call(scope || window, k, e);
16265                           return;
16266                         }
16267                     }
16268                 }else{
16269                     if(k == keyCode){
16270                         if(this.stopEvent){
16271                            e.stopEvent();
16272                         }
16273                         fn.call(scope || window, k, e);
16274                     }
16275                 }
16276             }
16277         };
16278         this.bindings.push(handler);  
16279         },
16280
16281     /**
16282      * Shorthand for adding a single key listener
16283      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
16284      * following options:
16285      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
16286      * @param {Function} fn The function to call
16287      * @param {Object} scope (optional) The scope of the function
16288      */
16289     on : function(key, fn, scope){
16290         var keyCode, shift, ctrl, alt;
16291         if(typeof key == "object" && !(key instanceof Array)){
16292             keyCode = key.key;
16293             shift = key.shift;
16294             ctrl = key.ctrl;
16295             alt = key.alt;
16296         }else{
16297             keyCode = key;
16298         }
16299         this.addBinding({
16300             key: keyCode,
16301             shift: shift,
16302             ctrl: ctrl,
16303             alt: alt,
16304             fn: fn,
16305             scope: scope
16306         })
16307     },
16308
16309     // private
16310     handleKeyDown : function(e){
16311             if(this.enabled){ //just in case
16312             var b = this.bindings;
16313             for(var i = 0, len = b.length; i < len; i++){
16314                 b[i].call(this, e);
16315             }
16316             }
16317         },
16318         
16319         /**
16320          * Returns true if this KeyMap is enabled
16321          * @return {Boolean} 
16322          */
16323         isEnabled : function(){
16324             return this.enabled;  
16325         },
16326         
16327         /**
16328          * Enables this KeyMap
16329          */
16330         enable: function(){
16331                 if(!this.enabled){
16332                     this.el.on(this.eventName, this.handleKeyDown, this);
16333                     this.enabled = true;
16334                 }
16335         },
16336
16337         /**
16338          * Disable this KeyMap
16339          */
16340         disable: function(){
16341                 if(this.enabled){
16342                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
16343                     this.enabled = false;
16344                 }
16345         }
16346 };/*
16347  * Based on:
16348  * Ext JS Library 1.1.1
16349  * Copyright(c) 2006-2007, Ext JS, LLC.
16350  *
16351  * Originally Released Under LGPL - original licence link has changed is not relivant.
16352  *
16353  * Fork - LGPL
16354  * <script type="text/javascript">
16355  */
16356
16357  
16358 /**
16359  * @class Roo.util.TextMetrics
16360  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
16361  * wide, in pixels, a given block of text will be.
16362  * @static
16363  */
16364 Roo.util.TextMetrics = function(){
16365     var shared;
16366     return {
16367         /**
16368          * Measures the size of the specified text
16369          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
16370          * that can affect the size of the rendered text
16371          * @param {String} text The text to measure
16372          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
16373          * in order to accurately measure the text height
16374          * @return {Object} An object containing the text's size {width: (width), height: (height)}
16375          */
16376         measure : function(el, text, fixedWidth){
16377             if(!shared){
16378                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
16379             }
16380             shared.bind(el);
16381             shared.setFixedWidth(fixedWidth || 'auto');
16382             return shared.getSize(text);
16383         },
16384
16385         /**
16386          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
16387          * the overhead of multiple calls to initialize the style properties on each measurement.
16388          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
16389          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
16390          * in order to accurately measure the text height
16391          * @return {Roo.util.TextMetrics.Instance} instance The new instance
16392          */
16393         createInstance : function(el, fixedWidth){
16394             return Roo.util.TextMetrics.Instance(el, fixedWidth);
16395         }
16396     };
16397 }();
16398
16399 /**
16400  * @class Roo.util.TextMetrics.Instance
16401  * Instance of  TextMetrics Calcuation
16402  * @constructor
16403  * Create a new TextMetrics Instance
16404  * @param {Object} bindto
16405  * @param {Boolean} fixedWidth
16406  */
16407
16408 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth)
16409 {
16410     var ml = new Roo.Element(document.createElement('div'));
16411     document.body.appendChild(ml.dom);
16412     ml.position('absolute');
16413     ml.setLeftTop(-1000, -1000);
16414     ml.hide();
16415
16416     if(fixedWidth){
16417         ml.setWidth(fixedWidth);
16418     }
16419      
16420     var instance = {
16421         /**
16422          * Returns the size of the specified text based on the internal element's style and width properties
16423          * @param {String} text The text to measure
16424          * @return {Object} An object containing the text's size {width: (width), height: (height)}
16425          */
16426         getSize : function(text){
16427             ml.update(text);
16428             var s = ml.getSize();
16429             ml.update('');
16430             return s;
16431         },
16432
16433         /**
16434          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
16435          * that can affect the size of the rendered text
16436          * @param {String/HTMLElement} el The element, dom node or id
16437          */
16438         bind : function(el){
16439             ml.setStyle(
16440                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
16441             );
16442         },
16443
16444         /**
16445          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
16446          * to set a fixed width in order to accurately measure the text height.
16447          * @param {Number} width The width to set on the element
16448          */
16449         setFixedWidth : function(width){
16450             ml.setWidth(width);
16451         },
16452
16453         /**
16454          * Returns the measured width of the specified text
16455          * @param {String} text The text to measure
16456          * @return {Number} width The width in pixels
16457          */
16458         getWidth : function(text){
16459             ml.dom.style.width = 'auto';
16460             return this.getSize(text).width;
16461         },
16462
16463         /**
16464          * Returns the measured height of the specified text.  For multiline text, be sure to call
16465          * {@link #setFixedWidth} if necessary.
16466          * @param {String} text The text to measure
16467          * @return {Number} height The height in pixels
16468          */
16469         getHeight : function(text){
16470             return this.getSize(text).height;
16471         }
16472     };
16473
16474     instance.bind(bindTo);
16475
16476     return instance;
16477 };
16478
16479 // backwards compat
16480 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
16481  * Based on:
16482  * Ext JS Library 1.1.1
16483  * Copyright(c) 2006-2007, Ext JS, LLC.
16484  *
16485  * Originally Released Under LGPL - original licence link has changed is not relivant.
16486  *
16487  * Fork - LGPL
16488  * <script type="text/javascript">
16489  */
16490
16491 /**
16492  * @class Roo.state.Provider
16493  * Abstract base class for state provider implementations. This class provides methods
16494  * for encoding and decoding <b>typed</b> variables including dates and defines the 
16495  * Provider interface.
16496  */
16497 Roo.state.Provider = function(){
16498     /**
16499      * @event statechange
16500      * Fires when a state change occurs.
16501      * @param {Provider} this This state provider
16502      * @param {String} key The state key which was changed
16503      * @param {String} value The encoded value for the state
16504      */
16505     this.addEvents({
16506         "statechange": true
16507     });
16508     this.state = {};
16509     Roo.state.Provider.superclass.constructor.call(this);
16510 };
16511 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
16512     /**
16513      * Returns the current value for a key
16514      * @param {String} name The key name
16515      * @param {Mixed} defaultValue A default value to return if the key's value is not found
16516      * @return {Mixed} The state data
16517      */
16518     get : function(name, defaultValue){
16519         return typeof this.state[name] == "undefined" ?
16520             defaultValue : this.state[name];
16521     },
16522     
16523     /**
16524      * Clears a value from the state
16525      * @param {String} name The key name
16526      */
16527     clear : function(name){
16528         delete this.state[name];
16529         this.fireEvent("statechange", this, name, null);
16530     },
16531     
16532     /**
16533      * Sets the value for a key
16534      * @param {String} name The key name
16535      * @param {Mixed} value The value to set
16536      */
16537     set : function(name, value){
16538         this.state[name] = value;
16539         this.fireEvent("statechange", this, name, value);
16540     },
16541     
16542     /**
16543      * Decodes a string previously encoded with {@link #encodeValue}.
16544      * @param {String} value The value to decode
16545      * @return {Mixed} The decoded value
16546      */
16547     decodeValue : function(cookie){
16548         var re = /^(a|n|d|b|s|o)\:(.*)$/;
16549         var matches = re.exec(unescape(cookie));
16550         if(!matches || !matches[1]) {
16551             return; // non state cookie
16552         }
16553         var type = matches[1];
16554         var v = matches[2];
16555         switch(type){
16556             case "n":
16557                 return parseFloat(v);
16558             case "d":
16559                 return new Date(Date.parse(v));
16560             case "b":
16561                 return (v == "1");
16562             case "a":
16563                 var all = [];
16564                 var values = v.split("^");
16565                 for(var i = 0, len = values.length; i < len; i++){
16566                     all.push(this.decodeValue(values[i]));
16567                 }
16568                 return all;
16569            case "o":
16570                 var all = {};
16571                 var values = v.split("^");
16572                 for(var i = 0, len = values.length; i < len; i++){
16573                     var kv = values[i].split("=");
16574                     all[kv[0]] = this.decodeValue(kv[1]);
16575                 }
16576                 return all;
16577            default:
16578                 return v;
16579         }
16580     },
16581     
16582     /**
16583      * Encodes a value including type information.  Decode with {@link #decodeValue}.
16584      * @param {Mixed} value The value to encode
16585      * @return {String} The encoded value
16586      */
16587     encodeValue : function(v){
16588         var enc;
16589         if(typeof v == "number"){
16590             enc = "n:" + v;
16591         }else if(typeof v == "boolean"){
16592             enc = "b:" + (v ? "1" : "0");
16593         }else if(v instanceof Date){
16594             enc = "d:" + v.toGMTString();
16595         }else if(v instanceof Array){
16596             var flat = "";
16597             for(var i = 0, len = v.length; i < len; i++){
16598                 flat += this.encodeValue(v[i]);
16599                 if(i != len-1) {
16600                     flat += "^";
16601                 }
16602             }
16603             enc = "a:" + flat;
16604         }else if(typeof v == "object"){
16605             var flat = "";
16606             for(var key in v){
16607                 if(typeof v[key] != "function"){
16608                     flat += key + "=" + this.encodeValue(v[key]) + "^";
16609                 }
16610             }
16611             enc = "o:" + flat.substring(0, flat.length-1);
16612         }else{
16613             enc = "s:" + v;
16614         }
16615         return escape(enc);        
16616     }
16617 });
16618
16619 /*
16620  * Based on:
16621  * Ext JS Library 1.1.1
16622  * Copyright(c) 2006-2007, Ext JS, LLC.
16623  *
16624  * Originally Released Under LGPL - original licence link has changed is not relivant.
16625  *
16626  * Fork - LGPL
16627  * <script type="text/javascript">
16628  */
16629 /**
16630  * @class Roo.state.Manager
16631  * This is the global state manager. By default all components that are "state aware" check this class
16632  * for state information if you don't pass them a custom state provider. In order for this class
16633  * to be useful, it must be initialized with a provider when your application initializes.
16634  <pre><code>
16635 // in your initialization function
16636 init : function(){
16637    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
16638    ...
16639    // supposed you have a {@link Roo.layout.Border}
16640    var layout = new Roo.layout.Border(...);
16641    layout.restoreState();
16642    // or a {Roo.BasicDialog}
16643    var dialog = new Roo.BasicDialog(...);
16644    dialog.restoreState();
16645  </code></pre>
16646  * @static
16647  */
16648 Roo.state.Manager = function(){
16649     var provider = new Roo.state.Provider();
16650     
16651     return {
16652         /**
16653          * Configures the default state provider for your application
16654          * @param {Provider} stateProvider The state provider to set
16655          */
16656         setProvider : function(stateProvider){
16657             provider = stateProvider;
16658         },
16659         
16660         /**
16661          * Returns the current value for a key
16662          * @param {String} name The key name
16663          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
16664          * @return {Mixed} The state data
16665          */
16666         get : function(key, defaultValue){
16667             return provider.get(key, defaultValue);
16668         },
16669         
16670         /**
16671          * Sets the value for a key
16672          * @param {String} name The key name
16673          * @param {Mixed} value The state data
16674          */
16675          set : function(key, value){
16676             provider.set(key, value);
16677         },
16678         
16679         /**
16680          * Clears a value from the state
16681          * @param {String} name The key name
16682          */
16683         clear : function(key){
16684             provider.clear(key);
16685         },
16686         
16687         /**
16688          * Gets the currently configured state provider
16689          * @return {Provider} The state provider
16690          */
16691         getProvider : function(){
16692             return provider;
16693         }
16694     };
16695 }();
16696 /*
16697  * Based on:
16698  * Ext JS Library 1.1.1
16699  * Copyright(c) 2006-2007, Ext JS, LLC.
16700  *
16701  * Originally Released Under LGPL - original licence link has changed is not relivant.
16702  *
16703  * Fork - LGPL
16704  * <script type="text/javascript">
16705  */
16706 /**
16707  * @class Roo.state.CookieProvider
16708  * @extends Roo.state.Provider
16709  * The default Provider implementation which saves state via cookies.
16710  * <br />Usage:
16711  <pre><code>
16712    var cp = new Roo.state.CookieProvider({
16713        path: "/cgi-bin/",
16714        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
16715        domain: "roojs.com"
16716    })
16717    Roo.state.Manager.setProvider(cp);
16718  </code></pre>
16719  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
16720  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
16721  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
16722  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
16723  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
16724  * domain the page is running on including the 'www' like 'www.roojs.com')
16725  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
16726  * @constructor
16727  * Create a new CookieProvider
16728  * @param {Object} config The configuration object
16729  */
16730 Roo.state.CookieProvider = function(config){
16731     Roo.state.CookieProvider.superclass.constructor.call(this);
16732     this.path = "/";
16733     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
16734     this.domain = null;
16735     this.secure = false;
16736     Roo.apply(this, config);
16737     this.state = this.readCookies();
16738 };
16739
16740 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
16741     // private
16742     set : function(name, value){
16743         if(typeof value == "undefined" || value === null){
16744             this.clear(name);
16745             return;
16746         }
16747         this.setCookie(name, value);
16748         Roo.state.CookieProvider.superclass.set.call(this, name, value);
16749     },
16750
16751     // private
16752     clear : function(name){
16753         this.clearCookie(name);
16754         Roo.state.CookieProvider.superclass.clear.call(this, name);
16755     },
16756
16757     // private
16758     readCookies : function(){
16759         var cookies = {};
16760         var c = document.cookie + ";";
16761         var re = /\s?(.*?)=(.*?);/g;
16762         var matches;
16763         while((matches = re.exec(c)) != null){
16764             var name = matches[1];
16765             var value = matches[2];
16766             if(name && name.substring(0,3) == "ys-"){
16767                 cookies[name.substr(3)] = this.decodeValue(value);
16768             }
16769         }
16770         return cookies;
16771     },
16772
16773     // private
16774     setCookie : function(name, value){
16775         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
16776            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
16777            ((this.path == null) ? "" : ("; path=" + this.path)) +
16778            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16779            ((this.secure == true) ? "; secure" : "");
16780     },
16781
16782     // private
16783     clearCookie : function(name){
16784         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
16785            ((this.path == null) ? "" : ("; path=" + this.path)) +
16786            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16787            ((this.secure == true) ? "; secure" : "");
16788     }
16789 });/*
16790  * Based on:
16791  * Ext JS Library 1.1.1
16792  * Copyright(c) 2006-2007, Ext JS, LLC.
16793  *
16794  * Originally Released Under LGPL - original licence link has changed is not relivant.
16795  *
16796  * Fork - LGPL
16797  * <script type="text/javascript">
16798  */
16799  
16800
16801 /**
16802  * @class Roo.ComponentMgr
16803  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
16804  * @static
16805  */
16806 Roo.ComponentMgr = function(){
16807     var all = new Roo.util.MixedCollection();
16808
16809     return {
16810         /**
16811          * Registers a component.
16812          * @param {Roo.Component} c The component
16813          */
16814         register : function(c){
16815             all.add(c);
16816         },
16817
16818         /**
16819          * Unregisters a component.
16820          * @param {Roo.Component} c The component
16821          */
16822         unregister : function(c){
16823             all.remove(c);
16824         },
16825
16826         /**
16827          * Returns a component by id
16828          * @param {String} id The component id
16829          */
16830         get : function(id){
16831             return all.get(id);
16832         },
16833
16834         /**
16835          * Registers a function that will be called when a specified component is added to ComponentMgr
16836          * @param {String} id The component id
16837          * @param {Funtction} fn The callback function
16838          * @param {Object} scope The scope of the callback
16839          */
16840         onAvailable : function(id, fn, scope){
16841             all.on("add", function(index, o){
16842                 if(o.id == id){
16843                     fn.call(scope || o, o);
16844                     all.un("add", fn, scope);
16845                 }
16846             });
16847         }
16848     };
16849 }();/*
16850  * Based on:
16851  * Ext JS Library 1.1.1
16852  * Copyright(c) 2006-2007, Ext JS, LLC.
16853  *
16854  * Originally Released Under LGPL - original licence link has changed is not relivant.
16855  *
16856  * Fork - LGPL
16857  * <script type="text/javascript">
16858  */
16859  
16860 /**
16861  * @class Roo.Component
16862  * @extends Roo.util.Observable
16863  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
16864  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
16865  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
16866  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
16867  * All visual components (widgets) that require rendering into a layout should subclass Component.
16868  * @constructor
16869  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
16870  * 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
16871  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
16872  */
16873 Roo.Component = function(config){
16874     config = config || {};
16875     if(config.tagName || config.dom || typeof config == "string"){ // element object
16876         config = {el: config, id: config.id || config};
16877     }
16878     this.initialConfig = config;
16879
16880     Roo.apply(this, config);
16881     this.addEvents({
16882         /**
16883          * @event disable
16884          * Fires after the component is disabled.
16885              * @param {Roo.Component} this
16886              */
16887         disable : true,
16888         /**
16889          * @event enable
16890          * Fires after the component is enabled.
16891              * @param {Roo.Component} this
16892              */
16893         enable : true,
16894         /**
16895          * @event beforeshow
16896          * Fires before the component is shown.  Return false to stop the show.
16897              * @param {Roo.Component} this
16898              */
16899         beforeshow : true,
16900         /**
16901          * @event show
16902          * Fires after the component is shown.
16903              * @param {Roo.Component} this
16904              */
16905         show : true,
16906         /**
16907          * @event beforehide
16908          * Fires before the component is hidden. Return false to stop the hide.
16909              * @param {Roo.Component} this
16910              */
16911         beforehide : true,
16912         /**
16913          * @event hide
16914          * Fires after the component is hidden.
16915              * @param {Roo.Component} this
16916              */
16917         hide : true,
16918         /**
16919          * @event beforerender
16920          * Fires before the component is rendered. Return false to stop the render.
16921              * @param {Roo.Component} this
16922              */
16923         beforerender : true,
16924         /**
16925          * @event render
16926          * Fires after the component is rendered.
16927              * @param {Roo.Component} this
16928              */
16929         render : true,
16930         /**
16931          * @event beforedestroy
16932          * Fires before the component is destroyed. Return false to stop the destroy.
16933              * @param {Roo.Component} this
16934              */
16935         beforedestroy : true,
16936         /**
16937          * @event destroy
16938          * Fires after the component is destroyed.
16939              * @param {Roo.Component} this
16940              */
16941         destroy : true
16942     });
16943     if(!this.id){
16944         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
16945     }
16946     Roo.ComponentMgr.register(this);
16947     Roo.Component.superclass.constructor.call(this);
16948     this.initComponent();
16949     if(this.renderTo){ // not supported by all components yet. use at your own risk!
16950         this.render(this.renderTo);
16951         delete this.renderTo;
16952     }
16953 };
16954
16955 /** @private */
16956 Roo.Component.AUTO_ID = 1000;
16957
16958 Roo.extend(Roo.Component, Roo.util.Observable, {
16959     /**
16960      * @scope Roo.Component.prototype
16961      * @type {Boolean}
16962      * true if this component is hidden. Read-only.
16963      */
16964     hidden : false,
16965     /**
16966      * @type {Boolean}
16967      * true if this component is disabled. Read-only.
16968      */
16969     disabled : false,
16970     /**
16971      * @type {Boolean}
16972      * true if this component has been rendered. Read-only.
16973      */
16974     rendered : false,
16975     
16976     /** @cfg {String} disableClass
16977      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
16978      */
16979     disabledClass : "x-item-disabled",
16980         /** @cfg {Boolean} allowDomMove
16981          * Whether the component can move the Dom node when rendering (defaults to true).
16982          */
16983     allowDomMove : true,
16984     /** @cfg {String} hideMode (display|visibility)
16985      * How this component should hidden. Supported values are
16986      * "visibility" (css visibility), "offsets" (negative offset position) and
16987      * "display" (css display) - defaults to "display".
16988      */
16989     hideMode: 'display',
16990
16991     /** @private */
16992     ctype : "Roo.Component",
16993
16994     /**
16995      * @cfg {String} actionMode 
16996      * which property holds the element that used for  hide() / show() / disable() / enable()
16997      * default is 'el' for forms you probably want to set this to fieldEl 
16998      */
16999     actionMode : "el",
17000
17001          /**
17002      * @cfg {String} style
17003      * css styles to add to component
17004      * eg. text-align:right;
17005      */
17006     style : false,
17007         
17008     /** @private */
17009     getActionEl : function(){
17010         return this[this.actionMode];
17011     },
17012
17013     initComponent : Roo.emptyFn,
17014     /**
17015      * If this is a lazy rendering component, render it to its container element.
17016      * @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.
17017      */
17018     render : function(container, position){
17019         
17020         if(this.rendered){
17021             return this;
17022         }
17023         
17024         if(this.fireEvent("beforerender", this) === false){
17025             return false;
17026         }
17027         
17028         if(!container && this.el){
17029             this.el = Roo.get(this.el);
17030             container = this.el.dom.parentNode;
17031             this.allowDomMove = false;
17032         }
17033         this.container = Roo.get(container);
17034         this.rendered = true;
17035         if(position !== undefined){
17036             if(typeof position == 'number'){
17037                 position = this.container.dom.childNodes[position];
17038             }else{
17039                 position = Roo.getDom(position);
17040             }
17041         }
17042         this.onRender(this.container, position || null);
17043         if(this.cls){
17044             this.el.addClass(this.cls);
17045             delete this.cls;
17046         }
17047         if(this.style){
17048             this.el.applyStyles(this.style);
17049             delete this.style;
17050         }
17051         this.fireEvent("render", this);
17052         this.afterRender(this.container);
17053         if(this.hidden){
17054             this.hide();
17055         }
17056         if(this.disabled){
17057             this.disable();
17058         }
17059
17060         return this;
17061         
17062     },
17063
17064     /** @private */
17065     // default function is not really useful
17066     onRender : function(ct, position){
17067         if(this.el){
17068             this.el = Roo.get(this.el);
17069             if(this.allowDomMove !== false){
17070                 ct.dom.insertBefore(this.el.dom, position);
17071             }
17072         }
17073     },
17074
17075     /** @private */
17076     getAutoCreate : function(){
17077         var cfg = typeof this.autoCreate == "object" ?
17078                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
17079         if(this.id && !cfg.id){
17080             cfg.id = this.id;
17081         }
17082         return cfg;
17083     },
17084
17085     /** @private */
17086     afterRender : Roo.emptyFn,
17087
17088     /**
17089      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
17090      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
17091      */
17092     destroy : function(){
17093         if(this.fireEvent("beforedestroy", this) !== false){
17094             this.purgeListeners();
17095             this.beforeDestroy();
17096             if(this.rendered){
17097                 this.el.removeAllListeners();
17098                 this.el.remove();
17099                 if(this.actionMode == "container"){
17100                     this.container.remove();
17101                 }
17102             }
17103             this.onDestroy();
17104             Roo.ComponentMgr.unregister(this);
17105             this.fireEvent("destroy", this);
17106         }
17107     },
17108
17109         /** @private */
17110     beforeDestroy : function(){
17111
17112     },
17113
17114         /** @private */
17115         onDestroy : function(){
17116
17117     },
17118
17119     /**
17120      * Returns the underlying {@link Roo.Element}.
17121      * @return {Roo.Element} The element
17122      */
17123     getEl : function(){
17124         return this.el;
17125     },
17126
17127     /**
17128      * Returns the id of this component.
17129      * @return {String}
17130      */
17131     getId : function(){
17132         return this.id;
17133     },
17134
17135     /**
17136      * Try to focus this component.
17137      * @param {Boolean} selectText True to also select the text in this component (if applicable)
17138      * @return {Roo.Component} this
17139      */
17140     focus : function(selectText){
17141         if(this.rendered){
17142             this.el.focus();
17143             if(selectText === true){
17144                 this.el.dom.select();
17145             }
17146         }
17147         return this;
17148     },
17149
17150     /** @private */
17151     blur : function(){
17152         if(this.rendered){
17153             this.el.blur();
17154         }
17155         return this;
17156     },
17157
17158     /**
17159      * Disable this component.
17160      * @return {Roo.Component} this
17161      */
17162     disable : function(){
17163         if(this.rendered){
17164             this.onDisable();
17165         }
17166         this.disabled = true;
17167         this.fireEvent("disable", this);
17168         return this;
17169     },
17170
17171         // private
17172     onDisable : function(){
17173         this.getActionEl().addClass(this.disabledClass);
17174         this.el.dom.disabled = true;
17175     },
17176
17177     /**
17178      * Enable this component.
17179      * @return {Roo.Component} this
17180      */
17181     enable : function(){
17182         if(this.rendered){
17183             this.onEnable();
17184         }
17185         this.disabled = false;
17186         this.fireEvent("enable", this);
17187         return this;
17188     },
17189
17190         // private
17191     onEnable : function(){
17192         this.getActionEl().removeClass(this.disabledClass);
17193         this.el.dom.disabled = false;
17194     },
17195
17196     /**
17197      * Convenience function for setting disabled/enabled by boolean.
17198      * @param {Boolean} disabled
17199      */
17200     setDisabled : function(disabled){
17201         this[disabled ? "disable" : "enable"]();
17202     },
17203
17204     /**
17205      * Show this component.
17206      * @return {Roo.Component} this
17207      */
17208     show: function(){
17209         if(this.fireEvent("beforeshow", this) !== false){
17210             this.hidden = false;
17211             if(this.rendered){
17212                 this.onShow();
17213             }
17214             this.fireEvent("show", this);
17215         }
17216         return this;
17217     },
17218
17219     // private
17220     onShow : function(){
17221         var ae = this.getActionEl();
17222         if(this.hideMode == 'visibility'){
17223             ae.dom.style.visibility = "visible";
17224         }else if(this.hideMode == 'offsets'){
17225             ae.removeClass('x-hidden');
17226         }else{
17227             ae.dom.style.display = "";
17228         }
17229     },
17230
17231     /**
17232      * Hide this component.
17233      * @return {Roo.Component} this
17234      */
17235     hide: function(){
17236         if(this.fireEvent("beforehide", this) !== false){
17237             this.hidden = true;
17238             if(this.rendered){
17239                 this.onHide();
17240             }
17241             this.fireEvent("hide", this);
17242         }
17243         return this;
17244     },
17245
17246     // private
17247     onHide : function(){
17248         var ae = this.getActionEl();
17249         if(this.hideMode == 'visibility'){
17250             ae.dom.style.visibility = "hidden";
17251         }else if(this.hideMode == 'offsets'){
17252             ae.addClass('x-hidden');
17253         }else{
17254             ae.dom.style.display = "none";
17255         }
17256     },
17257
17258     /**
17259      * Convenience function to hide or show this component by boolean.
17260      * @param {Boolean} visible True to show, false to hide
17261      * @return {Roo.Component} this
17262      */
17263     setVisible: function(visible){
17264         if(visible) {
17265             this.show();
17266         }else{
17267             this.hide();
17268         }
17269         return this;
17270     },
17271
17272     /**
17273      * Returns true if this component is visible.
17274      */
17275     isVisible : function(){
17276         return this.getActionEl().isVisible();
17277     },
17278
17279     cloneConfig : function(overrides){
17280         overrides = overrides || {};
17281         var id = overrides.id || Roo.id();
17282         var cfg = Roo.applyIf(overrides, this.initialConfig);
17283         cfg.id = id; // prevent dup id
17284         return new this.constructor(cfg);
17285     }
17286 });/*
17287  * Based on:
17288  * Ext JS Library 1.1.1
17289  * Copyright(c) 2006-2007, Ext JS, LLC.
17290  *
17291  * Originally Released Under LGPL - original licence link has changed is not relivant.
17292  *
17293  * Fork - LGPL
17294  * <script type="text/javascript">
17295  */
17296
17297 /**
17298  * @class Roo.BoxComponent
17299  * @extends Roo.Component
17300  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
17301  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
17302  * container classes should subclass BoxComponent so that they will work consistently when nested within other Roo
17303  * layout containers.
17304  * @constructor
17305  * @param {Roo.Element/String/Object} config The configuration options.
17306  */
17307 Roo.BoxComponent = function(config){
17308     Roo.Component.call(this, config);
17309     this.addEvents({
17310         /**
17311          * @event resize
17312          * Fires after the component is resized.
17313              * @param {Roo.Component} this
17314              * @param {Number} adjWidth The box-adjusted width that was set
17315              * @param {Number} adjHeight The box-adjusted height that was set
17316              * @param {Number} rawWidth The width that was originally specified
17317              * @param {Number} rawHeight The height that was originally specified
17318              */
17319         resize : true,
17320         /**
17321          * @event move
17322          * Fires after the component is moved.
17323              * @param {Roo.Component} this
17324              * @param {Number} x The new x position
17325              * @param {Number} y The new y position
17326              */
17327         move : true
17328     });
17329 };
17330
17331 Roo.extend(Roo.BoxComponent, Roo.Component, {
17332     // private, set in afterRender to signify that the component has been rendered
17333     boxReady : false,
17334     // private, used to defer height settings to subclasses
17335     deferHeight: false,
17336     /** @cfg {Number} width
17337      * width (optional) size of component
17338      */
17339      /** @cfg {Number} height
17340      * height (optional) size of component
17341      */
17342      
17343     /**
17344      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
17345      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
17346      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
17347      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
17348      * @return {Roo.BoxComponent} this
17349      */
17350     setSize : function(w, h){
17351         // support for standard size objects
17352         if(typeof w == 'object'){
17353             h = w.height;
17354             w = w.width;
17355         }
17356         // not rendered
17357         if(!this.boxReady){
17358             this.width = w;
17359             this.height = h;
17360             return this;
17361         }
17362
17363         // prevent recalcs when not needed
17364         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
17365             return this;
17366         }
17367         this.lastSize = {width: w, height: h};
17368
17369         var adj = this.adjustSize(w, h);
17370         var aw = adj.width, ah = adj.height;
17371         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
17372             var rz = this.getResizeEl();
17373             if(!this.deferHeight && aw !== undefined && ah !== undefined){
17374                 rz.setSize(aw, ah);
17375             }else if(!this.deferHeight && ah !== undefined){
17376                 rz.setHeight(ah);
17377             }else if(aw !== undefined){
17378                 rz.setWidth(aw);
17379             }
17380             this.onResize(aw, ah, w, h);
17381             this.fireEvent('resize', this, aw, ah, w, h);
17382         }
17383         return this;
17384     },
17385
17386     /**
17387      * Gets the current size of the component's underlying element.
17388      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
17389      */
17390     getSize : function(){
17391         return this.el.getSize();
17392     },
17393
17394     /**
17395      * Gets the current XY position of the component's underlying element.
17396      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
17397      * @return {Array} The XY position of the element (e.g., [100, 200])
17398      */
17399     getPosition : function(local){
17400         if(local === true){
17401             return [this.el.getLeft(true), this.el.getTop(true)];
17402         }
17403         return this.xy || this.el.getXY();
17404     },
17405
17406     /**
17407      * Gets the current box measurements of the component's underlying element.
17408      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
17409      * @returns {Object} box An object in the format {x, y, width, height}
17410      */
17411     getBox : function(local){
17412         var s = this.el.getSize();
17413         if(local){
17414             s.x = this.el.getLeft(true);
17415             s.y = this.el.getTop(true);
17416         }else{
17417             var xy = this.xy || this.el.getXY();
17418             s.x = xy[0];
17419             s.y = xy[1];
17420         }
17421         return s;
17422     },
17423
17424     /**
17425      * Sets the current box measurements of the component's underlying element.
17426      * @param {Object} box An object in the format {x, y, width, height}
17427      * @returns {Roo.BoxComponent} this
17428      */
17429     updateBox : function(box){
17430         this.setSize(box.width, box.height);
17431         this.setPagePosition(box.x, box.y);
17432         return this;
17433     },
17434
17435     // protected
17436     getResizeEl : function(){
17437         return this.resizeEl || this.el;
17438     },
17439
17440     // protected
17441     getPositionEl : function(){
17442         return this.positionEl || this.el;
17443     },
17444
17445     /**
17446      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
17447      * This method fires the move event.
17448      * @param {Number} left The new left
17449      * @param {Number} top The new top
17450      * @returns {Roo.BoxComponent} this
17451      */
17452     setPosition : function(x, y){
17453         this.x = x;
17454         this.y = y;
17455         if(!this.boxReady){
17456             return this;
17457         }
17458         var adj = this.adjustPosition(x, y);
17459         var ax = adj.x, ay = adj.y;
17460
17461         var el = this.getPositionEl();
17462         if(ax !== undefined || ay !== undefined){
17463             if(ax !== undefined && ay !== undefined){
17464                 el.setLeftTop(ax, ay);
17465             }else if(ax !== undefined){
17466                 el.setLeft(ax);
17467             }else if(ay !== undefined){
17468                 el.setTop(ay);
17469             }
17470             this.onPosition(ax, ay);
17471             this.fireEvent('move', this, ax, ay);
17472         }
17473         return this;
17474     },
17475
17476     /**
17477      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
17478      * This method fires the move event.
17479      * @param {Number} x The new x position
17480      * @param {Number} y The new y position
17481      * @returns {Roo.BoxComponent} this
17482      */
17483     setPagePosition : function(x, y){
17484         this.pageX = x;
17485         this.pageY = y;
17486         if(!this.boxReady){
17487             return;
17488         }
17489         if(x === undefined || y === undefined){ // cannot translate undefined points
17490             return;
17491         }
17492         var p = this.el.translatePoints(x, y);
17493         this.setPosition(p.left, p.top);
17494         return this;
17495     },
17496
17497     // private
17498     onRender : function(ct, position){
17499         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
17500         if(this.resizeEl){
17501             this.resizeEl = Roo.get(this.resizeEl);
17502         }
17503         if(this.positionEl){
17504             this.positionEl = Roo.get(this.positionEl);
17505         }
17506     },
17507
17508     // private
17509     afterRender : function(){
17510         Roo.BoxComponent.superclass.afterRender.call(this);
17511         this.boxReady = true;
17512         this.setSize(this.width, this.height);
17513         if(this.x || this.y){
17514             this.setPosition(this.x, this.y);
17515         }
17516         if(this.pageX || this.pageY){
17517             this.setPagePosition(this.pageX, this.pageY);
17518         }
17519     },
17520
17521     /**
17522      * Force the component's size to recalculate based on the underlying element's current height and width.
17523      * @returns {Roo.BoxComponent} this
17524      */
17525     syncSize : function(){
17526         delete this.lastSize;
17527         this.setSize(this.el.getWidth(), this.el.getHeight());
17528         return this;
17529     },
17530
17531     /**
17532      * Called after the component is resized, this method is empty by default but can be implemented by any
17533      * subclass that needs to perform custom logic after a resize occurs.
17534      * @param {Number} adjWidth The box-adjusted width that was set
17535      * @param {Number} adjHeight The box-adjusted height that was set
17536      * @param {Number} rawWidth The width that was originally specified
17537      * @param {Number} rawHeight The height that was originally specified
17538      */
17539     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
17540
17541     },
17542
17543     /**
17544      * Called after the component is moved, this method is empty by default but can be implemented by any
17545      * subclass that needs to perform custom logic after a move occurs.
17546      * @param {Number} x The new x position
17547      * @param {Number} y The new y position
17548      */
17549     onPosition : function(x, y){
17550
17551     },
17552
17553     // private
17554     adjustSize : function(w, h){
17555         if(this.autoWidth){
17556             w = 'auto';
17557         }
17558         if(this.autoHeight){
17559             h = 'auto';
17560         }
17561         return {width : w, height: h};
17562     },
17563
17564     // private
17565     adjustPosition : function(x, y){
17566         return {x : x, y: y};
17567     }
17568 });/*
17569  * Based on:
17570  * Ext JS Library 1.1.1
17571  * Copyright(c) 2006-2007, Ext JS, LLC.
17572  *
17573  * Originally Released Under LGPL - original licence link has changed is not relivant.
17574  *
17575  * Fork - LGPL
17576  * <script type="text/javascript">
17577  */
17578  (function(){ 
17579 /**
17580  * @class Roo.Layer
17581  * @extends Roo.Element
17582  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
17583  * automatic maintaining of shadow/shim positions.
17584  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
17585  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
17586  * you can pass a string with a CSS class name. False turns off the shadow.
17587  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
17588  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
17589  * @cfg {String} cls CSS class to add to the element
17590  * @cfg {Number} zindex Starting z-index (defaults to 11000)
17591  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
17592  * @constructor
17593  * @param {Object} config An object with config options.
17594  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
17595  */
17596
17597 Roo.Layer = function(config, existingEl){
17598     config = config || {};
17599     var dh = Roo.DomHelper;
17600     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
17601     if(existingEl){
17602         this.dom = Roo.getDom(existingEl);
17603     }
17604     if(!this.dom){
17605         var o = config.dh || {tag: "div", cls: "x-layer"};
17606         this.dom = dh.append(pel, o);
17607     }
17608     if(config.cls){
17609         this.addClass(config.cls);
17610     }
17611     this.constrain = config.constrain !== false;
17612     this.visibilityMode = Roo.Element.VISIBILITY;
17613     if(config.id){
17614         this.id = this.dom.id = config.id;
17615     }else{
17616         this.id = Roo.id(this.dom);
17617     }
17618     this.zindex = config.zindex || this.getZIndex();
17619     this.position("absolute", this.zindex);
17620     if(config.shadow){
17621         this.shadowOffset = config.shadowOffset || 4;
17622         this.shadow = new Roo.Shadow({
17623             offset : this.shadowOffset,
17624             mode : config.shadow
17625         });
17626     }else{
17627         this.shadowOffset = 0;
17628     }
17629     this.useShim = config.shim !== false && Roo.useShims;
17630     this.useDisplay = config.useDisplay;
17631     this.hide();
17632 };
17633
17634 var supr = Roo.Element.prototype;
17635
17636 // shims are shared among layer to keep from having 100 iframes
17637 var shims = [];
17638
17639 Roo.extend(Roo.Layer, Roo.Element, {
17640
17641     getZIndex : function(){
17642         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
17643     },
17644
17645     getShim : function(){
17646         if(!this.useShim){
17647             return null;
17648         }
17649         if(this.shim){
17650             return this.shim;
17651         }
17652         var shim = shims.shift();
17653         if(!shim){
17654             shim = this.createShim();
17655             shim.enableDisplayMode('block');
17656             shim.dom.style.display = 'none';
17657             shim.dom.style.visibility = 'visible';
17658         }
17659         var pn = this.dom.parentNode;
17660         if(shim.dom.parentNode != pn){
17661             pn.insertBefore(shim.dom, this.dom);
17662         }
17663         shim.setStyle('z-index', this.getZIndex()-2);
17664         this.shim = shim;
17665         return shim;
17666     },
17667
17668     hideShim : function(){
17669         if(this.shim){
17670             this.shim.setDisplayed(false);
17671             shims.push(this.shim);
17672             delete this.shim;
17673         }
17674     },
17675
17676     disableShadow : function(){
17677         if(this.shadow){
17678             this.shadowDisabled = true;
17679             this.shadow.hide();
17680             this.lastShadowOffset = this.shadowOffset;
17681             this.shadowOffset = 0;
17682         }
17683     },
17684
17685     enableShadow : function(show){
17686         if(this.shadow){
17687             this.shadowDisabled = false;
17688             this.shadowOffset = this.lastShadowOffset;
17689             delete this.lastShadowOffset;
17690             if(show){
17691                 this.sync(true);
17692             }
17693         }
17694     },
17695
17696     // private
17697     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
17698     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
17699     sync : function(doShow){
17700         var sw = this.shadow;
17701         if(!this.updating && this.isVisible() && (sw || this.useShim)){
17702             var sh = this.getShim();
17703
17704             var w = this.getWidth(),
17705                 h = this.getHeight();
17706
17707             var l = this.getLeft(true),
17708                 t = this.getTop(true);
17709
17710             if(sw && !this.shadowDisabled){
17711                 if(doShow && !sw.isVisible()){
17712                     sw.show(this);
17713                 }else{
17714                     sw.realign(l, t, w, h);
17715                 }
17716                 if(sh){
17717                     if(doShow){
17718                        sh.show();
17719                     }
17720                     // fit the shim behind the shadow, so it is shimmed too
17721                     var a = sw.adjusts, s = sh.dom.style;
17722                     s.left = (Math.min(l, l+a.l))+"px";
17723                     s.top = (Math.min(t, t+a.t))+"px";
17724                     s.width = (w+a.w)+"px";
17725                     s.height = (h+a.h)+"px";
17726                 }
17727             }else if(sh){
17728                 if(doShow){
17729                    sh.show();
17730                 }
17731                 sh.setSize(w, h);
17732                 sh.setLeftTop(l, t);
17733             }
17734             
17735         }
17736     },
17737
17738     // private
17739     destroy : function(){
17740         this.hideShim();
17741         if(this.shadow){
17742             this.shadow.hide();
17743         }
17744         this.removeAllListeners();
17745         var pn = this.dom.parentNode;
17746         if(pn){
17747             pn.removeChild(this.dom);
17748         }
17749         Roo.Element.uncache(this.id);
17750     },
17751
17752     remove : function(){
17753         this.destroy();
17754     },
17755
17756     // private
17757     beginUpdate : function(){
17758         this.updating = true;
17759     },
17760
17761     // private
17762     endUpdate : function(){
17763         this.updating = false;
17764         this.sync(true);
17765     },
17766
17767     // private
17768     hideUnders : function(negOffset){
17769         if(this.shadow){
17770             this.shadow.hide();
17771         }
17772         this.hideShim();
17773     },
17774
17775     // private
17776     constrainXY : function(){
17777         if(this.constrain){
17778             var vw = Roo.lib.Dom.getViewWidth(),
17779                 vh = Roo.lib.Dom.getViewHeight();
17780             var s = Roo.get(document).getScroll();
17781
17782             var xy = this.getXY();
17783             var x = xy[0], y = xy[1];   
17784             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
17785             // only move it if it needs it
17786             var moved = false;
17787             // first validate right/bottom
17788             if((x + w) > vw+s.left){
17789                 x = vw - w - this.shadowOffset;
17790                 moved = true;
17791             }
17792             if((y + h) > vh+s.top){
17793                 y = vh - h - this.shadowOffset;
17794                 moved = true;
17795             }
17796             // then make sure top/left isn't negative
17797             if(x < s.left){
17798                 x = s.left;
17799                 moved = true;
17800             }
17801             if(y < s.top){
17802                 y = s.top;
17803                 moved = true;
17804             }
17805             if(moved){
17806                 if(this.avoidY){
17807                     var ay = this.avoidY;
17808                     if(y <= ay && (y+h) >= ay){
17809                         y = ay-h-5;   
17810                     }
17811                 }
17812                 xy = [x, y];
17813                 this.storeXY(xy);
17814                 supr.setXY.call(this, xy);
17815                 this.sync();
17816             }
17817         }
17818     },
17819
17820     isVisible : function(){
17821         return this.visible;    
17822     },
17823
17824     // private
17825     showAction : function(){
17826         this.visible = true; // track visibility to prevent getStyle calls
17827         if(this.useDisplay === true){
17828             this.setDisplayed("");
17829         }else if(this.lastXY){
17830             supr.setXY.call(this, this.lastXY);
17831         }else if(this.lastLT){
17832             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
17833         }
17834     },
17835
17836     // private
17837     hideAction : function(){
17838         this.visible = false;
17839         if(this.useDisplay === true){
17840             this.setDisplayed(false);
17841         }else{
17842             this.setLeftTop(-10000,-10000);
17843         }
17844     },
17845
17846     // overridden Element method
17847     setVisible : function(v, a, d, c, e){
17848         if(v){
17849             this.showAction();
17850         }
17851         if(a && v){
17852             var cb = function(){
17853                 this.sync(true);
17854                 if(c){
17855                     c();
17856                 }
17857             }.createDelegate(this);
17858             supr.setVisible.call(this, true, true, d, cb, e);
17859         }else{
17860             if(!v){
17861                 this.hideUnders(true);
17862             }
17863             var cb = c;
17864             if(a){
17865                 cb = function(){
17866                     this.hideAction();
17867                     if(c){
17868                         c();
17869                     }
17870                 }.createDelegate(this);
17871             }
17872             supr.setVisible.call(this, v, a, d, cb, e);
17873             if(v){
17874                 this.sync(true);
17875             }else if(!a){
17876                 this.hideAction();
17877             }
17878         }
17879     },
17880
17881     storeXY : function(xy){
17882         delete this.lastLT;
17883         this.lastXY = xy;
17884     },
17885
17886     storeLeftTop : function(left, top){
17887         delete this.lastXY;
17888         this.lastLT = [left, top];
17889     },
17890
17891     // private
17892     beforeFx : function(){
17893         this.beforeAction();
17894         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
17895     },
17896
17897     // private
17898     afterFx : function(){
17899         Roo.Layer.superclass.afterFx.apply(this, arguments);
17900         this.sync(this.isVisible());
17901     },
17902
17903     // private
17904     beforeAction : function(){
17905         if(!this.updating && this.shadow){
17906             this.shadow.hide();
17907         }
17908     },
17909
17910     // overridden Element method
17911     setLeft : function(left){
17912         this.storeLeftTop(left, this.getTop(true));
17913         supr.setLeft.apply(this, arguments);
17914         this.sync();
17915     },
17916
17917     setTop : function(top){
17918         this.storeLeftTop(this.getLeft(true), top);
17919         supr.setTop.apply(this, arguments);
17920         this.sync();
17921     },
17922
17923     setLeftTop : function(left, top){
17924         this.storeLeftTop(left, top);
17925         supr.setLeftTop.apply(this, arguments);
17926         this.sync();
17927     },
17928
17929     setXY : function(xy, a, d, c, e){
17930         this.fixDisplay();
17931         this.beforeAction();
17932         this.storeXY(xy);
17933         var cb = this.createCB(c);
17934         supr.setXY.call(this, xy, a, d, cb, e);
17935         if(!a){
17936             cb();
17937         }
17938     },
17939
17940     // private
17941     createCB : function(c){
17942         var el = this;
17943         return function(){
17944             el.constrainXY();
17945             el.sync(true);
17946             if(c){
17947                 c();
17948             }
17949         };
17950     },
17951
17952     // overridden Element method
17953     setX : function(x, a, d, c, e){
17954         this.setXY([x, this.getY()], a, d, c, e);
17955     },
17956
17957     // overridden Element method
17958     setY : function(y, a, d, c, e){
17959         this.setXY([this.getX(), y], a, d, c, e);
17960     },
17961
17962     // overridden Element method
17963     setSize : function(w, h, a, d, c, e){
17964         this.beforeAction();
17965         var cb = this.createCB(c);
17966         supr.setSize.call(this, w, h, a, d, cb, e);
17967         if(!a){
17968             cb();
17969         }
17970     },
17971
17972     // overridden Element method
17973     setWidth : function(w, a, d, c, e){
17974         this.beforeAction();
17975         var cb = this.createCB(c);
17976         supr.setWidth.call(this, w, a, d, cb, e);
17977         if(!a){
17978             cb();
17979         }
17980     },
17981
17982     // overridden Element method
17983     setHeight : function(h, a, d, c, e){
17984         this.beforeAction();
17985         var cb = this.createCB(c);
17986         supr.setHeight.call(this, h, a, d, cb, e);
17987         if(!a){
17988             cb();
17989         }
17990     },
17991
17992     // overridden Element method
17993     setBounds : function(x, y, w, h, a, d, c, e){
17994         this.beforeAction();
17995         var cb = this.createCB(c);
17996         if(!a){
17997             this.storeXY([x, y]);
17998             supr.setXY.call(this, [x, y]);
17999             supr.setSize.call(this, w, h, a, d, cb, e);
18000             cb();
18001         }else{
18002             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
18003         }
18004         return this;
18005     },
18006     
18007     /**
18008      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
18009      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
18010      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
18011      * @param {Number} zindex The new z-index to set
18012      * @return {this} The Layer
18013      */
18014     setZIndex : function(zindex){
18015         this.zindex = zindex;
18016         this.setStyle("z-index", zindex + 2);
18017         if(this.shadow){
18018             this.shadow.setZIndex(zindex + 1);
18019         }
18020         if(this.shim){
18021             this.shim.setStyle("z-index", zindex);
18022         }
18023     }
18024 });
18025 })();/*
18026  * Original code for Roojs - LGPL
18027  * <script type="text/javascript">
18028  */
18029  
18030 /**
18031  * @class Roo.XComponent
18032  * A delayed Element creator...
18033  * Or a way to group chunks of interface together.
18034  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
18035  *  used in conjunction with XComponent.build() it will create an instance of each element,
18036  *  then call addxtype() to build the User interface.
18037  * 
18038  * Mypart.xyx = new Roo.XComponent({
18039
18040     parent : 'Mypart.xyz', // empty == document.element.!!
18041     order : '001',
18042     name : 'xxxx'
18043     region : 'xxxx'
18044     disabled : function() {} 
18045      
18046     tree : function() { // return an tree of xtype declared components
18047         var MODULE = this;
18048         return 
18049         {
18050             xtype : 'NestedLayoutPanel',
18051             // technicall
18052         }
18053      ]
18054  *})
18055  *
18056  *
18057  * It can be used to build a big heiracy, with parent etc.
18058  * or you can just use this to render a single compoent to a dom element
18059  * MYPART.render(Roo.Element | String(id) | dom_element )
18060  *
18061  *
18062  * Usage patterns.
18063  *
18064  * Classic Roo
18065  *
18066  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
18067  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
18068  *
18069  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
18070  *
18071  * When the top level is false, a 'Roo.layout.Border' is created and the element is flagged as 'topModule'
18072  * - if mulitple topModules exist, the last one is defined as the top module.
18073  *
18074  * Embeded Roo
18075  * 
18076  * When the top level or multiple modules are to embedded into a existing HTML page,
18077  * the parent element can container '#id' of the element where the module will be drawn.
18078  *
18079  * Bootstrap Roo
18080  *
18081  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
18082  * it relies more on a include mechanism, where sub modules are included into an outer page.
18083  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
18084  * 
18085  * Bootstrap Roo Included elements
18086  *
18087  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
18088  * hence confusing the component builder as it thinks there are multiple top level elements. 
18089  *
18090  * String Over-ride & Translations
18091  *
18092  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
18093  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
18094  * are needed. @see Roo.XComponent.overlayString  
18095  * 
18096  * 
18097  * 
18098  * @extends Roo.util.Observable
18099  * @constructor
18100  * @param cfg {Object} configuration of component
18101  * 
18102  */
18103 Roo.XComponent = function(cfg) {
18104     Roo.apply(this, cfg);
18105     this.addEvents({ 
18106         /**
18107              * @event built
18108              * Fires when this the componnt is built
18109              * @param {Roo.XComponent} c the component
18110              */
18111         'built' : true
18112         
18113     });
18114     this.region = this.region || 'center'; // default..
18115     Roo.XComponent.register(this);
18116     this.modules = false;
18117     this.el = false; // where the layout goes..
18118     
18119     
18120 }
18121 Roo.extend(Roo.XComponent, Roo.util.Observable, {
18122     /**
18123      * @property el
18124      * The created element (with Roo.factory())
18125      * @type {Roo.Layout}
18126      */
18127     el  : false,
18128     
18129     /**
18130      * @property el
18131      * for BC  - use el in new code
18132      * @type {Roo.Layout}
18133      */
18134     panel : false,
18135     
18136     /**
18137      * @property layout
18138      * for BC  - use el in new code
18139      * @type {Roo.Layout}
18140      */
18141     layout : false,
18142     
18143      /**
18144      * @cfg {Function|boolean} disabled
18145      * If this module is disabled by some rule, return true from the funtion
18146      */
18147     disabled : false,
18148     
18149     /**
18150      * @cfg {String} parent 
18151      * Name of parent element which it get xtype added to..
18152      */
18153     parent: false,
18154     
18155     /**
18156      * @cfg {String} order
18157      * Used to set the order in which elements are created (usefull for multiple tabs)
18158      */
18159     
18160     order : false,
18161     /**
18162      * @cfg {String} name
18163      * String to display while loading.
18164      */
18165     name : false,
18166     /**
18167      * @cfg {String} region
18168      * Region to render component to (defaults to center)
18169      */
18170     region : 'center',
18171     
18172     /**
18173      * @cfg {Array} items
18174      * A single item array - the first element is the root of the tree..
18175      * It's done this way to stay compatible with the Xtype system...
18176      */
18177     items : false,
18178     
18179     /**
18180      * @property _tree
18181      * The method that retuns the tree of parts that make up this compoennt 
18182      * @type {function}
18183      */
18184     _tree  : false,
18185     
18186      /**
18187      * render
18188      * render element to dom or tree
18189      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
18190      */
18191     
18192     render : function(el)
18193     {
18194         
18195         el = el || false;
18196         var hp = this.parent ? 1 : 0;
18197         Roo.debug &&  Roo.log(this);
18198         
18199         var tree = this._tree ? this._tree() : this.tree();
18200
18201         
18202         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
18203             // if parent is a '#.....' string, then let's use that..
18204             var ename = this.parent.substr(1);
18205             this.parent = false;
18206             Roo.debug && Roo.log(ename);
18207             switch (ename) {
18208                 case 'bootstrap-body':
18209                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
18210                         // this is the BorderLayout standard?
18211                        this.parent = { el : true };
18212                        break;
18213                     }
18214                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
18215                         // need to insert stuff...
18216                         this.parent =  {
18217                              el : new Roo.bootstrap.layout.Border({
18218                                  el : document.body, 
18219                      
18220                                  center: {
18221                                     titlebar: false,
18222                                     autoScroll:false,
18223                                     closeOnTab: true,
18224                                     tabPosition: 'top',
18225                                       //resizeTabs: true,
18226                                     alwaysShowTabs: true,
18227                                     hideTabs: false
18228                                      //minTabWidth: 140
18229                                  }
18230                              })
18231                         
18232                          };
18233                          break;
18234                     }
18235                          
18236                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
18237                         this.parent = { el :  new  Roo.bootstrap.Body() };
18238                         Roo.debug && Roo.log("setting el to doc body");
18239                          
18240                     } else {
18241                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
18242                     }
18243                     break;
18244                 case 'bootstrap':
18245                     this.parent = { el : true};
18246                     // fall through
18247                 default:
18248                     el = Roo.get(ename);
18249                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
18250                         this.parent = { el : true};
18251                     }
18252                     
18253                     break;
18254             }
18255                 
18256             
18257             if (!el && !this.parent) {
18258                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
18259                 return;
18260             }
18261         }
18262         
18263         Roo.debug && Roo.log("EL:");
18264         Roo.debug && Roo.log(el);
18265         Roo.debug && Roo.log("this.parent.el:");
18266         Roo.debug && Roo.log(this.parent.el);
18267         
18268
18269         // altertive root elements ??? - we need a better way to indicate these.
18270         var is_alt = Roo.XComponent.is_alt ||
18271                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
18272                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
18273                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
18274         
18275         
18276         
18277         if (!this.parent && is_alt) {
18278             //el = Roo.get(document.body);
18279             this.parent = { el : true };
18280         }
18281             
18282             
18283         
18284         if (!this.parent) {
18285             
18286             Roo.debug && Roo.log("no parent - creating one");
18287             
18288             el = el ? Roo.get(el) : false;      
18289             
18290             if (typeof(Roo.layout.Border) == 'undefined' ) {
18291                 
18292                 this.parent =  {
18293                     el : new Roo.bootstrap.layout.Border({
18294                         el: el || document.body,
18295                     
18296                         center: {
18297                             titlebar: false,
18298                             autoScroll:false,
18299                             closeOnTab: true,
18300                             tabPosition: 'top',
18301                              //resizeTabs: true,
18302                             alwaysShowTabs: false,
18303                             hideTabs: true,
18304                             minTabWidth: 140,
18305                             overflow: 'visible'
18306                          }
18307                      })
18308                 };
18309             } else {
18310             
18311                 // it's a top level one..
18312                 this.parent =  {
18313                     el : new Roo.layout.Border(el || document.body, {
18314                         center: {
18315                             titlebar: false,
18316                             autoScroll:false,
18317                             closeOnTab: true,
18318                             tabPosition: 'top',
18319                              //resizeTabs: true,
18320                             alwaysShowTabs: el && hp? false :  true,
18321                             hideTabs: el || !hp ? true :  false,
18322                             minTabWidth: 140
18323                          }
18324                     })
18325                 };
18326             }
18327         }
18328         
18329         if (!this.parent.el) {
18330                 // probably an old style ctor, which has been disabled.
18331                 return;
18332
18333         }
18334                 // The 'tree' method is  '_tree now' 
18335             
18336         tree.region = tree.region || this.region;
18337         var is_body = false;
18338         if (this.parent.el === true) {
18339             // bootstrap... - body..
18340             if (el) {
18341                 tree.el = el;
18342             }
18343             this.parent.el = Roo.factory(tree);
18344             is_body = true;
18345         }
18346         
18347         this.el = this.parent.el.addxtype(tree, undefined, is_body);
18348         this.fireEvent('built', this);
18349         
18350         this.panel = this.el;
18351         this.layout = this.panel.layout;
18352         this.parentLayout = this.parent.layout  || false;  
18353          
18354     }
18355     
18356 });
18357
18358 Roo.apply(Roo.XComponent, {
18359     /**
18360      * @property  hideProgress
18361      * true to disable the building progress bar.. usefull on single page renders.
18362      * @type Boolean
18363      */
18364     hideProgress : false,
18365     /**
18366      * @property  buildCompleted
18367      * True when the builder has completed building the interface.
18368      * @type Boolean
18369      */
18370     buildCompleted : false,
18371      
18372     /**
18373      * @property  topModule
18374      * the upper most module - uses document.element as it's constructor.
18375      * @type Object
18376      */
18377      
18378     topModule  : false,
18379       
18380     /**
18381      * @property  modules
18382      * array of modules to be created by registration system.
18383      * @type {Array} of Roo.XComponent
18384      */
18385     
18386     modules : [],
18387     /**
18388      * @property  elmodules
18389      * array of modules to be created by which use #ID 
18390      * @type {Array} of Roo.XComponent
18391      */
18392      
18393     elmodules : [],
18394
18395      /**
18396      * @property  is_alt
18397      * Is an alternative Root - normally used by bootstrap or other systems,
18398      *    where the top element in the tree can wrap 'body' 
18399      * @type {boolean}  (default false)
18400      */
18401      
18402     is_alt : false,
18403     /**
18404      * @property  build_from_html
18405      * Build elements from html - used by bootstrap HTML stuff 
18406      *    - this is cleared after build is completed
18407      * @type {boolean}    (default false)
18408      */
18409      
18410     build_from_html : false,
18411     /**
18412      * Register components to be built later.
18413      *
18414      * This solves the following issues
18415      * - Building is not done on page load, but after an authentication process has occured.
18416      * - Interface elements are registered on page load
18417      * - Parent Interface elements may not be loaded before child, so this handles that..
18418      * 
18419      *
18420      * example:
18421      * 
18422      * MyApp.register({
18423           order : '000001',
18424           module : 'Pman.Tab.projectMgr',
18425           region : 'center',
18426           parent : 'Pman.layout',
18427           disabled : false,  // or use a function..
18428         })
18429      
18430      * * @param {Object} details about module
18431      */
18432     register : function(obj) {
18433                 
18434         Roo.XComponent.event.fireEvent('register', obj);
18435         switch(typeof(obj.disabled) ) {
18436                 
18437             case 'undefined':
18438                 break;
18439             
18440             case 'function':
18441                 if ( obj.disabled() ) {
18442                         return;
18443                 }
18444                 break;
18445             
18446             default:
18447                 if (obj.disabled || obj.region == '#disabled') {
18448                         return;
18449                 }
18450                 break;
18451         }
18452                 
18453         this.modules.push(obj);
18454          
18455     },
18456     /**
18457      * convert a string to an object..
18458      * eg. 'AAA.BBB' -> finds AAA.BBB
18459
18460      */
18461     
18462     toObject : function(str)
18463     {
18464         if (!str || typeof(str) == 'object') {
18465             return str;
18466         }
18467         if (str.substring(0,1) == '#') {
18468             return str;
18469         }
18470
18471         var ar = str.split('.');
18472         var rt, o;
18473         rt = ar.shift();
18474             /** eval:var:o */
18475         try {
18476             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
18477         } catch (e) {
18478             throw "Module not found : " + str;
18479         }
18480         
18481         if (o === false) {
18482             throw "Module not found : " + str;
18483         }
18484         Roo.each(ar, function(e) {
18485             if (typeof(o[e]) == 'undefined') {
18486                 throw "Module not found : " + str;
18487             }
18488             o = o[e];
18489         });
18490         
18491         return o;
18492         
18493     },
18494     
18495     
18496     /**
18497      * move modules into their correct place in the tree..
18498      * 
18499      */
18500     preBuild : function ()
18501     {
18502         var _t = this;
18503         Roo.each(this.modules , function (obj)
18504         {
18505             Roo.XComponent.event.fireEvent('beforebuild', obj);
18506             
18507             var opar = obj.parent;
18508             try { 
18509                 obj.parent = this.toObject(opar);
18510             } catch(e) {
18511                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
18512                 return;
18513             }
18514             
18515             if (!obj.parent) {
18516                 Roo.debug && Roo.log("GOT top level module");
18517                 Roo.debug && Roo.log(obj);
18518                 obj.modules = new Roo.util.MixedCollection(false, 
18519                     function(o) { return o.order + '' }
18520                 );
18521                 this.topModule = obj;
18522                 return;
18523             }
18524                         // parent is a string (usually a dom element name..)
18525             if (typeof(obj.parent) == 'string') {
18526                 this.elmodules.push(obj);
18527                 return;
18528             }
18529             if (obj.parent.constructor != Roo.XComponent) {
18530                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
18531             }
18532             if (!obj.parent.modules) {
18533                 obj.parent.modules = new Roo.util.MixedCollection(false, 
18534                     function(o) { return o.order + '' }
18535                 );
18536             }
18537             if (obj.parent.disabled) {
18538                 obj.disabled = true;
18539             }
18540             obj.parent.modules.add(obj);
18541         }, this);
18542     },
18543     
18544      /**
18545      * make a list of modules to build.
18546      * @return {Array} list of modules. 
18547      */ 
18548     
18549     buildOrder : function()
18550     {
18551         var _this = this;
18552         var cmp = function(a,b) {   
18553             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
18554         };
18555         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
18556             throw "No top level modules to build";
18557         }
18558         
18559         // make a flat list in order of modules to build.
18560         var mods = this.topModule ? [ this.topModule ] : [];
18561                 
18562         
18563         // elmodules (is a list of DOM based modules )
18564         Roo.each(this.elmodules, function(e) {
18565             mods.push(e);
18566             if (!this.topModule &&
18567                 typeof(e.parent) == 'string' &&
18568                 e.parent.substring(0,1) == '#' &&
18569                 Roo.get(e.parent.substr(1))
18570                ) {
18571                 
18572                 _this.topModule = e;
18573             }
18574             
18575         });
18576
18577         
18578         // add modules to their parents..
18579         var addMod = function(m) {
18580             Roo.debug && Roo.log("build Order: add: " + m.name);
18581                 
18582             mods.push(m);
18583             if (m.modules && !m.disabled) {
18584                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
18585                 m.modules.keySort('ASC',  cmp );
18586                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
18587     
18588                 m.modules.each(addMod);
18589             } else {
18590                 Roo.debug && Roo.log("build Order: no child modules");
18591             }
18592             // not sure if this is used any more..
18593             if (m.finalize) {
18594                 m.finalize.name = m.name + " (clean up) ";
18595                 mods.push(m.finalize);
18596             }
18597             
18598         }
18599         if (this.topModule && this.topModule.modules) { 
18600             this.topModule.modules.keySort('ASC',  cmp );
18601             this.topModule.modules.each(addMod);
18602         } 
18603         return mods;
18604     },
18605     
18606      /**
18607      * Build the registered modules.
18608      * @param {Object} parent element.
18609      * @param {Function} optional method to call after module has been added.
18610      * 
18611      */ 
18612    
18613     build : function(opts) 
18614     {
18615         
18616         if (typeof(opts) != 'undefined') {
18617             Roo.apply(this,opts);
18618         }
18619         
18620         this.preBuild();
18621         var mods = this.buildOrder();
18622       
18623         //this.allmods = mods;
18624         //Roo.debug && Roo.log(mods);
18625         //return;
18626         if (!mods.length) { // should not happen
18627             throw "NO modules!!!";
18628         }
18629         
18630         
18631         var msg = "Building Interface...";
18632         // flash it up as modal - so we store the mask!?
18633         if (!this.hideProgress && Roo.MessageBox) {
18634             Roo.MessageBox.show({ title: 'loading' });
18635             Roo.MessageBox.show({
18636                title: "Please wait...",
18637                msg: msg,
18638                width:450,
18639                progress:true,
18640                buttons : false,
18641                closable:false,
18642                modal: false
18643               
18644             });
18645         }
18646         var total = mods.length;
18647         
18648         var _this = this;
18649         var progressRun = function() {
18650             if (!mods.length) {
18651                 Roo.debug && Roo.log('hide?');
18652                 if (!this.hideProgress && Roo.MessageBox) {
18653                     Roo.MessageBox.hide();
18654                 }
18655                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
18656                 
18657                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
18658                 
18659                 // THE END...
18660                 return false;   
18661             }
18662             
18663             var m = mods.shift();
18664             
18665             
18666             Roo.debug && Roo.log(m);
18667             // not sure if this is supported any more.. - modules that are are just function
18668             if (typeof(m) == 'function') { 
18669                 m.call(this);
18670                 return progressRun.defer(10, _this);
18671             } 
18672             
18673             
18674             msg = "Building Interface " + (total  - mods.length) + 
18675                     " of " + total + 
18676                     (m.name ? (' - ' + m.name) : '');
18677                         Roo.debug && Roo.log(msg);
18678             if (!_this.hideProgress &&  Roo.MessageBox) { 
18679                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
18680             }
18681             
18682          
18683             // is the module disabled?
18684             var disabled = (typeof(m.disabled) == 'function') ?
18685                 m.disabled.call(m.module.disabled) : m.disabled;    
18686             
18687             
18688             if (disabled) {
18689                 return progressRun(); // we do not update the display!
18690             }
18691             
18692             // now build 
18693             
18694                         
18695                         
18696             m.render();
18697             // it's 10 on top level, and 1 on others??? why...
18698             return progressRun.defer(10, _this);
18699              
18700         }
18701         progressRun.defer(1, _this);
18702      
18703         
18704         
18705     },
18706     /**
18707      * Overlay a set of modified strings onto a component
18708      * This is dependant on our builder exporting the strings and 'named strings' elements.
18709      * 
18710      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
18711      * @param {Object} associative array of 'named' string and it's new value.
18712      * 
18713      */
18714         overlayStrings : function( component, strings )
18715     {
18716         if (typeof(component['_named_strings']) == 'undefined') {
18717             throw "ERROR: component does not have _named_strings";
18718         }
18719         for ( var k in strings ) {
18720             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
18721             if (md !== false) {
18722                 component['_strings'][md] = strings[k];
18723             } else {
18724                 Roo.log('could not find named string: ' + k + ' in');
18725                 Roo.log(component);
18726             }
18727             
18728         }
18729         
18730     },
18731     
18732         
18733         /**
18734          * Event Object.
18735          *
18736          *
18737          */
18738         event: false, 
18739     /**
18740          * wrapper for event.on - aliased later..  
18741          * Typically use to register a event handler for register:
18742          *
18743          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
18744          *
18745          */
18746     on : false
18747    
18748     
18749     
18750 });
18751
18752 Roo.XComponent.event = new Roo.util.Observable({
18753                 events : { 
18754                         /**
18755                          * @event register
18756                          * Fires when an Component is registered,
18757                          * set the disable property on the Component to stop registration.
18758                          * @param {Roo.XComponent} c the component being registerd.
18759                          * 
18760                          */
18761                         'register' : true,
18762             /**
18763                          * @event beforebuild
18764                          * Fires before each Component is built
18765                          * can be used to apply permissions.
18766                          * @param {Roo.XComponent} c the component being registerd.
18767                          * 
18768                          */
18769                         'beforebuild' : true,
18770                         /**
18771                          * @event buildcomplete
18772                          * Fires on the top level element when all elements have been built
18773                          * @param {Roo.XComponent} the top level component.
18774                          */
18775                         'buildcomplete' : true
18776                         
18777                 }
18778 });
18779
18780 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
18781  //
18782  /**
18783  * marked - a markdown parser
18784  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
18785  * https://github.com/chjj/marked
18786  */
18787
18788
18789 /**
18790  *
18791  * Roo.Markdown - is a very crude wrapper around marked..
18792  *
18793  * usage:
18794  * 
18795  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
18796  * 
18797  * Note: move the sample code to the bottom of this
18798  * file before uncommenting it.
18799  *
18800  */
18801
18802 Roo.Markdown = {};
18803 Roo.Markdown.toHtml = function(text) {
18804     
18805     var c = new Roo.Markdown.marked.setOptions({
18806             renderer: new Roo.Markdown.marked.Renderer(),
18807             gfm: true,
18808             tables: true,
18809             breaks: false,
18810             pedantic: false,
18811             sanitize: false,
18812             smartLists: true,
18813             smartypants: false
18814           });
18815     // A FEW HACKS!!?
18816     
18817     text = text.replace(/\\\n/g,' ');
18818     return Roo.Markdown.marked(text);
18819 };
18820 //
18821 // converter
18822 //
18823 // Wraps all "globals" so that the only thing
18824 // exposed is makeHtml().
18825 //
18826 (function() {
18827     
18828      /**
18829          * eval:var:escape
18830          * eval:var:unescape
18831          * eval:var:replace
18832          */
18833       
18834     /**
18835      * Helpers
18836      */
18837     
18838     var escape = function (html, encode) {
18839       return html
18840         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
18841         .replace(/</g, '&lt;')
18842         .replace(/>/g, '&gt;')
18843         .replace(/"/g, '&quot;')
18844         .replace(/'/g, '&#39;');
18845     }
18846     
18847     var unescape = function (html) {
18848         // explicitly match decimal, hex, and named HTML entities 
18849       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
18850         n = n.toLowerCase();
18851         if (n === 'colon') { return ':'; }
18852         if (n.charAt(0) === '#') {
18853           return n.charAt(1) === 'x'
18854             ? String.fromCharCode(parseInt(n.substring(2), 16))
18855             : String.fromCharCode(+n.substring(1));
18856         }
18857         return '';
18858       });
18859     }
18860     
18861     var replace = function (regex, opt) {
18862       regex = regex.source;
18863       opt = opt || '';
18864       return function self(name, val) {
18865         if (!name) { return new RegExp(regex, opt); }
18866         val = val.source || val;
18867         val = val.replace(/(^|[^\[])\^/g, '$1');
18868         regex = regex.replace(name, val);
18869         return self;
18870       };
18871     }
18872
18873
18874          /**
18875          * eval:var:noop
18876     */
18877     var noop = function () {}
18878     noop.exec = noop;
18879     
18880          /**
18881          * eval:var:merge
18882     */
18883     var merge = function (obj) {
18884       var i = 1
18885         , target
18886         , key;
18887     
18888       for (; i < arguments.length; i++) {
18889         target = arguments[i];
18890         for (key in target) {
18891           if (Object.prototype.hasOwnProperty.call(target, key)) {
18892             obj[key] = target[key];
18893           }
18894         }
18895       }
18896     
18897       return obj;
18898     }
18899     
18900     
18901     /**
18902      * Block-Level Grammar
18903      */
18904     
18905     
18906     
18907     
18908     var block = {
18909       newline: /^\n+/,
18910       code: /^( {4}[^\n]+\n*)+/,
18911       fences: noop,
18912       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
18913       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
18914       nptable: noop,
18915       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
18916       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
18917       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
18918       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
18919       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
18920       table: noop,
18921       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
18922       text: /^[^\n]+/
18923     };
18924     
18925     block.bullet = /(?:[*+-]|\d+\.)/;
18926     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
18927     block.item = replace(block.item, 'gm')
18928       (/bull/g, block.bullet)
18929       ();
18930     
18931     block.list = replace(block.list)
18932       (/bull/g, block.bullet)
18933       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
18934       ('def', '\\n+(?=' + block.def.source + ')')
18935       ();
18936     
18937     block.blockquote = replace(block.blockquote)
18938       ('def', block.def)
18939       ();
18940     
18941     block._tag = '(?!(?:'
18942       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
18943       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
18944       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
18945     
18946     block.html = replace(block.html)
18947       ('comment', /<!--[\s\S]*?-->/)
18948       ('closed', /<(tag)[\s\S]+?<\/\1>/)
18949       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
18950       (/tag/g, block._tag)
18951       ();
18952     
18953     block.paragraph = replace(block.paragraph)
18954       ('hr', block.hr)
18955       ('heading', block.heading)
18956       ('lheading', block.lheading)
18957       ('blockquote', block.blockquote)
18958       ('tag', '<' + block._tag)
18959       ('def', block.def)
18960       ();
18961     
18962     /**
18963      * Normal Block Grammar
18964      */
18965     
18966     block.normal = merge({}, block);
18967     
18968     /**
18969      * GFM Block Grammar
18970      */
18971     
18972     block.gfm = merge({}, block.normal, {
18973       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
18974       paragraph: /^/,
18975       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
18976     });
18977     
18978     block.gfm.paragraph = replace(block.paragraph)
18979       ('(?!', '(?!'
18980         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
18981         + block.list.source.replace('\\1', '\\3') + '|')
18982       ();
18983     
18984     /**
18985      * GFM + Tables Block Grammar
18986      */
18987     
18988     block.tables = merge({}, block.gfm, {
18989       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
18990       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
18991     });
18992     
18993     /**
18994      * Block Lexer
18995      */
18996     
18997     var Lexer = function (options) {
18998       this.tokens = [];
18999       this.tokens.links = {};
19000       this.options = options || marked.defaults;
19001       this.rules = block.normal;
19002     
19003       if (this.options.gfm) {
19004         if (this.options.tables) {
19005           this.rules = block.tables;
19006         } else {
19007           this.rules = block.gfm;
19008         }
19009       }
19010     }
19011     
19012     /**
19013      * Expose Block Rules
19014      */
19015     
19016     Lexer.rules = block;
19017     
19018     /**
19019      * Static Lex Method
19020      */
19021     
19022     Lexer.lex = function(src, options) {
19023       var lexer = new Lexer(options);
19024       return lexer.lex(src);
19025     };
19026     
19027     /**
19028      * Preprocessing
19029      */
19030     
19031     Lexer.prototype.lex = function(src) {
19032       src = src
19033         .replace(/\r\n|\r/g, '\n')
19034         .replace(/\t/g, '    ')
19035         .replace(/\u00a0/g, ' ')
19036         .replace(/\u2424/g, '\n');
19037     
19038       return this.token(src, true);
19039     };
19040     
19041     /**
19042      * Lexing
19043      */
19044     
19045     Lexer.prototype.token = function(src, top, bq) {
19046       var src = src.replace(/^ +$/gm, '')
19047         , next
19048         , loose
19049         , cap
19050         , bull
19051         , b
19052         , item
19053         , space
19054         , i
19055         , l;
19056     
19057       while (src) {
19058         // newline
19059         if (cap = this.rules.newline.exec(src)) {
19060           src = src.substring(cap[0].length);
19061           if (cap[0].length > 1) {
19062             this.tokens.push({
19063               type: 'space'
19064             });
19065           }
19066         }
19067     
19068         // code
19069         if (cap = this.rules.code.exec(src)) {
19070           src = src.substring(cap[0].length);
19071           cap = cap[0].replace(/^ {4}/gm, '');
19072           this.tokens.push({
19073             type: 'code',
19074             text: !this.options.pedantic
19075               ? cap.replace(/\n+$/, '')
19076               : cap
19077           });
19078           continue;
19079         }
19080     
19081         // fences (gfm)
19082         if (cap = this.rules.fences.exec(src)) {
19083           src = src.substring(cap[0].length);
19084           this.tokens.push({
19085             type: 'code',
19086             lang: cap[2],
19087             text: cap[3] || ''
19088           });
19089           continue;
19090         }
19091     
19092         // heading
19093         if (cap = this.rules.heading.exec(src)) {
19094           src = src.substring(cap[0].length);
19095           this.tokens.push({
19096             type: 'heading',
19097             depth: cap[1].length,
19098             text: cap[2]
19099           });
19100           continue;
19101         }
19102     
19103         // table no leading pipe (gfm)
19104         if (top && (cap = this.rules.nptable.exec(src))) {
19105           src = src.substring(cap[0].length);
19106     
19107           item = {
19108             type: 'table',
19109             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
19110             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
19111             cells: cap[3].replace(/\n$/, '').split('\n')
19112           };
19113     
19114           for (i = 0; i < item.align.length; i++) {
19115             if (/^ *-+: *$/.test(item.align[i])) {
19116               item.align[i] = 'right';
19117             } else if (/^ *:-+: *$/.test(item.align[i])) {
19118               item.align[i] = 'center';
19119             } else if (/^ *:-+ *$/.test(item.align[i])) {
19120               item.align[i] = 'left';
19121             } else {
19122               item.align[i] = null;
19123             }
19124           }
19125     
19126           for (i = 0; i < item.cells.length; i++) {
19127             item.cells[i] = item.cells[i].split(/ *\| */);
19128           }
19129     
19130           this.tokens.push(item);
19131     
19132           continue;
19133         }
19134     
19135         // lheading
19136         if (cap = this.rules.lheading.exec(src)) {
19137           src = src.substring(cap[0].length);
19138           this.tokens.push({
19139             type: 'heading',
19140             depth: cap[2] === '=' ? 1 : 2,
19141             text: cap[1]
19142           });
19143           continue;
19144         }
19145     
19146         // hr
19147         if (cap = this.rules.hr.exec(src)) {
19148           src = src.substring(cap[0].length);
19149           this.tokens.push({
19150             type: 'hr'
19151           });
19152           continue;
19153         }
19154     
19155         // blockquote
19156         if (cap = this.rules.blockquote.exec(src)) {
19157           src = src.substring(cap[0].length);
19158     
19159           this.tokens.push({
19160             type: 'blockquote_start'
19161           });
19162     
19163           cap = cap[0].replace(/^ *> ?/gm, '');
19164     
19165           // Pass `top` to keep the current
19166           // "toplevel" state. This is exactly
19167           // how markdown.pl works.
19168           this.token(cap, top, true);
19169     
19170           this.tokens.push({
19171             type: 'blockquote_end'
19172           });
19173     
19174           continue;
19175         }
19176     
19177         // list
19178         if (cap = this.rules.list.exec(src)) {
19179           src = src.substring(cap[0].length);
19180           bull = cap[2];
19181     
19182           this.tokens.push({
19183             type: 'list_start',
19184             ordered: bull.length > 1
19185           });
19186     
19187           // Get each top-level item.
19188           cap = cap[0].match(this.rules.item);
19189     
19190           next = false;
19191           l = cap.length;
19192           i = 0;
19193     
19194           for (; i < l; i++) {
19195             item = cap[i];
19196     
19197             // Remove the list item's bullet
19198             // so it is seen as the next token.
19199             space = item.length;
19200             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
19201     
19202             // Outdent whatever the
19203             // list item contains. Hacky.
19204             if (~item.indexOf('\n ')) {
19205               space -= item.length;
19206               item = !this.options.pedantic
19207                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
19208                 : item.replace(/^ {1,4}/gm, '');
19209             }
19210     
19211             // Determine whether the next list item belongs here.
19212             // Backpedal if it does not belong in this list.
19213             if (this.options.smartLists && i !== l - 1) {
19214               b = block.bullet.exec(cap[i + 1])[0];
19215               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
19216                 src = cap.slice(i + 1).join('\n') + src;
19217                 i = l - 1;
19218               }
19219             }
19220     
19221             // Determine whether item is loose or not.
19222             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
19223             // for discount behavior.
19224             loose = next || /\n\n(?!\s*$)/.test(item);
19225             if (i !== l - 1) {
19226               next = item.charAt(item.length - 1) === '\n';
19227               if (!loose) { loose = next; }
19228             }
19229     
19230             this.tokens.push({
19231               type: loose
19232                 ? 'loose_item_start'
19233                 : 'list_item_start'
19234             });
19235     
19236             // Recurse.
19237             this.token(item, false, bq);
19238     
19239             this.tokens.push({
19240               type: 'list_item_end'
19241             });
19242           }
19243     
19244           this.tokens.push({
19245             type: 'list_end'
19246           });
19247     
19248           continue;
19249         }
19250     
19251         // html
19252         if (cap = this.rules.html.exec(src)) {
19253           src = src.substring(cap[0].length);
19254           this.tokens.push({
19255             type: this.options.sanitize
19256               ? 'paragraph'
19257               : 'html',
19258             pre: !this.options.sanitizer
19259               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
19260             text: cap[0]
19261           });
19262           continue;
19263         }
19264     
19265         // def
19266         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
19267           src = src.substring(cap[0].length);
19268           this.tokens.links[cap[1].toLowerCase()] = {
19269             href: cap[2],
19270             title: cap[3]
19271           };
19272           continue;
19273         }
19274     
19275         // table (gfm)
19276         if (top && (cap = this.rules.table.exec(src))) {
19277           src = src.substring(cap[0].length);
19278     
19279           item = {
19280             type: 'table',
19281             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
19282             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
19283             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
19284           };
19285     
19286           for (i = 0; i < item.align.length; i++) {
19287             if (/^ *-+: *$/.test(item.align[i])) {
19288               item.align[i] = 'right';
19289             } else if (/^ *:-+: *$/.test(item.align[i])) {
19290               item.align[i] = 'center';
19291             } else if (/^ *:-+ *$/.test(item.align[i])) {
19292               item.align[i] = 'left';
19293             } else {
19294               item.align[i] = null;
19295             }
19296           }
19297     
19298           for (i = 0; i < item.cells.length; i++) {
19299             item.cells[i] = item.cells[i]
19300               .replace(/^ *\| *| *\| *$/g, '')
19301               .split(/ *\| */);
19302           }
19303     
19304           this.tokens.push(item);
19305     
19306           continue;
19307         }
19308     
19309         // top-level paragraph
19310         if (top && (cap = this.rules.paragraph.exec(src))) {
19311           src = src.substring(cap[0].length);
19312           this.tokens.push({
19313             type: 'paragraph',
19314             text: cap[1].charAt(cap[1].length - 1) === '\n'
19315               ? cap[1].slice(0, -1)
19316               : cap[1]
19317           });
19318           continue;
19319         }
19320     
19321         // text
19322         if (cap = this.rules.text.exec(src)) {
19323           // Top-level should never reach here.
19324           src = src.substring(cap[0].length);
19325           this.tokens.push({
19326             type: 'text',
19327             text: cap[0]
19328           });
19329           continue;
19330         }
19331     
19332         if (src) {
19333           throw new
19334             Error('Infinite loop on byte: ' + src.charCodeAt(0));
19335         }
19336       }
19337     
19338       return this.tokens;
19339     };
19340     
19341     /**
19342      * Inline-Level Grammar
19343      */
19344     
19345     var inline = {
19346       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
19347       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
19348       url: noop,
19349       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
19350       link: /^!?\[(inside)\]\(href\)/,
19351       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
19352       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
19353       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
19354       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
19355       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
19356       br: /^ {2,}\n(?!\s*$)/,
19357       del: noop,
19358       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
19359     };
19360     
19361     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
19362     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
19363     
19364     inline.link = replace(inline.link)
19365       ('inside', inline._inside)
19366       ('href', inline._href)
19367       ();
19368     
19369     inline.reflink = replace(inline.reflink)
19370       ('inside', inline._inside)
19371       ();
19372     
19373     /**
19374      * Normal Inline Grammar
19375      */
19376     
19377     inline.normal = merge({}, inline);
19378     
19379     /**
19380      * Pedantic Inline Grammar
19381      */
19382     
19383     inline.pedantic = merge({}, inline.normal, {
19384       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
19385       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
19386     });
19387     
19388     /**
19389      * GFM Inline Grammar
19390      */
19391     
19392     inline.gfm = merge({}, inline.normal, {
19393       escape: replace(inline.escape)('])', '~|])')(),
19394       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
19395       del: /^~~(?=\S)([\s\S]*?\S)~~/,
19396       text: replace(inline.text)
19397         (']|', '~]|')
19398         ('|', '|https?://|')
19399         ()
19400     });
19401     
19402     /**
19403      * GFM + Line Breaks Inline Grammar
19404      */
19405     
19406     inline.breaks = merge({}, inline.gfm, {
19407       br: replace(inline.br)('{2,}', '*')(),
19408       text: replace(inline.gfm.text)('{2,}', '*')()
19409     });
19410     
19411     /**
19412      * Inline Lexer & Compiler
19413      */
19414     
19415     var InlineLexer  = function (links, options) {
19416       this.options = options || marked.defaults;
19417       this.links = links;
19418       this.rules = inline.normal;
19419       this.renderer = this.options.renderer || new Renderer;
19420       this.renderer.options = this.options;
19421     
19422       if (!this.links) {
19423         throw new
19424           Error('Tokens array requires a `links` property.');
19425       }
19426     
19427       if (this.options.gfm) {
19428         if (this.options.breaks) {
19429           this.rules = inline.breaks;
19430         } else {
19431           this.rules = inline.gfm;
19432         }
19433       } else if (this.options.pedantic) {
19434         this.rules = inline.pedantic;
19435       }
19436     }
19437     
19438     /**
19439      * Expose Inline Rules
19440      */
19441     
19442     InlineLexer.rules = inline;
19443     
19444     /**
19445      * Static Lexing/Compiling Method
19446      */
19447     
19448     InlineLexer.output = function(src, links, options) {
19449       var inline = new InlineLexer(links, options);
19450       return inline.output(src);
19451     };
19452     
19453     /**
19454      * Lexing/Compiling
19455      */
19456     
19457     InlineLexer.prototype.output = function(src) {
19458       var out = ''
19459         , link
19460         , text
19461         , href
19462         , cap;
19463     
19464       while (src) {
19465         // escape
19466         if (cap = this.rules.escape.exec(src)) {
19467           src = src.substring(cap[0].length);
19468           out += cap[1];
19469           continue;
19470         }
19471     
19472         // autolink
19473         if (cap = this.rules.autolink.exec(src)) {
19474           src = src.substring(cap[0].length);
19475           if (cap[2] === '@') {
19476             text = cap[1].charAt(6) === ':'
19477               ? this.mangle(cap[1].substring(7))
19478               : this.mangle(cap[1]);
19479             href = this.mangle('mailto:') + text;
19480           } else {
19481             text = escape(cap[1]);
19482             href = text;
19483           }
19484           out += this.renderer.link(href, null, text);
19485           continue;
19486         }
19487     
19488         // url (gfm)
19489         if (!this.inLink && (cap = this.rules.url.exec(src))) {
19490           src = src.substring(cap[0].length);
19491           text = escape(cap[1]);
19492           href = text;
19493           out += this.renderer.link(href, null, text);
19494           continue;
19495         }
19496     
19497         // tag
19498         if (cap = this.rules.tag.exec(src)) {
19499           if (!this.inLink && /^<a /i.test(cap[0])) {
19500             this.inLink = true;
19501           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
19502             this.inLink = false;
19503           }
19504           src = src.substring(cap[0].length);
19505           out += this.options.sanitize
19506             ? this.options.sanitizer
19507               ? this.options.sanitizer(cap[0])
19508               : escape(cap[0])
19509             : cap[0];
19510           continue;
19511         }
19512     
19513         // link
19514         if (cap = this.rules.link.exec(src)) {
19515           src = src.substring(cap[0].length);
19516           this.inLink = true;
19517           out += this.outputLink(cap, {
19518             href: cap[2],
19519             title: cap[3]
19520           });
19521           this.inLink = false;
19522           continue;
19523         }
19524     
19525         // reflink, nolink
19526         if ((cap = this.rules.reflink.exec(src))
19527             || (cap = this.rules.nolink.exec(src))) {
19528           src = src.substring(cap[0].length);
19529           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
19530           link = this.links[link.toLowerCase()];
19531           if (!link || !link.href) {
19532             out += cap[0].charAt(0);
19533             src = cap[0].substring(1) + src;
19534             continue;
19535           }
19536           this.inLink = true;
19537           out += this.outputLink(cap, link);
19538           this.inLink = false;
19539           continue;
19540         }
19541     
19542         // strong
19543         if (cap = this.rules.strong.exec(src)) {
19544           src = src.substring(cap[0].length);
19545           out += this.renderer.strong(this.output(cap[2] || cap[1]));
19546           continue;
19547         }
19548     
19549         // em
19550         if (cap = this.rules.em.exec(src)) {
19551           src = src.substring(cap[0].length);
19552           out += this.renderer.em(this.output(cap[2] || cap[1]));
19553           continue;
19554         }
19555     
19556         // code
19557         if (cap = this.rules.code.exec(src)) {
19558           src = src.substring(cap[0].length);
19559           out += this.renderer.codespan(escape(cap[2], true));
19560           continue;
19561         }
19562     
19563         // br
19564         if (cap = this.rules.br.exec(src)) {
19565           src = src.substring(cap[0].length);
19566           out += this.renderer.br();
19567           continue;
19568         }
19569     
19570         // del (gfm)
19571         if (cap = this.rules.del.exec(src)) {
19572           src = src.substring(cap[0].length);
19573           out += this.renderer.del(this.output(cap[1]));
19574           continue;
19575         }
19576     
19577         // text
19578         if (cap = this.rules.text.exec(src)) {
19579           src = src.substring(cap[0].length);
19580           out += this.renderer.text(escape(this.smartypants(cap[0])));
19581           continue;
19582         }
19583     
19584         if (src) {
19585           throw new
19586             Error('Infinite loop on byte: ' + src.charCodeAt(0));
19587         }
19588       }
19589     
19590       return out;
19591     };
19592     
19593     /**
19594      * Compile Link
19595      */
19596     
19597     InlineLexer.prototype.outputLink = function(cap, link) {
19598       var href = escape(link.href)
19599         , title = link.title ? escape(link.title) : null;
19600     
19601       return cap[0].charAt(0) !== '!'
19602         ? this.renderer.link(href, title, this.output(cap[1]))
19603         : this.renderer.image(href, title, escape(cap[1]));
19604     };
19605     
19606     /**
19607      * Smartypants Transformations
19608      */
19609     
19610     InlineLexer.prototype.smartypants = function(text) {
19611       if (!this.options.smartypants)  { return text; }
19612       return text
19613         // em-dashes
19614         .replace(/---/g, '\u2014')
19615         // en-dashes
19616         .replace(/--/g, '\u2013')
19617         // opening singles
19618         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
19619         // closing singles & apostrophes
19620         .replace(/'/g, '\u2019')
19621         // opening doubles
19622         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
19623         // closing doubles
19624         .replace(/"/g, '\u201d')
19625         // ellipses
19626         .replace(/\.{3}/g, '\u2026');
19627     };
19628     
19629     /**
19630      * Mangle Links
19631      */
19632     
19633     InlineLexer.prototype.mangle = function(text) {
19634       if (!this.options.mangle) { return text; }
19635       var out = ''
19636         , l = text.length
19637         , i = 0
19638         , ch;
19639     
19640       for (; i < l; i++) {
19641         ch = text.charCodeAt(i);
19642         if (Math.random() > 0.5) {
19643           ch = 'x' + ch.toString(16);
19644         }
19645         out += '&#' + ch + ';';
19646       }
19647     
19648       return out;
19649     };
19650     
19651     /**
19652      * Renderer
19653      */
19654     
19655      /**
19656          * eval:var:Renderer
19657     */
19658     
19659     var Renderer   = function (options) {
19660       this.options = options || {};
19661     }
19662     
19663     Renderer.prototype.code = function(code, lang, escaped) {
19664       if (this.options.highlight) {
19665         var out = this.options.highlight(code, lang);
19666         if (out != null && out !== code) {
19667           escaped = true;
19668           code = out;
19669         }
19670       } else {
19671             // hack!!! - it's already escapeD?
19672             escaped = true;
19673       }
19674     
19675       if (!lang) {
19676         return '<pre><code>'
19677           + (escaped ? code : escape(code, true))
19678           + '\n</code></pre>';
19679       }
19680     
19681       return '<pre><code class="'
19682         + this.options.langPrefix
19683         + escape(lang, true)
19684         + '">'
19685         + (escaped ? code : escape(code, true))
19686         + '\n</code></pre>\n';
19687     };
19688     
19689     Renderer.prototype.blockquote = function(quote) {
19690       return '<blockquote>\n' + quote + '</blockquote>\n';
19691     };
19692     
19693     Renderer.prototype.html = function(html) {
19694       return html;
19695     };
19696     
19697     Renderer.prototype.heading = function(text, level, raw) {
19698       return '<h'
19699         + level
19700         + ' id="'
19701         + this.options.headerPrefix
19702         + raw.toLowerCase().replace(/[^\w]+/g, '-')
19703         + '">'
19704         + text
19705         + '</h'
19706         + level
19707         + '>\n';
19708     };
19709     
19710     Renderer.prototype.hr = function() {
19711       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
19712     };
19713     
19714     Renderer.prototype.list = function(body, ordered) {
19715       var type = ordered ? 'ol' : 'ul';
19716       return '<' + type + '>\n' + body + '</' + type + '>\n';
19717     };
19718     
19719     Renderer.prototype.listitem = function(text) {
19720       return '<li>' + text + '</li>\n';
19721     };
19722     
19723     Renderer.prototype.paragraph = function(text) {
19724       return '<p>' + text + '</p>\n';
19725     };
19726     
19727     Renderer.prototype.table = function(header, body) {
19728       return '<table class="table table-striped">\n'
19729         + '<thead>\n'
19730         + header
19731         + '</thead>\n'
19732         + '<tbody>\n'
19733         + body
19734         + '</tbody>\n'
19735         + '</table>\n';
19736     };
19737     
19738     Renderer.prototype.tablerow = function(content) {
19739       return '<tr>\n' + content + '</tr>\n';
19740     };
19741     
19742     Renderer.prototype.tablecell = function(content, flags) {
19743       var type = flags.header ? 'th' : 'td';
19744       var tag = flags.align
19745         ? '<' + type + ' style="text-align:' + flags.align + '">'
19746         : '<' + type + '>';
19747       return tag + content + '</' + type + '>\n';
19748     };
19749     
19750     // span level renderer
19751     Renderer.prototype.strong = function(text) {
19752       return '<strong>' + text + '</strong>';
19753     };
19754     
19755     Renderer.prototype.em = function(text) {
19756       return '<em>' + text + '</em>';
19757     };
19758     
19759     Renderer.prototype.codespan = function(text) {
19760       return '<code>' + text + '</code>';
19761     };
19762     
19763     Renderer.prototype.br = function() {
19764       return this.options.xhtml ? '<br/>' : '<br>';
19765     };
19766     
19767     Renderer.prototype.del = function(text) {
19768       return '<del>' + text + '</del>';
19769     };
19770     
19771     Renderer.prototype.link = function(href, title, text) {
19772       if (this.options.sanitize) {
19773         try {
19774           var prot = decodeURIComponent(unescape(href))
19775             .replace(/[^\w:]/g, '')
19776             .toLowerCase();
19777         } catch (e) {
19778           return '';
19779         }
19780         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
19781           return '';
19782         }
19783       }
19784       var out = '<a href="' + href + '"';
19785       if (title) {
19786         out += ' title="' + title + '"';
19787       }
19788       out += '>' + text + '</a>';
19789       return out;
19790     };
19791     
19792     Renderer.prototype.image = function(href, title, text) {
19793       var out = '<img src="' + href + '" alt="' + text + '"';
19794       if (title) {
19795         out += ' title="' + title + '"';
19796       }
19797       out += this.options.xhtml ? '/>' : '>';
19798       return out;
19799     };
19800     
19801     Renderer.prototype.text = function(text) {
19802       return text;
19803     };
19804     
19805     /**
19806      * Parsing & Compiling
19807      */
19808          /**
19809          * eval:var:Parser
19810     */
19811     
19812     var Parser= function (options) {
19813       this.tokens = [];
19814       this.token = null;
19815       this.options = options || marked.defaults;
19816       this.options.renderer = this.options.renderer || new Renderer;
19817       this.renderer = this.options.renderer;
19818       this.renderer.options = this.options;
19819     }
19820     
19821     /**
19822      * Static Parse Method
19823      */
19824     
19825     Parser.parse = function(src, options, renderer) {
19826       var parser = new Parser(options, renderer);
19827       return parser.parse(src);
19828     };
19829     
19830     /**
19831      * Parse Loop
19832      */
19833     
19834     Parser.prototype.parse = function(src) {
19835       this.inline = new InlineLexer(src.links, this.options, this.renderer);
19836       this.tokens = src.reverse();
19837     
19838       var out = '';
19839       while (this.next()) {
19840         out += this.tok();
19841       }
19842     
19843       return out;
19844     };
19845     
19846     /**
19847      * Next Token
19848      */
19849     
19850     Parser.prototype.next = function() {
19851       return this.token = this.tokens.pop();
19852     };
19853     
19854     /**
19855      * Preview Next Token
19856      */
19857     
19858     Parser.prototype.peek = function() {
19859       return this.tokens[this.tokens.length - 1] || 0;
19860     };
19861     
19862     /**
19863      * Parse Text Tokens
19864      */
19865     
19866     Parser.prototype.parseText = function() {
19867       var body = this.token.text;
19868     
19869       while (this.peek().type === 'text') {
19870         body += '\n' + this.next().text;
19871       }
19872     
19873       return this.inline.output(body);
19874     };
19875     
19876     /**
19877      * Parse Current Token
19878      */
19879     
19880     Parser.prototype.tok = function() {
19881       switch (this.token.type) {
19882         case 'space': {
19883           return '';
19884         }
19885         case 'hr': {
19886           return this.renderer.hr();
19887         }
19888         case 'heading': {
19889           return this.renderer.heading(
19890             this.inline.output(this.token.text),
19891             this.token.depth,
19892             this.token.text);
19893         }
19894         case 'code': {
19895           return this.renderer.code(this.token.text,
19896             this.token.lang,
19897             this.token.escaped);
19898         }
19899         case 'table': {
19900           var header = ''
19901             , body = ''
19902             , i
19903             , row
19904             , cell
19905             , flags
19906             , j;
19907     
19908           // header
19909           cell = '';
19910           for (i = 0; i < this.token.header.length; i++) {
19911             flags = { header: true, align: this.token.align[i] };
19912             cell += this.renderer.tablecell(
19913               this.inline.output(this.token.header[i]),
19914               { header: true, align: this.token.align[i] }
19915             );
19916           }
19917           header += this.renderer.tablerow(cell);
19918     
19919           for (i = 0; i < this.token.cells.length; i++) {
19920             row = this.token.cells[i];
19921     
19922             cell = '';
19923             for (j = 0; j < row.length; j++) {
19924               cell += this.renderer.tablecell(
19925                 this.inline.output(row[j]),
19926                 { header: false, align: this.token.align[j] }
19927               );
19928             }
19929     
19930             body += this.renderer.tablerow(cell);
19931           }
19932           return this.renderer.table(header, body);
19933         }
19934         case 'blockquote_start': {
19935           var body = '';
19936     
19937           while (this.next().type !== 'blockquote_end') {
19938             body += this.tok();
19939           }
19940     
19941           return this.renderer.blockquote(body);
19942         }
19943         case 'list_start': {
19944           var body = ''
19945             , ordered = this.token.ordered;
19946     
19947           while (this.next().type !== 'list_end') {
19948             body += this.tok();
19949           }
19950     
19951           return this.renderer.list(body, ordered);
19952         }
19953         case 'list_item_start': {
19954           var body = '';
19955     
19956           while (this.next().type !== 'list_item_end') {
19957             body += this.token.type === 'text'
19958               ? this.parseText()
19959               : this.tok();
19960           }
19961     
19962           return this.renderer.listitem(body);
19963         }
19964         case 'loose_item_start': {
19965           var body = '';
19966     
19967           while (this.next().type !== 'list_item_end') {
19968             body += this.tok();
19969           }
19970     
19971           return this.renderer.listitem(body);
19972         }
19973         case 'html': {
19974           var html = !this.token.pre && !this.options.pedantic
19975             ? this.inline.output(this.token.text)
19976             : this.token.text;
19977           return this.renderer.html(html);
19978         }
19979         case 'paragraph': {
19980           return this.renderer.paragraph(this.inline.output(this.token.text));
19981         }
19982         case 'text': {
19983           return this.renderer.paragraph(this.parseText());
19984         }
19985       }
19986     };
19987   
19988     
19989     /**
19990      * Marked
19991      */
19992          /**
19993          * eval:var:marked
19994     */
19995     var marked = function (src, opt, callback) {
19996       if (callback || typeof opt === 'function') {
19997         if (!callback) {
19998           callback = opt;
19999           opt = null;
20000         }
20001     
20002         opt = merge({}, marked.defaults, opt || {});
20003     
20004         var highlight = opt.highlight
20005           , tokens
20006           , pending
20007           , i = 0;
20008     
20009         try {
20010           tokens = Lexer.lex(src, opt)
20011         } catch (e) {
20012           return callback(e);
20013         }
20014     
20015         pending = tokens.length;
20016          /**
20017          * eval:var:done
20018     */
20019         var done = function(err) {
20020           if (err) {
20021             opt.highlight = highlight;
20022             return callback(err);
20023           }
20024     
20025           var out;
20026     
20027           try {
20028             out = Parser.parse(tokens, opt);
20029           } catch (e) {
20030             err = e;
20031           }
20032     
20033           opt.highlight = highlight;
20034     
20035           return err
20036             ? callback(err)
20037             : callback(null, out);
20038         };
20039     
20040         if (!highlight || highlight.length < 3) {
20041           return done();
20042         }
20043     
20044         delete opt.highlight;
20045     
20046         if (!pending) { return done(); }
20047     
20048         for (; i < tokens.length; i++) {
20049           (function(token) {
20050             if (token.type !== 'code') {
20051               return --pending || done();
20052             }
20053             return highlight(token.text, token.lang, function(err, code) {
20054               if (err) { return done(err); }
20055               if (code == null || code === token.text) {
20056                 return --pending || done();
20057               }
20058               token.text = code;
20059               token.escaped = true;
20060               --pending || done();
20061             });
20062           })(tokens[i]);
20063         }
20064     
20065         return;
20066       }
20067       try {
20068         if (opt) { opt = merge({}, marked.defaults, opt); }
20069         return Parser.parse(Lexer.lex(src, opt), opt);
20070       } catch (e) {
20071         e.message += '\nPlease report this to https://github.com/chjj/marked.';
20072         if ((opt || marked.defaults).silent) {
20073           return '<p>An error occured:</p><pre>'
20074             + escape(e.message + '', true)
20075             + '</pre>';
20076         }
20077         throw e;
20078       }
20079     }
20080     
20081     /**
20082      * Options
20083      */
20084     
20085     marked.options =
20086     marked.setOptions = function(opt) {
20087       merge(marked.defaults, opt);
20088       return marked;
20089     };
20090     
20091     marked.defaults = {
20092       gfm: true,
20093       tables: true,
20094       breaks: false,
20095       pedantic: false,
20096       sanitize: false,
20097       sanitizer: null,
20098       mangle: true,
20099       smartLists: false,
20100       silent: false,
20101       highlight: null,
20102       langPrefix: 'lang-',
20103       smartypants: false,
20104       headerPrefix: '',
20105       renderer: new Renderer,
20106       xhtml: false
20107     };
20108     
20109     /**
20110      * Expose
20111      */
20112     
20113     marked.Parser = Parser;
20114     marked.parser = Parser.parse;
20115     
20116     marked.Renderer = Renderer;
20117     
20118     marked.Lexer = Lexer;
20119     marked.lexer = Lexer.lex;
20120     
20121     marked.InlineLexer = InlineLexer;
20122     marked.inlineLexer = InlineLexer.output;
20123     
20124     marked.parse = marked;
20125     
20126     Roo.Markdown.marked = marked;
20127
20128 })();/*
20129  * Based on:
20130  * Ext JS Library 1.1.1
20131  * Copyright(c) 2006-2007, Ext JS, LLC.
20132  *
20133  * Originally Released Under LGPL - original licence link has changed is not relivant.
20134  *
20135  * Fork - LGPL
20136  * <script type="text/javascript">
20137  */
20138
20139
20140
20141 /*
20142  * These classes are derivatives of the similarly named classes in the YUI Library.
20143  * The original license:
20144  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
20145  * Code licensed under the BSD License:
20146  * http://developer.yahoo.net/yui/license.txt
20147  */
20148
20149 (function() {
20150
20151 var Event=Roo.EventManager;
20152 var Dom=Roo.lib.Dom;
20153
20154 /**
20155  * @class Roo.dd.DragDrop
20156  * @extends Roo.util.Observable
20157  * Defines the interface and base operation of items that that can be
20158  * dragged or can be drop targets.  It was designed to be extended, overriding
20159  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
20160  * Up to three html elements can be associated with a DragDrop instance:
20161  * <ul>
20162  * <li>linked element: the element that is passed into the constructor.
20163  * This is the element which defines the boundaries for interaction with
20164  * other DragDrop objects.</li>
20165  * <li>handle element(s): The drag operation only occurs if the element that
20166  * was clicked matches a handle element.  By default this is the linked
20167  * element, but there are times that you will want only a portion of the
20168  * linked element to initiate the drag operation, and the setHandleElId()
20169  * method provides a way to define this.</li>
20170  * <li>drag element: this represents the element that would be moved along
20171  * with the cursor during a drag operation.  By default, this is the linked
20172  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
20173  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
20174  * </li>
20175  * </ul>
20176  * This class should not be instantiated until the onload event to ensure that
20177  * the associated elements are available.
20178  * The following would define a DragDrop obj that would interact with any
20179  * other DragDrop obj in the "group1" group:
20180  * <pre>
20181  *  dd = new Roo.dd.DragDrop("div1", "group1");
20182  * </pre>
20183  * Since none of the event handlers have been implemented, nothing would
20184  * actually happen if you were to run the code above.  Normally you would
20185  * override this class or one of the default implementations, but you can
20186  * also override the methods you want on an instance of the class...
20187  * <pre>
20188  *  dd.onDragDrop = function(e, id) {
20189  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
20190  *  }
20191  * </pre>
20192  * @constructor
20193  * @param {String} id of the element that is linked to this instance
20194  * @param {String} sGroup the group of related DragDrop objects
20195  * @param {object} config an object containing configurable attributes
20196  *                Valid properties for DragDrop:
20197  *                    padding, isTarget, maintainOffset, primaryButtonOnly
20198  */
20199 Roo.dd.DragDrop = function(id, sGroup, config) {
20200     if (id) {
20201         this.init(id, sGroup, config);
20202     }
20203     
20204 };
20205
20206 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
20207
20208     /**
20209      * The id of the element associated with this object.  This is what we
20210      * refer to as the "linked element" because the size and position of
20211      * this element is used to determine when the drag and drop objects have
20212      * interacted.
20213      * @property id
20214      * @type String
20215      */
20216     id: null,
20217
20218     /**
20219      * Configuration attributes passed into the constructor
20220      * @property config
20221      * @type object
20222      */
20223     config: null,
20224
20225     /**
20226      * The id of the element that will be dragged.  By default this is same
20227      * as the linked element , but could be changed to another element. Ex:
20228      * Roo.dd.DDProxy
20229      * @property dragElId
20230      * @type String
20231      * @private
20232      */
20233     dragElId: null,
20234
20235     /**
20236      * the id of the element that initiates the drag operation.  By default
20237      * this is the linked element, but could be changed to be a child of this
20238      * element.  This lets us do things like only starting the drag when the
20239      * header element within the linked html element is clicked.
20240      * @property handleElId
20241      * @type String
20242      * @private
20243      */
20244     handleElId: null,
20245
20246     /**
20247      * An associative array of HTML tags that will be ignored if clicked.
20248      * @property invalidHandleTypes
20249      * @type {string: string}
20250      */
20251     invalidHandleTypes: null,
20252
20253     /**
20254      * An associative array of ids for elements that will be ignored if clicked
20255      * @property invalidHandleIds
20256      * @type {string: string}
20257      */
20258     invalidHandleIds: null,
20259
20260     /**
20261      * An indexted array of css class names for elements that will be ignored
20262      * if clicked.
20263      * @property invalidHandleClasses
20264      * @type string[]
20265      */
20266     invalidHandleClasses: null,
20267
20268     /**
20269      * The linked element's absolute X position at the time the drag was
20270      * started
20271      * @property startPageX
20272      * @type int
20273      * @private
20274      */
20275     startPageX: 0,
20276
20277     /**
20278      * The linked element's absolute X position at the time the drag was
20279      * started
20280      * @property startPageY
20281      * @type int
20282      * @private
20283      */
20284     startPageY: 0,
20285
20286     /**
20287      * The group defines a logical collection of DragDrop objects that are
20288      * related.  Instances only get events when interacting with other
20289      * DragDrop object in the same group.  This lets us define multiple
20290      * groups using a single DragDrop subclass if we want.
20291      * @property groups
20292      * @type {string: string}
20293      */
20294     groups: null,
20295
20296     /**
20297      * Individual drag/drop instances can be locked.  This will prevent
20298      * onmousedown start drag.
20299      * @property locked
20300      * @type boolean
20301      * @private
20302      */
20303     locked: false,
20304
20305     /**
20306      * Lock this instance
20307      * @method lock
20308      */
20309     lock: function() { this.locked = true; },
20310
20311     /**
20312      * Unlock this instace
20313      * @method unlock
20314      */
20315     unlock: function() { this.locked = false; },
20316
20317     /**
20318      * By default, all insances can be a drop target.  This can be disabled by
20319      * setting isTarget to false.
20320      * @method isTarget
20321      * @type boolean
20322      */
20323     isTarget: true,
20324
20325     /**
20326      * The padding configured for this drag and drop object for calculating
20327      * the drop zone intersection with this object.
20328      * @method padding
20329      * @type int[]
20330      */
20331     padding: null,
20332
20333     /**
20334      * Cached reference to the linked element
20335      * @property _domRef
20336      * @private
20337      */
20338     _domRef: null,
20339
20340     /**
20341      * Internal typeof flag
20342      * @property __ygDragDrop
20343      * @private
20344      */
20345     __ygDragDrop: true,
20346
20347     /**
20348      * Set to true when horizontal contraints are applied
20349      * @property constrainX
20350      * @type boolean
20351      * @private
20352      */
20353     constrainX: false,
20354
20355     /**
20356      * Set to true when vertical contraints are applied
20357      * @property constrainY
20358      * @type boolean
20359      * @private
20360      */
20361     constrainY: false,
20362
20363     /**
20364      * The left constraint
20365      * @property minX
20366      * @type int
20367      * @private
20368      */
20369     minX: 0,
20370
20371     /**
20372      * The right constraint
20373      * @property maxX
20374      * @type int
20375      * @private
20376      */
20377     maxX: 0,
20378
20379     /**
20380      * The up constraint
20381      * @property minY
20382      * @type int
20383      * @type int
20384      * @private
20385      */
20386     minY: 0,
20387
20388     /**
20389      * The down constraint
20390      * @property maxY
20391      * @type int
20392      * @private
20393      */
20394     maxY: 0,
20395
20396     /**
20397      * Maintain offsets when we resetconstraints.  Set to true when you want
20398      * the position of the element relative to its parent to stay the same
20399      * when the page changes
20400      *
20401      * @property maintainOffset
20402      * @type boolean
20403      */
20404     maintainOffset: false,
20405
20406     /**
20407      * Array of pixel locations the element will snap to if we specified a
20408      * horizontal graduation/interval.  This array is generated automatically
20409      * when you define a tick interval.
20410      * @property xTicks
20411      * @type int[]
20412      */
20413     xTicks: null,
20414
20415     /**
20416      * Array of pixel locations the element will snap to if we specified a
20417      * vertical graduation/interval.  This array is generated automatically
20418      * when you define a tick interval.
20419      * @property yTicks
20420      * @type int[]
20421      */
20422     yTicks: null,
20423
20424     /**
20425      * By default the drag and drop instance will only respond to the primary
20426      * button click (left button for a right-handed mouse).  Set to true to
20427      * allow drag and drop to start with any mouse click that is propogated
20428      * by the browser
20429      * @property primaryButtonOnly
20430      * @type boolean
20431      */
20432     primaryButtonOnly: true,
20433
20434     /**
20435      * The availabe property is false until the linked dom element is accessible.
20436      * @property available
20437      * @type boolean
20438      */
20439     available: false,
20440
20441     /**
20442      * By default, drags can only be initiated if the mousedown occurs in the
20443      * region the linked element is.  This is done in part to work around a
20444      * bug in some browsers that mis-report the mousedown if the previous
20445      * mouseup happened outside of the window.  This property is set to true
20446      * if outer handles are defined.
20447      *
20448      * @property hasOuterHandles
20449      * @type boolean
20450      * @default false
20451      */
20452     hasOuterHandles: false,
20453
20454     /**
20455      * Code that executes immediately before the startDrag event
20456      * @method b4StartDrag
20457      * @private
20458      */
20459     b4StartDrag: function(x, y) { },
20460
20461     /**
20462      * Abstract method called after a drag/drop object is clicked
20463      * and the drag or mousedown time thresholds have beeen met.
20464      * @method startDrag
20465      * @param {int} X click location
20466      * @param {int} Y click location
20467      */
20468     startDrag: function(x, y) { /* override this */ },
20469
20470     /**
20471      * Code that executes immediately before the onDrag event
20472      * @method b4Drag
20473      * @private
20474      */
20475     b4Drag: function(e) { },
20476
20477     /**
20478      * Abstract method called during the onMouseMove event while dragging an
20479      * object.
20480      * @method onDrag
20481      * @param {Event} e the mousemove event
20482      */
20483     onDrag: function(e) { /* override this */ },
20484
20485     /**
20486      * Abstract method called when this element fist begins hovering over
20487      * another DragDrop obj
20488      * @method onDragEnter
20489      * @param {Event} e the mousemove event
20490      * @param {String|DragDrop[]} id In POINT mode, the element
20491      * id this is hovering over.  In INTERSECT mode, an array of one or more
20492      * dragdrop items being hovered over.
20493      */
20494     onDragEnter: function(e, id) { /* override this */ },
20495
20496     /**
20497      * Code that executes immediately before the onDragOver event
20498      * @method b4DragOver
20499      * @private
20500      */
20501     b4DragOver: function(e) { },
20502
20503     /**
20504      * Abstract method called when this element is hovering over another
20505      * DragDrop obj
20506      * @method onDragOver
20507      * @param {Event} e the mousemove event
20508      * @param {String|DragDrop[]} id In POINT mode, the element
20509      * id this is hovering over.  In INTERSECT mode, an array of dd items
20510      * being hovered over.
20511      */
20512     onDragOver: function(e, id) { /* override this */ },
20513
20514     /**
20515      * Code that executes immediately before the onDragOut event
20516      * @method b4DragOut
20517      * @private
20518      */
20519     b4DragOut: function(e) { },
20520
20521     /**
20522      * Abstract method called when we are no longer hovering over an element
20523      * @method onDragOut
20524      * @param {Event} e the mousemove event
20525      * @param {String|DragDrop[]} id In POINT mode, the element
20526      * id this was hovering over.  In INTERSECT mode, an array of dd items
20527      * that the mouse is no longer over.
20528      */
20529     onDragOut: function(e, id) { /* override this */ },
20530
20531     /**
20532      * Code that executes immediately before the onDragDrop event
20533      * @method b4DragDrop
20534      * @private
20535      */
20536     b4DragDrop: function(e) { },
20537
20538     /**
20539      * Abstract method called when this item is dropped on another DragDrop
20540      * obj
20541      * @method onDragDrop
20542      * @param {Event} e the mouseup event
20543      * @param {String|DragDrop[]} id In POINT mode, the element
20544      * id this was dropped on.  In INTERSECT mode, an array of dd items this
20545      * was dropped on.
20546      */
20547     onDragDrop: function(e, id) { /* override this */ },
20548
20549     /**
20550      * Abstract method called when this item is dropped on an area with no
20551      * drop target
20552      * @method onInvalidDrop
20553      * @param {Event} e the mouseup event
20554      */
20555     onInvalidDrop: function(e) { /* override this */ },
20556
20557     /**
20558      * Code that executes immediately before the endDrag event
20559      * @method b4EndDrag
20560      * @private
20561      */
20562     b4EndDrag: function(e) { },
20563
20564     /**
20565      * Fired when we are done dragging the object
20566      * @method endDrag
20567      * @param {Event} e the mouseup event
20568      */
20569     endDrag: function(e) { /* override this */ },
20570
20571     /**
20572      * Code executed immediately before the onMouseDown event
20573      * @method b4MouseDown
20574      * @param {Event} e the mousedown event
20575      * @private
20576      */
20577     b4MouseDown: function(e) {  },
20578
20579     /**
20580      * Event handler that fires when a drag/drop obj gets a mousedown
20581      * @method onMouseDown
20582      * @param {Event} e the mousedown event
20583      */
20584     onMouseDown: function(e) { /* override this */ },
20585
20586     /**
20587      * Event handler that fires when a drag/drop obj gets a mouseup
20588      * @method onMouseUp
20589      * @param {Event} e the mouseup event
20590      */
20591     onMouseUp: function(e) { /* override this */ },
20592
20593     /**
20594      * Override the onAvailable method to do what is needed after the initial
20595      * position was determined.
20596      * @method onAvailable
20597      */
20598     onAvailable: function () {
20599     },
20600
20601     /*
20602      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
20603      * @type Object
20604      */
20605     defaultPadding : {left:0, right:0, top:0, bottom:0},
20606
20607     /*
20608      * Initializes the drag drop object's constraints to restrict movement to a certain element.
20609  *
20610  * Usage:
20611  <pre><code>
20612  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
20613                 { dragElId: "existingProxyDiv" });
20614  dd.startDrag = function(){
20615      this.constrainTo("parent-id");
20616  };
20617  </code></pre>
20618  * Or you can initalize it using the {@link Roo.Element} object:
20619  <pre><code>
20620  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
20621      startDrag : function(){
20622          this.constrainTo("parent-id");
20623      }
20624  });
20625  </code></pre>
20626      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
20627      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
20628      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
20629      * an object containing the sides to pad. For example: {right:10, bottom:10}
20630      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
20631      */
20632     constrainTo : function(constrainTo, pad, inContent){
20633         if(typeof pad == "number"){
20634             pad = {left: pad, right:pad, top:pad, bottom:pad};
20635         }
20636         pad = pad || this.defaultPadding;
20637         var b = Roo.get(this.getEl()).getBox();
20638         var ce = Roo.get(constrainTo);
20639         var s = ce.getScroll();
20640         var c, cd = ce.dom;
20641         if(cd == document.body){
20642             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
20643         }else{
20644             xy = ce.getXY();
20645             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
20646         }
20647
20648
20649         var topSpace = b.y - c.y;
20650         var leftSpace = b.x - c.x;
20651
20652         this.resetConstraints();
20653         this.setXConstraint(leftSpace - (pad.left||0), // left
20654                 c.width - leftSpace - b.width - (pad.right||0) //right
20655         );
20656         this.setYConstraint(topSpace - (pad.top||0), //top
20657                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
20658         );
20659     },
20660
20661     /**
20662      * Returns a reference to the linked element
20663      * @method getEl
20664      * @return {HTMLElement} the html element
20665      */
20666     getEl: function() {
20667         if (!this._domRef) {
20668             this._domRef = Roo.getDom(this.id);
20669         }
20670
20671         return this._domRef;
20672     },
20673
20674     /**
20675      * Returns a reference to the actual element to drag.  By default this is
20676      * the same as the html element, but it can be assigned to another
20677      * element. An example of this can be found in Roo.dd.DDProxy
20678      * @method getDragEl
20679      * @return {HTMLElement} the html element
20680      */
20681     getDragEl: function() {
20682         return Roo.getDom(this.dragElId);
20683     },
20684
20685     /**
20686      * Sets up the DragDrop object.  Must be called in the constructor of any
20687      * Roo.dd.DragDrop subclass
20688      * @method init
20689      * @param id the id of the linked element
20690      * @param {String} sGroup the group of related items
20691      * @param {object} config configuration attributes
20692      */
20693     init: function(id, sGroup, config) {
20694         this.initTarget(id, sGroup, config);
20695         if (!Roo.isTouch) {
20696             Event.on(this.id, "mousedown", this.handleMouseDown, this);
20697         }
20698         Event.on(this.id, "touchstart", this.handleMouseDown, this);
20699         // Event.on(this.id, "selectstart", Event.preventDefault);
20700     },
20701
20702     /**
20703      * Initializes Targeting functionality only... the object does not
20704      * get a mousedown handler.
20705      * @method initTarget
20706      * @param id the id of the linked element
20707      * @param {String} sGroup the group of related items
20708      * @param {object} config configuration attributes
20709      */
20710     initTarget: function(id, sGroup, config) {
20711
20712         // configuration attributes
20713         this.config = config || {};
20714
20715         // create a local reference to the drag and drop manager
20716         this.DDM = Roo.dd.DDM;
20717         // initialize the groups array
20718         this.groups = {};
20719
20720         // assume that we have an element reference instead of an id if the
20721         // parameter is not a string
20722         if (typeof id !== "string") {
20723             id = Roo.id(id);
20724         }
20725
20726         // set the id
20727         this.id = id;
20728
20729         // add to an interaction group
20730         this.addToGroup((sGroup) ? sGroup : "default");
20731
20732         // We don't want to register this as the handle with the manager
20733         // so we just set the id rather than calling the setter.
20734         this.handleElId = id;
20735
20736         // the linked element is the element that gets dragged by default
20737         this.setDragElId(id);
20738
20739         // by default, clicked anchors will not start drag operations.
20740         this.invalidHandleTypes = { A: "A" };
20741         this.invalidHandleIds = {};
20742         this.invalidHandleClasses = [];
20743
20744         this.applyConfig();
20745
20746         this.handleOnAvailable();
20747     },
20748
20749     /**
20750      * Applies the configuration parameters that were passed into the constructor.
20751      * This is supposed to happen at each level through the inheritance chain.  So
20752      * a DDProxy implentation will execute apply config on DDProxy, DD, and
20753      * DragDrop in order to get all of the parameters that are available in
20754      * each object.
20755      * @method applyConfig
20756      */
20757     applyConfig: function() {
20758
20759         // configurable properties:
20760         //    padding, isTarget, maintainOffset, primaryButtonOnly
20761         this.padding           = this.config.padding || [0, 0, 0, 0];
20762         this.isTarget          = (this.config.isTarget !== false);
20763         this.maintainOffset    = (this.config.maintainOffset);
20764         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
20765
20766     },
20767
20768     /**
20769      * Executed when the linked element is available
20770      * @method handleOnAvailable
20771      * @private
20772      */
20773     handleOnAvailable: function() {
20774         this.available = true;
20775         this.resetConstraints();
20776         this.onAvailable();
20777     },
20778
20779      /**
20780      * Configures the padding for the target zone in px.  Effectively expands
20781      * (or reduces) the virtual object size for targeting calculations.
20782      * Supports css-style shorthand; if only one parameter is passed, all sides
20783      * will have that padding, and if only two are passed, the top and bottom
20784      * will have the first param, the left and right the second.
20785      * @method setPadding
20786      * @param {int} iTop    Top pad
20787      * @param {int} iRight  Right pad
20788      * @param {int} iBot    Bot pad
20789      * @param {int} iLeft   Left pad
20790      */
20791     setPadding: function(iTop, iRight, iBot, iLeft) {
20792         // this.padding = [iLeft, iRight, iTop, iBot];
20793         if (!iRight && 0 !== iRight) {
20794             this.padding = [iTop, iTop, iTop, iTop];
20795         } else if (!iBot && 0 !== iBot) {
20796             this.padding = [iTop, iRight, iTop, iRight];
20797         } else {
20798             this.padding = [iTop, iRight, iBot, iLeft];
20799         }
20800     },
20801
20802     /**
20803      * Stores the initial placement of the linked element.
20804      * @method setInitialPosition
20805      * @param {int} diffX   the X offset, default 0
20806      * @param {int} diffY   the Y offset, default 0
20807      */
20808     setInitPosition: function(diffX, diffY) {
20809         var el = this.getEl();
20810
20811         if (!this.DDM.verifyEl(el)) {
20812             return;
20813         }
20814
20815         var dx = diffX || 0;
20816         var dy = diffY || 0;
20817
20818         var p = Dom.getXY( el );
20819
20820         this.initPageX = p[0] - dx;
20821         this.initPageY = p[1] - dy;
20822
20823         this.lastPageX = p[0];
20824         this.lastPageY = p[1];
20825
20826
20827         this.setStartPosition(p);
20828     },
20829
20830     /**
20831      * Sets the start position of the element.  This is set when the obj
20832      * is initialized, the reset when a drag is started.
20833      * @method setStartPosition
20834      * @param pos current position (from previous lookup)
20835      * @private
20836      */
20837     setStartPosition: function(pos) {
20838         var p = pos || Dom.getXY( this.getEl() );
20839         this.deltaSetXY = null;
20840
20841         this.startPageX = p[0];
20842         this.startPageY = p[1];
20843     },
20844
20845     /**
20846      * Add this instance to a group of related drag/drop objects.  All
20847      * instances belong to at least one group, and can belong to as many
20848      * groups as needed.
20849      * @method addToGroup
20850      * @param sGroup {string} the name of the group
20851      */
20852     addToGroup: function(sGroup) {
20853         this.groups[sGroup] = true;
20854         this.DDM.regDragDrop(this, sGroup);
20855     },
20856
20857     /**
20858      * Remove's this instance from the supplied interaction group
20859      * @method removeFromGroup
20860      * @param {string}  sGroup  The group to drop
20861      */
20862     removeFromGroup: function(sGroup) {
20863         if (this.groups[sGroup]) {
20864             delete this.groups[sGroup];
20865         }
20866
20867         this.DDM.removeDDFromGroup(this, sGroup);
20868     },
20869
20870     /**
20871      * Allows you to specify that an element other than the linked element
20872      * will be moved with the cursor during a drag
20873      * @method setDragElId
20874      * @param id {string} the id of the element that will be used to initiate the drag
20875      */
20876     setDragElId: function(id) {
20877         this.dragElId = id;
20878     },
20879
20880     /**
20881      * Allows you to specify a child of the linked element that should be
20882      * used to initiate the drag operation.  An example of this would be if
20883      * you have a content div with text and links.  Clicking anywhere in the
20884      * content area would normally start the drag operation.  Use this method
20885      * to specify that an element inside of the content div is the element
20886      * that starts the drag operation.
20887      * @method setHandleElId
20888      * @param id {string} the id of the element that will be used to
20889      * initiate the drag.
20890      */
20891     setHandleElId: function(id) {
20892         if (typeof id !== "string") {
20893             id = Roo.id(id);
20894         }
20895         this.handleElId = id;
20896         this.DDM.regHandle(this.id, id);
20897     },
20898
20899     /**
20900      * Allows you to set an element outside of the linked element as a drag
20901      * handle
20902      * @method setOuterHandleElId
20903      * @param id the id of the element that will be used to initiate the drag
20904      */
20905     setOuterHandleElId: function(id) {
20906         if (typeof id !== "string") {
20907             id = Roo.id(id);
20908         }
20909         Event.on(id, "mousedown",
20910                 this.handleMouseDown, this);
20911         this.setHandleElId(id);
20912
20913         this.hasOuterHandles = true;
20914     },
20915
20916     /**
20917      * Remove all drag and drop hooks for this element
20918      * @method unreg
20919      */
20920     unreg: function() {
20921         Event.un(this.id, "mousedown",
20922                 this.handleMouseDown);
20923         Event.un(this.id, "touchstart",
20924                 this.handleMouseDown);
20925         this._domRef = null;
20926         this.DDM._remove(this);
20927     },
20928
20929     destroy : function(){
20930         this.unreg();
20931     },
20932
20933     /**
20934      * Returns true if this instance is locked, or the drag drop mgr is locked
20935      * (meaning that all drag/drop is disabled on the page.)
20936      * @method isLocked
20937      * @return {boolean} true if this obj or all drag/drop is locked, else
20938      * false
20939      */
20940     isLocked: function() {
20941         return (this.DDM.isLocked() || this.locked);
20942     },
20943
20944     /**
20945      * Fired when this object is clicked
20946      * @method handleMouseDown
20947      * @param {Event} e
20948      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
20949      * @private
20950      */
20951     handleMouseDown: function(e, oDD){
20952      
20953         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
20954             //Roo.log('not touch/ button !=0');
20955             return;
20956         }
20957         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
20958             return; // double touch..
20959         }
20960         
20961
20962         if (this.isLocked()) {
20963             //Roo.log('locked');
20964             return;
20965         }
20966
20967         this.DDM.refreshCache(this.groups);
20968 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
20969         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
20970         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
20971             //Roo.log('no outer handes or not over target');
20972                 // do nothing.
20973         } else {
20974 //            Roo.log('check validator');
20975             if (this.clickValidator(e)) {
20976 //                Roo.log('validate success');
20977                 // set the initial element position
20978                 this.setStartPosition();
20979
20980
20981                 this.b4MouseDown(e);
20982                 this.onMouseDown(e);
20983
20984                 this.DDM.handleMouseDown(e, this);
20985
20986                 this.DDM.stopEvent(e);
20987             } else {
20988
20989
20990             }
20991         }
20992     },
20993
20994     clickValidator: function(e) {
20995         var target = e.getTarget();
20996         return ( this.isValidHandleChild(target) &&
20997                     (this.id == this.handleElId ||
20998                         this.DDM.handleWasClicked(target, this.id)) );
20999     },
21000
21001     /**
21002      * Allows you to specify a tag name that should not start a drag operation
21003      * when clicked.  This is designed to facilitate embedding links within a
21004      * drag handle that do something other than start the drag.
21005      * @method addInvalidHandleType
21006      * @param {string} tagName the type of element to exclude
21007      */
21008     addInvalidHandleType: function(tagName) {
21009         var type = tagName.toUpperCase();
21010         this.invalidHandleTypes[type] = type;
21011     },
21012
21013     /**
21014      * Lets you to specify an element id for a child of a drag handle
21015      * that should not initiate a drag
21016      * @method addInvalidHandleId
21017      * @param {string} id the element id of the element you wish to ignore
21018      */
21019     addInvalidHandleId: function(id) {
21020         if (typeof id !== "string") {
21021             id = Roo.id(id);
21022         }
21023         this.invalidHandleIds[id] = id;
21024     },
21025
21026     /**
21027      * Lets you specify a css class of elements that will not initiate a drag
21028      * @method addInvalidHandleClass
21029      * @param {string} cssClass the class of the elements you wish to ignore
21030      */
21031     addInvalidHandleClass: function(cssClass) {
21032         this.invalidHandleClasses.push(cssClass);
21033     },
21034
21035     /**
21036      * Unsets an excluded tag name set by addInvalidHandleType
21037      * @method removeInvalidHandleType
21038      * @param {string} tagName the type of element to unexclude
21039      */
21040     removeInvalidHandleType: function(tagName) {
21041         var type = tagName.toUpperCase();
21042         // this.invalidHandleTypes[type] = null;
21043         delete this.invalidHandleTypes[type];
21044     },
21045
21046     /**
21047      * Unsets an invalid handle id
21048      * @method removeInvalidHandleId
21049      * @param {string} id the id of the element to re-enable
21050      */
21051     removeInvalidHandleId: function(id) {
21052         if (typeof id !== "string") {
21053             id = Roo.id(id);
21054         }
21055         delete this.invalidHandleIds[id];
21056     },
21057
21058     /**
21059      * Unsets an invalid css class
21060      * @method removeInvalidHandleClass
21061      * @param {string} cssClass the class of the element(s) you wish to
21062      * re-enable
21063      */
21064     removeInvalidHandleClass: function(cssClass) {
21065         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
21066             if (this.invalidHandleClasses[i] == cssClass) {
21067                 delete this.invalidHandleClasses[i];
21068             }
21069         }
21070     },
21071
21072     /**
21073      * Checks the tag exclusion list to see if this click should be ignored
21074      * @method isValidHandleChild
21075      * @param {HTMLElement} node the HTMLElement to evaluate
21076      * @return {boolean} true if this is a valid tag type, false if not
21077      */
21078     isValidHandleChild: function(node) {
21079
21080         var valid = true;
21081         // var n = (node.nodeName == "#text") ? node.parentNode : node;
21082         var nodeName;
21083         try {
21084             nodeName = node.nodeName.toUpperCase();
21085         } catch(e) {
21086             nodeName = node.nodeName;
21087         }
21088         valid = valid && !this.invalidHandleTypes[nodeName];
21089         valid = valid && !this.invalidHandleIds[node.id];
21090
21091         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
21092             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
21093         }
21094
21095
21096         return valid;
21097
21098     },
21099
21100     /**
21101      * Create the array of horizontal tick marks if an interval was specified
21102      * in setXConstraint().
21103      * @method setXTicks
21104      * @private
21105      */
21106     setXTicks: function(iStartX, iTickSize) {
21107         this.xTicks = [];
21108         this.xTickSize = iTickSize;
21109
21110         var tickMap = {};
21111
21112         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
21113             if (!tickMap[i]) {
21114                 this.xTicks[this.xTicks.length] = i;
21115                 tickMap[i] = true;
21116             }
21117         }
21118
21119         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
21120             if (!tickMap[i]) {
21121                 this.xTicks[this.xTicks.length] = i;
21122                 tickMap[i] = true;
21123             }
21124         }
21125
21126         this.xTicks.sort(this.DDM.numericSort) ;
21127     },
21128
21129     /**
21130      * Create the array of vertical tick marks if an interval was specified in
21131      * setYConstraint().
21132      * @method setYTicks
21133      * @private
21134      */
21135     setYTicks: function(iStartY, iTickSize) {
21136         this.yTicks = [];
21137         this.yTickSize = iTickSize;
21138
21139         var tickMap = {};
21140
21141         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
21142             if (!tickMap[i]) {
21143                 this.yTicks[this.yTicks.length] = i;
21144                 tickMap[i] = true;
21145             }
21146         }
21147
21148         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
21149             if (!tickMap[i]) {
21150                 this.yTicks[this.yTicks.length] = i;
21151                 tickMap[i] = true;
21152             }
21153         }
21154
21155         this.yTicks.sort(this.DDM.numericSort) ;
21156     },
21157
21158     /**
21159      * By default, the element can be dragged any place on the screen.  Use
21160      * this method to limit the horizontal travel of the element.  Pass in
21161      * 0,0 for the parameters if you want to lock the drag to the y axis.
21162      * @method setXConstraint
21163      * @param {int} iLeft the number of pixels the element can move to the left
21164      * @param {int} iRight the number of pixels the element can move to the
21165      * right
21166      * @param {int} iTickSize optional parameter for specifying that the
21167      * element
21168      * should move iTickSize pixels at a time.
21169      */
21170     setXConstraint: function(iLeft, iRight, iTickSize) {
21171         this.leftConstraint = iLeft;
21172         this.rightConstraint = iRight;
21173
21174         this.minX = this.initPageX - iLeft;
21175         this.maxX = this.initPageX + iRight;
21176         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
21177
21178         this.constrainX = true;
21179     },
21180
21181     /**
21182      * Clears any constraints applied to this instance.  Also clears ticks
21183      * since they can't exist independent of a constraint at this time.
21184      * @method clearConstraints
21185      */
21186     clearConstraints: function() {
21187         this.constrainX = false;
21188         this.constrainY = false;
21189         this.clearTicks();
21190     },
21191
21192     /**
21193      * Clears any tick interval defined for this instance
21194      * @method clearTicks
21195      */
21196     clearTicks: function() {
21197         this.xTicks = null;
21198         this.yTicks = null;
21199         this.xTickSize = 0;
21200         this.yTickSize = 0;
21201     },
21202
21203     /**
21204      * By default, the element can be dragged any place on the screen.  Set
21205      * this to limit the vertical travel of the element.  Pass in 0,0 for the
21206      * parameters if you want to lock the drag to the x axis.
21207      * @method setYConstraint
21208      * @param {int} iUp the number of pixels the element can move up
21209      * @param {int} iDown the number of pixels the element can move down
21210      * @param {int} iTickSize optional parameter for specifying that the
21211      * element should move iTickSize pixels at a time.
21212      */
21213     setYConstraint: function(iUp, iDown, iTickSize) {
21214         this.topConstraint = iUp;
21215         this.bottomConstraint = iDown;
21216
21217         this.minY = this.initPageY - iUp;
21218         this.maxY = this.initPageY + iDown;
21219         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
21220
21221         this.constrainY = true;
21222
21223     },
21224
21225     /**
21226      * resetConstraints must be called if you manually reposition a dd element.
21227      * @method resetConstraints
21228      * @param {boolean} maintainOffset
21229      */
21230     resetConstraints: function() {
21231
21232
21233         // Maintain offsets if necessary
21234         if (this.initPageX || this.initPageX === 0) {
21235             // figure out how much this thing has moved
21236             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
21237             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
21238
21239             this.setInitPosition(dx, dy);
21240
21241         // This is the first time we have detected the element's position
21242         } else {
21243             this.setInitPosition();
21244         }
21245
21246         if (this.constrainX) {
21247             this.setXConstraint( this.leftConstraint,
21248                                  this.rightConstraint,
21249                                  this.xTickSize        );
21250         }
21251
21252         if (this.constrainY) {
21253             this.setYConstraint( this.topConstraint,
21254                                  this.bottomConstraint,
21255                                  this.yTickSize         );
21256         }
21257     },
21258
21259     /**
21260      * Normally the drag element is moved pixel by pixel, but we can specify
21261      * that it move a number of pixels at a time.  This method resolves the
21262      * location when we have it set up like this.
21263      * @method getTick
21264      * @param {int} val where we want to place the object
21265      * @param {int[]} tickArray sorted array of valid points
21266      * @return {int} the closest tick
21267      * @private
21268      */
21269     getTick: function(val, tickArray) {
21270
21271         if (!tickArray) {
21272             // If tick interval is not defined, it is effectively 1 pixel,
21273             // so we return the value passed to us.
21274             return val;
21275         } else if (tickArray[0] >= val) {
21276             // The value is lower than the first tick, so we return the first
21277             // tick.
21278             return tickArray[0];
21279         } else {
21280             for (var i=0, len=tickArray.length; i<len; ++i) {
21281                 var next = i + 1;
21282                 if (tickArray[next] && tickArray[next] >= val) {
21283                     var diff1 = val - tickArray[i];
21284                     var diff2 = tickArray[next] - val;
21285                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
21286                 }
21287             }
21288
21289             // The value is larger than the last tick, so we return the last
21290             // tick.
21291             return tickArray[tickArray.length - 1];
21292         }
21293     },
21294
21295     /**
21296      * toString method
21297      * @method toString
21298      * @return {string} string representation of the dd obj
21299      */
21300     toString: function() {
21301         return ("DragDrop " + this.id);
21302     }
21303
21304 });
21305
21306 })();
21307 /*
21308  * Based on:
21309  * Ext JS Library 1.1.1
21310  * Copyright(c) 2006-2007, Ext JS, LLC.
21311  *
21312  * Originally Released Under LGPL - original licence link has changed is not relivant.
21313  *
21314  * Fork - LGPL
21315  * <script type="text/javascript">
21316  */
21317
21318
21319 /**
21320  * The drag and drop utility provides a framework for building drag and drop
21321  * applications.  In addition to enabling drag and drop for specific elements,
21322  * the drag and drop elements are tracked by the manager class, and the
21323  * interactions between the various elements are tracked during the drag and
21324  * the implementing code is notified about these important moments.
21325  */
21326
21327 // Only load the library once.  Rewriting the manager class would orphan
21328 // existing drag and drop instances.
21329 if (!Roo.dd.DragDropMgr) {
21330
21331 /**
21332  * @class Roo.dd.DragDropMgr
21333  * DragDropMgr is a singleton that tracks the element interaction for
21334  * all DragDrop items in the window.  Generally, you will not call
21335  * this class directly, but it does have helper methods that could
21336  * be useful in your DragDrop implementations.
21337  * @static
21338  */
21339 Roo.dd.DragDropMgr = function() {
21340
21341     var Event = Roo.EventManager;
21342
21343     return {
21344
21345         /**
21346          * Two dimensional Array of registered DragDrop objects.  The first
21347          * dimension is the DragDrop item group, the second the DragDrop
21348          * object.
21349          * @property ids
21350          * @type {string: string}
21351          * @private
21352          * @static
21353          */
21354         ids: {},
21355
21356         /**
21357          * Array of element ids defined as drag handles.  Used to determine
21358          * if the element that generated the mousedown event is actually the
21359          * handle and not the html element itself.
21360          * @property handleIds
21361          * @type {string: string}
21362          * @private
21363          * @static
21364          */
21365         handleIds: {},
21366
21367         /**
21368          * the DragDrop object that is currently being dragged
21369          * @property dragCurrent
21370          * @type DragDrop
21371          * @private
21372          * @static
21373          **/
21374         dragCurrent: null,
21375
21376         /**
21377          * the DragDrop object(s) that are being hovered over
21378          * @property dragOvers
21379          * @type Array
21380          * @private
21381          * @static
21382          */
21383         dragOvers: {},
21384
21385         /**
21386          * the X distance between the cursor and the object being dragged
21387          * @property deltaX
21388          * @type int
21389          * @private
21390          * @static
21391          */
21392         deltaX: 0,
21393
21394         /**
21395          * the Y distance between the cursor and the object being dragged
21396          * @property deltaY
21397          * @type int
21398          * @private
21399          * @static
21400          */
21401         deltaY: 0,
21402
21403         /**
21404          * Flag to determine if we should prevent the default behavior of the
21405          * events we define. By default this is true, but this can be set to
21406          * false if you need the default behavior (not recommended)
21407          * @property preventDefault
21408          * @type boolean
21409          * @static
21410          */
21411         preventDefault: true,
21412
21413         /**
21414          * Flag to determine if we should stop the propagation of the events
21415          * we generate. This is true by default but you may want to set it to
21416          * false if the html element contains other features that require the
21417          * mouse click.
21418          * @property stopPropagation
21419          * @type boolean
21420          * @static
21421          */
21422         stopPropagation: true,
21423
21424         /**
21425          * Internal flag that is set to true when drag and drop has been
21426          * intialized
21427          * @property initialized
21428          * @private
21429          * @static
21430          */
21431         initalized: false,
21432
21433         /**
21434          * All drag and drop can be disabled.
21435          * @property locked
21436          * @private
21437          * @static
21438          */
21439         locked: false,
21440
21441         /**
21442          * Called the first time an element is registered.
21443          * @method init
21444          * @private
21445          * @static
21446          */
21447         init: function() {
21448             this.initialized = true;
21449         },
21450
21451         /**
21452          * In point mode, drag and drop interaction is defined by the
21453          * location of the cursor during the drag/drop
21454          * @property POINT
21455          * @type int
21456          * @static
21457          */
21458         POINT: 0,
21459
21460         /**
21461          * In intersect mode, drag and drop interactio nis defined by the
21462          * overlap of two or more drag and drop objects.
21463          * @property INTERSECT
21464          * @type int
21465          * @static
21466          */
21467         INTERSECT: 1,
21468
21469         /**
21470          * The current drag and drop mode.  Default: POINT
21471          * @property mode
21472          * @type int
21473          * @static
21474          */
21475         mode: 0,
21476
21477         /**
21478          * Runs method on all drag and drop objects
21479          * @method _execOnAll
21480          * @private
21481          * @static
21482          */
21483         _execOnAll: function(sMethod, args) {
21484             for (var i in this.ids) {
21485                 for (var j in this.ids[i]) {
21486                     var oDD = this.ids[i][j];
21487                     if (! this.isTypeOfDD(oDD)) {
21488                         continue;
21489                     }
21490                     oDD[sMethod].apply(oDD, args);
21491                 }
21492             }
21493         },
21494
21495         /**
21496          * Drag and drop initialization.  Sets up the global event handlers
21497          * @method _onLoad
21498          * @private
21499          * @static
21500          */
21501         _onLoad: function() {
21502
21503             this.init();
21504
21505             if (!Roo.isTouch) {
21506                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
21507                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
21508             }
21509             Event.on(document, "touchend",   this.handleMouseUp, this, true);
21510             Event.on(document, "touchmove", this.handleMouseMove, this, true);
21511             
21512             Event.on(window,   "unload",    this._onUnload, this, true);
21513             Event.on(window,   "resize",    this._onResize, this, true);
21514             // Event.on(window,   "mouseout",    this._test);
21515
21516         },
21517
21518         /**
21519          * Reset constraints on all drag and drop objs
21520          * @method _onResize
21521          * @private
21522          * @static
21523          */
21524         _onResize: function(e) {
21525             this._execOnAll("resetConstraints", []);
21526         },
21527
21528         /**
21529          * Lock all drag and drop functionality
21530          * @method lock
21531          * @static
21532          */
21533         lock: function() { this.locked = true; },
21534
21535         /**
21536          * Unlock all drag and drop functionality
21537          * @method unlock
21538          * @static
21539          */
21540         unlock: function() { this.locked = false; },
21541
21542         /**
21543          * Is drag and drop locked?
21544          * @method isLocked
21545          * @return {boolean} True if drag and drop is locked, false otherwise.
21546          * @static
21547          */
21548         isLocked: function() { return this.locked; },
21549
21550         /**
21551          * Location cache that is set for all drag drop objects when a drag is
21552          * initiated, cleared when the drag is finished.
21553          * @property locationCache
21554          * @private
21555          * @static
21556          */
21557         locationCache: {},
21558
21559         /**
21560          * Set useCache to false if you want to force object the lookup of each
21561          * drag and drop linked element constantly during a drag.
21562          * @property useCache
21563          * @type boolean
21564          * @static
21565          */
21566         useCache: true,
21567
21568         /**
21569          * The number of pixels that the mouse needs to move after the
21570          * mousedown before the drag is initiated.  Default=3;
21571          * @property clickPixelThresh
21572          * @type int
21573          * @static
21574          */
21575         clickPixelThresh: 3,
21576
21577         /**
21578          * The number of milliseconds after the mousedown event to initiate the
21579          * drag if we don't get a mouseup event. Default=1000
21580          * @property clickTimeThresh
21581          * @type int
21582          * @static
21583          */
21584         clickTimeThresh: 350,
21585
21586         /**
21587          * Flag that indicates that either the drag pixel threshold or the
21588          * mousdown time threshold has been met
21589          * @property dragThreshMet
21590          * @type boolean
21591          * @private
21592          * @static
21593          */
21594         dragThreshMet: false,
21595
21596         /**
21597          * Timeout used for the click time threshold
21598          * @property clickTimeout
21599          * @type Object
21600          * @private
21601          * @static
21602          */
21603         clickTimeout: null,
21604
21605         /**
21606          * The X position of the mousedown event stored for later use when a
21607          * drag threshold is met.
21608          * @property startX
21609          * @type int
21610          * @private
21611          * @static
21612          */
21613         startX: 0,
21614
21615         /**
21616          * The Y position of the mousedown event stored for later use when a
21617          * drag threshold is met.
21618          * @property startY
21619          * @type int
21620          * @private
21621          * @static
21622          */
21623         startY: 0,
21624
21625         /**
21626          * Each DragDrop instance must be registered with the DragDropMgr.
21627          * This is executed in DragDrop.init()
21628          * @method regDragDrop
21629          * @param {DragDrop} oDD the DragDrop object to register
21630          * @param {String} sGroup the name of the group this element belongs to
21631          * @static
21632          */
21633         regDragDrop: function(oDD, sGroup) {
21634             if (!this.initialized) { this.init(); }
21635
21636             if (!this.ids[sGroup]) {
21637                 this.ids[sGroup] = {};
21638             }
21639             this.ids[sGroup][oDD.id] = oDD;
21640         },
21641
21642         /**
21643          * Removes the supplied dd instance from the supplied group. Executed
21644          * by DragDrop.removeFromGroup, so don't call this function directly.
21645          * @method removeDDFromGroup
21646          * @private
21647          * @static
21648          */
21649         removeDDFromGroup: function(oDD, sGroup) {
21650             if (!this.ids[sGroup]) {
21651                 this.ids[sGroup] = {};
21652             }
21653
21654             var obj = this.ids[sGroup];
21655             if (obj && obj[oDD.id]) {
21656                 delete obj[oDD.id];
21657             }
21658         },
21659
21660         /**
21661          * Unregisters a drag and drop item.  This is executed in
21662          * DragDrop.unreg, use that method instead of calling this directly.
21663          * @method _remove
21664          * @private
21665          * @static
21666          */
21667         _remove: function(oDD) {
21668             for (var g in oDD.groups) {
21669                 if (g && this.ids[g][oDD.id]) {
21670                     delete this.ids[g][oDD.id];
21671                 }
21672             }
21673             delete this.handleIds[oDD.id];
21674         },
21675
21676         /**
21677          * Each DragDrop handle element must be registered.  This is done
21678          * automatically when executing DragDrop.setHandleElId()
21679          * @method regHandle
21680          * @param {String} sDDId the DragDrop id this element is a handle for
21681          * @param {String} sHandleId the id of the element that is the drag
21682          * handle
21683          * @static
21684          */
21685         regHandle: function(sDDId, sHandleId) {
21686             if (!this.handleIds[sDDId]) {
21687                 this.handleIds[sDDId] = {};
21688             }
21689             this.handleIds[sDDId][sHandleId] = sHandleId;
21690         },
21691
21692         /**
21693          * Utility function to determine if a given element has been
21694          * registered as a drag drop item.
21695          * @method isDragDrop
21696          * @param {String} id the element id to check
21697          * @return {boolean} true if this element is a DragDrop item,
21698          * false otherwise
21699          * @static
21700          */
21701         isDragDrop: function(id) {
21702             return ( this.getDDById(id) ) ? true : false;
21703         },
21704
21705         /**
21706          * Returns the drag and drop instances that are in all groups the
21707          * passed in instance belongs to.
21708          * @method getRelated
21709          * @param {DragDrop} p_oDD the obj to get related data for
21710          * @param {boolean} bTargetsOnly if true, only return targetable objs
21711          * @return {DragDrop[]} the related instances
21712          * @static
21713          */
21714         getRelated: function(p_oDD, bTargetsOnly) {
21715             var oDDs = [];
21716             for (var i in p_oDD.groups) {
21717                 for (j in this.ids[i]) {
21718                     var dd = this.ids[i][j];
21719                     if (! this.isTypeOfDD(dd)) {
21720                         continue;
21721                     }
21722                     if (!bTargetsOnly || dd.isTarget) {
21723                         oDDs[oDDs.length] = dd;
21724                     }
21725                 }
21726             }
21727
21728             return oDDs;
21729         },
21730
21731         /**
21732          * Returns true if the specified dd target is a legal target for
21733          * the specifice drag obj
21734          * @method isLegalTarget
21735          * @param {DragDrop} the drag obj
21736          * @param {DragDrop} the target
21737          * @return {boolean} true if the target is a legal target for the
21738          * dd obj
21739          * @static
21740          */
21741         isLegalTarget: function (oDD, oTargetDD) {
21742             var targets = this.getRelated(oDD, true);
21743             for (var i=0, len=targets.length;i<len;++i) {
21744                 if (targets[i].id == oTargetDD.id) {
21745                     return true;
21746                 }
21747             }
21748
21749             return false;
21750         },
21751
21752         /**
21753          * My goal is to be able to transparently determine if an object is
21754          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
21755          * returns "object", oDD.constructor.toString() always returns
21756          * "DragDrop" and not the name of the subclass.  So for now it just
21757          * evaluates a well-known variable in DragDrop.
21758          * @method isTypeOfDD
21759          * @param {Object} the object to evaluate
21760          * @return {boolean} true if typeof oDD = DragDrop
21761          * @static
21762          */
21763         isTypeOfDD: function (oDD) {
21764             return (oDD && oDD.__ygDragDrop);
21765         },
21766
21767         /**
21768          * Utility function to determine if a given element has been
21769          * registered as a drag drop handle for the given Drag Drop object.
21770          * @method isHandle
21771          * @param {String} id the element id to check
21772          * @return {boolean} true if this element is a DragDrop handle, false
21773          * otherwise
21774          * @static
21775          */
21776         isHandle: function(sDDId, sHandleId) {
21777             return ( this.handleIds[sDDId] &&
21778                             this.handleIds[sDDId][sHandleId] );
21779         },
21780
21781         /**
21782          * Returns the DragDrop instance for a given id
21783          * @method getDDById
21784          * @param {String} id the id of the DragDrop object
21785          * @return {DragDrop} the drag drop object, null if it is not found
21786          * @static
21787          */
21788         getDDById: function(id) {
21789             for (var i in this.ids) {
21790                 if (this.ids[i][id]) {
21791                     return this.ids[i][id];
21792                 }
21793             }
21794             return null;
21795         },
21796
21797         /**
21798          * Fired after a registered DragDrop object gets the mousedown event.
21799          * Sets up the events required to track the object being dragged
21800          * @method handleMouseDown
21801          * @param {Event} e the event
21802          * @param oDD the DragDrop object being dragged
21803          * @private
21804          * @static
21805          */
21806         handleMouseDown: function(e, oDD) {
21807             if(Roo.QuickTips){
21808                 Roo.QuickTips.disable();
21809             }
21810             this.currentTarget = e.getTarget();
21811
21812             this.dragCurrent = oDD;
21813
21814             var el = oDD.getEl();
21815
21816             // track start position
21817             this.startX = e.getPageX();
21818             this.startY = e.getPageY();
21819
21820             this.deltaX = this.startX - el.offsetLeft;
21821             this.deltaY = this.startY - el.offsetTop;
21822
21823             this.dragThreshMet = false;
21824
21825             this.clickTimeout = setTimeout(
21826                     function() {
21827                         var DDM = Roo.dd.DDM;
21828                         DDM.startDrag(DDM.startX, DDM.startY);
21829                     },
21830                     this.clickTimeThresh );
21831         },
21832
21833         /**
21834          * Fired when either the drag pixel threshol or the mousedown hold
21835          * time threshold has been met.
21836          * @method startDrag
21837          * @param x {int} the X position of the original mousedown
21838          * @param y {int} the Y position of the original mousedown
21839          * @static
21840          */
21841         startDrag: function(x, y) {
21842             clearTimeout(this.clickTimeout);
21843             if (this.dragCurrent) {
21844                 this.dragCurrent.b4StartDrag(x, y);
21845                 this.dragCurrent.startDrag(x, y);
21846             }
21847             this.dragThreshMet = true;
21848         },
21849
21850         /**
21851          * Internal function to handle the mouseup event.  Will be invoked
21852          * from the context of the document.
21853          * @method handleMouseUp
21854          * @param {Event} e the event
21855          * @private
21856          * @static
21857          */
21858         handleMouseUp: function(e) {
21859
21860             if(Roo.QuickTips){
21861                 Roo.QuickTips.enable();
21862             }
21863             if (! this.dragCurrent) {
21864                 return;
21865             }
21866
21867             clearTimeout(this.clickTimeout);
21868
21869             if (this.dragThreshMet) {
21870                 this.fireEvents(e, true);
21871             } else {
21872             }
21873
21874             this.stopDrag(e);
21875
21876             this.stopEvent(e);
21877         },
21878
21879         /**
21880          * Utility to stop event propagation and event default, if these
21881          * features are turned on.
21882          * @method stopEvent
21883          * @param {Event} e the event as returned by this.getEvent()
21884          * @static
21885          */
21886         stopEvent: function(e){
21887             if(this.stopPropagation) {
21888                 e.stopPropagation();
21889             }
21890
21891             if (this.preventDefault) {
21892                 e.preventDefault();
21893             }
21894         },
21895
21896         /**
21897          * Internal function to clean up event handlers after the drag
21898          * operation is complete
21899          * @method stopDrag
21900          * @param {Event} e the event
21901          * @private
21902          * @static
21903          */
21904         stopDrag: function(e) {
21905             // Fire the drag end event for the item that was dragged
21906             if (this.dragCurrent) {
21907                 if (this.dragThreshMet) {
21908                     this.dragCurrent.b4EndDrag(e);
21909                     this.dragCurrent.endDrag(e);
21910                 }
21911
21912                 this.dragCurrent.onMouseUp(e);
21913             }
21914
21915             this.dragCurrent = null;
21916             this.dragOvers = {};
21917         },
21918
21919         /**
21920          * Internal function to handle the mousemove event.  Will be invoked
21921          * from the context of the html element.
21922          *
21923          * @TODO figure out what we can do about mouse events lost when the
21924          * user drags objects beyond the window boundary.  Currently we can
21925          * detect this in internet explorer by verifying that the mouse is
21926          * down during the mousemove event.  Firefox doesn't give us the
21927          * button state on the mousemove event.
21928          * @method handleMouseMove
21929          * @param {Event} e the event
21930          * @private
21931          * @static
21932          */
21933         handleMouseMove: function(e) {
21934             if (! this.dragCurrent) {
21935                 return true;
21936             }
21937
21938             // var button = e.which || e.button;
21939
21940             // check for IE mouseup outside of page boundary
21941             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
21942                 this.stopEvent(e);
21943                 return this.handleMouseUp(e);
21944             }
21945
21946             if (!this.dragThreshMet) {
21947                 var diffX = Math.abs(this.startX - e.getPageX());
21948                 var diffY = Math.abs(this.startY - e.getPageY());
21949                 if (diffX > this.clickPixelThresh ||
21950                             diffY > this.clickPixelThresh) {
21951                     this.startDrag(this.startX, this.startY);
21952                 }
21953             }
21954
21955             if (this.dragThreshMet) {
21956                 this.dragCurrent.b4Drag(e);
21957                 this.dragCurrent.onDrag(e);
21958                 if(!this.dragCurrent.moveOnly){
21959                     this.fireEvents(e, false);
21960                 }
21961             }
21962
21963             this.stopEvent(e);
21964
21965             return true;
21966         },
21967
21968         /**
21969          * Iterates over all of the DragDrop elements to find ones we are
21970          * hovering over or dropping on
21971          * @method fireEvents
21972          * @param {Event} e the event
21973          * @param {boolean} isDrop is this a drop op or a mouseover op?
21974          * @private
21975          * @static
21976          */
21977         fireEvents: function(e, isDrop) {
21978             var dc = this.dragCurrent;
21979
21980             // If the user did the mouse up outside of the window, we could
21981             // get here even though we have ended the drag.
21982             if (!dc || dc.isLocked()) {
21983                 return;
21984             }
21985
21986             var pt = e.getPoint();
21987
21988             // cache the previous dragOver array
21989             var oldOvers = [];
21990
21991             var outEvts   = [];
21992             var overEvts  = [];
21993             var dropEvts  = [];
21994             var enterEvts = [];
21995
21996             // Check to see if the object(s) we were hovering over is no longer
21997             // being hovered over so we can fire the onDragOut event
21998             for (var i in this.dragOvers) {
21999
22000                 var ddo = this.dragOvers[i];
22001
22002                 if (! this.isTypeOfDD(ddo)) {
22003                     continue;
22004                 }
22005
22006                 if (! this.isOverTarget(pt, ddo, this.mode)) {
22007                     outEvts.push( ddo );
22008                 }
22009
22010                 oldOvers[i] = true;
22011                 delete this.dragOvers[i];
22012             }
22013
22014             for (var sGroup in dc.groups) {
22015
22016                 if ("string" != typeof sGroup) {
22017                     continue;
22018                 }
22019
22020                 for (i in this.ids[sGroup]) {
22021                     var oDD = this.ids[sGroup][i];
22022                     if (! this.isTypeOfDD(oDD)) {
22023                         continue;
22024                     }
22025
22026                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
22027                         if (this.isOverTarget(pt, oDD, this.mode)) {
22028                             // look for drop interactions
22029                             if (isDrop) {
22030                                 dropEvts.push( oDD );
22031                             // look for drag enter and drag over interactions
22032                             } else {
22033
22034                                 // initial drag over: dragEnter fires
22035                                 if (!oldOvers[oDD.id]) {
22036                                     enterEvts.push( oDD );
22037                                 // subsequent drag overs: dragOver fires
22038                                 } else {
22039                                     overEvts.push( oDD );
22040                                 }
22041
22042                                 this.dragOvers[oDD.id] = oDD;
22043                             }
22044                         }
22045                     }
22046                 }
22047             }
22048
22049             if (this.mode) {
22050                 if (outEvts.length) {
22051                     dc.b4DragOut(e, outEvts);
22052                     dc.onDragOut(e, outEvts);
22053                 }
22054
22055                 if (enterEvts.length) {
22056                     dc.onDragEnter(e, enterEvts);
22057                 }
22058
22059                 if (overEvts.length) {
22060                     dc.b4DragOver(e, overEvts);
22061                     dc.onDragOver(e, overEvts);
22062                 }
22063
22064                 if (dropEvts.length) {
22065                     dc.b4DragDrop(e, dropEvts);
22066                     dc.onDragDrop(e, dropEvts);
22067                 }
22068
22069             } else {
22070                 // fire dragout events
22071                 var len = 0;
22072                 for (i=0, len=outEvts.length; i<len; ++i) {
22073                     dc.b4DragOut(e, outEvts[i].id);
22074                     dc.onDragOut(e, outEvts[i].id);
22075                 }
22076
22077                 // fire enter events
22078                 for (i=0,len=enterEvts.length; i<len; ++i) {
22079                     // dc.b4DragEnter(e, oDD.id);
22080                     dc.onDragEnter(e, enterEvts[i].id);
22081                 }
22082
22083                 // fire over events
22084                 for (i=0,len=overEvts.length; i<len; ++i) {
22085                     dc.b4DragOver(e, overEvts[i].id);
22086                     dc.onDragOver(e, overEvts[i].id);
22087                 }
22088
22089                 // fire drop events
22090                 for (i=0, len=dropEvts.length; i<len; ++i) {
22091                     dc.b4DragDrop(e, dropEvts[i].id);
22092                     dc.onDragDrop(e, dropEvts[i].id);
22093                 }
22094
22095             }
22096
22097             // notify about a drop that did not find a target
22098             if (isDrop && !dropEvts.length) {
22099                 dc.onInvalidDrop(e);
22100             }
22101
22102         },
22103
22104         /**
22105          * Helper function for getting the best match from the list of drag
22106          * and drop objects returned by the drag and drop events when we are
22107          * in INTERSECT mode.  It returns either the first object that the
22108          * cursor is over, or the object that has the greatest overlap with
22109          * the dragged element.
22110          * @method getBestMatch
22111          * @param  {DragDrop[]} dds The array of drag and drop objects
22112          * targeted
22113          * @return {DragDrop}       The best single match
22114          * @static
22115          */
22116         getBestMatch: function(dds) {
22117             var winner = null;
22118             // Return null if the input is not what we expect
22119             //if (!dds || !dds.length || dds.length == 0) {
22120                // winner = null;
22121             // If there is only one item, it wins
22122             //} else if (dds.length == 1) {
22123
22124             var len = dds.length;
22125
22126             if (len == 1) {
22127                 winner = dds[0];
22128             } else {
22129                 // Loop through the targeted items
22130                 for (var i=0; i<len; ++i) {
22131                     var dd = dds[i];
22132                     // If the cursor is over the object, it wins.  If the
22133                     // cursor is over multiple matches, the first one we come
22134                     // to wins.
22135                     if (dd.cursorIsOver) {
22136                         winner = dd;
22137                         break;
22138                     // Otherwise the object with the most overlap wins
22139                     } else {
22140                         if (!winner ||
22141                             winner.overlap.getArea() < dd.overlap.getArea()) {
22142                             winner = dd;
22143                         }
22144                     }
22145                 }
22146             }
22147
22148             return winner;
22149         },
22150
22151         /**
22152          * Refreshes the cache of the top-left and bottom-right points of the
22153          * drag and drop objects in the specified group(s).  This is in the
22154          * format that is stored in the drag and drop instance, so typical
22155          * usage is:
22156          * <code>
22157          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
22158          * </code>
22159          * Alternatively:
22160          * <code>
22161          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
22162          * </code>
22163          * @TODO this really should be an indexed array.  Alternatively this
22164          * method could accept both.
22165          * @method refreshCache
22166          * @param {Object} groups an associative array of groups to refresh
22167          * @static
22168          */
22169         refreshCache: function(groups) {
22170             for (var sGroup in groups) {
22171                 if ("string" != typeof sGroup) {
22172                     continue;
22173                 }
22174                 for (var i in this.ids[sGroup]) {
22175                     var oDD = this.ids[sGroup][i];
22176
22177                     if (this.isTypeOfDD(oDD)) {
22178                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
22179                         var loc = this.getLocation(oDD);
22180                         if (loc) {
22181                             this.locationCache[oDD.id] = loc;
22182                         } else {
22183                             delete this.locationCache[oDD.id];
22184                             // this will unregister the drag and drop object if
22185                             // the element is not in a usable state
22186                             // oDD.unreg();
22187                         }
22188                     }
22189                 }
22190             }
22191         },
22192
22193         /**
22194          * This checks to make sure an element exists and is in the DOM.  The
22195          * main purpose is to handle cases where innerHTML is used to remove
22196          * drag and drop objects from the DOM.  IE provides an 'unspecified
22197          * error' when trying to access the offsetParent of such an element
22198          * @method verifyEl
22199          * @param {HTMLElement} el the element to check
22200          * @return {boolean} true if the element looks usable
22201          * @static
22202          */
22203         verifyEl: function(el) {
22204             if (el) {
22205                 var parent;
22206                 if(Roo.isIE){
22207                     try{
22208                         parent = el.offsetParent;
22209                     }catch(e){}
22210                 }else{
22211                     parent = el.offsetParent;
22212                 }
22213                 if (parent) {
22214                     return true;
22215                 }
22216             }
22217
22218             return false;
22219         },
22220
22221         /**
22222          * Returns a Region object containing the drag and drop element's position
22223          * and size, including the padding configured for it
22224          * @method getLocation
22225          * @param {DragDrop} oDD the drag and drop object to get the
22226          *                       location for
22227          * @return {Roo.lib.Region} a Region object representing the total area
22228          *                             the element occupies, including any padding
22229          *                             the instance is configured for.
22230          * @static
22231          */
22232         getLocation: function(oDD) {
22233             if (! this.isTypeOfDD(oDD)) {
22234                 return null;
22235             }
22236
22237             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
22238
22239             try {
22240                 pos= Roo.lib.Dom.getXY(el);
22241             } catch (e) { }
22242
22243             if (!pos) {
22244                 return null;
22245             }
22246
22247             x1 = pos[0];
22248             x2 = x1 + el.offsetWidth;
22249             y1 = pos[1];
22250             y2 = y1 + el.offsetHeight;
22251
22252             t = y1 - oDD.padding[0];
22253             r = x2 + oDD.padding[1];
22254             b = y2 + oDD.padding[2];
22255             l = x1 - oDD.padding[3];
22256
22257             return new Roo.lib.Region( t, r, b, l );
22258         },
22259
22260         /**
22261          * Checks the cursor location to see if it over the target
22262          * @method isOverTarget
22263          * @param {Roo.lib.Point} pt The point to evaluate
22264          * @param {DragDrop} oTarget the DragDrop object we are inspecting
22265          * @return {boolean} true if the mouse is over the target
22266          * @private
22267          * @static
22268          */
22269         isOverTarget: function(pt, oTarget, intersect) {
22270             // use cache if available
22271             var loc = this.locationCache[oTarget.id];
22272             if (!loc || !this.useCache) {
22273                 loc = this.getLocation(oTarget);
22274                 this.locationCache[oTarget.id] = loc;
22275
22276             }
22277
22278             if (!loc) {
22279                 return false;
22280             }
22281
22282             oTarget.cursorIsOver = loc.contains( pt );
22283
22284             // DragDrop is using this as a sanity check for the initial mousedown
22285             // in this case we are done.  In POINT mode, if the drag obj has no
22286             // contraints, we are also done. Otherwise we need to evaluate the
22287             // location of the target as related to the actual location of the
22288             // dragged element.
22289             var dc = this.dragCurrent;
22290             if (!dc || !dc.getTargetCoord ||
22291                     (!intersect && !dc.constrainX && !dc.constrainY)) {
22292                 return oTarget.cursorIsOver;
22293             }
22294
22295             oTarget.overlap = null;
22296
22297             // Get the current location of the drag element, this is the
22298             // location of the mouse event less the delta that represents
22299             // where the original mousedown happened on the element.  We
22300             // need to consider constraints and ticks as well.
22301             var pos = dc.getTargetCoord(pt.x, pt.y);
22302
22303             var el = dc.getDragEl();
22304             var curRegion = new Roo.lib.Region( pos.y,
22305                                                    pos.x + el.offsetWidth,
22306                                                    pos.y + el.offsetHeight,
22307                                                    pos.x );
22308
22309             var overlap = curRegion.intersect(loc);
22310
22311             if (overlap) {
22312                 oTarget.overlap = overlap;
22313                 return (intersect) ? true : oTarget.cursorIsOver;
22314             } else {
22315                 return false;
22316             }
22317         },
22318
22319         /**
22320          * unload event handler
22321          * @method _onUnload
22322          * @private
22323          * @static
22324          */
22325         _onUnload: function(e, me) {
22326             Roo.dd.DragDropMgr.unregAll();
22327         },
22328
22329         /**
22330          * Cleans up the drag and drop events and objects.
22331          * @method unregAll
22332          * @private
22333          * @static
22334          */
22335         unregAll: function() {
22336
22337             if (this.dragCurrent) {
22338                 this.stopDrag();
22339                 this.dragCurrent = null;
22340             }
22341
22342             this._execOnAll("unreg", []);
22343
22344             for (i in this.elementCache) {
22345                 delete this.elementCache[i];
22346             }
22347
22348             this.elementCache = {};
22349             this.ids = {};
22350         },
22351
22352         /**
22353          * A cache of DOM elements
22354          * @property elementCache
22355          * @private
22356          * @static
22357          */
22358         elementCache: {},
22359
22360         /**
22361          * Get the wrapper for the DOM element specified
22362          * @method getElWrapper
22363          * @param {String} id the id of the element to get
22364          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
22365          * @private
22366          * @deprecated This wrapper isn't that useful
22367          * @static
22368          */
22369         getElWrapper: function(id) {
22370             var oWrapper = this.elementCache[id];
22371             if (!oWrapper || !oWrapper.el) {
22372                 oWrapper = this.elementCache[id] =
22373                     new this.ElementWrapper(Roo.getDom(id));
22374             }
22375             return oWrapper;
22376         },
22377
22378         /**
22379          * Returns the actual DOM element
22380          * @method getElement
22381          * @param {String} id the id of the elment to get
22382          * @return {Object} The element
22383          * @deprecated use Roo.getDom instead
22384          * @static
22385          */
22386         getElement: function(id) {
22387             return Roo.getDom(id);
22388         },
22389
22390         /**
22391          * Returns the style property for the DOM element (i.e.,
22392          * document.getElById(id).style)
22393          * @method getCss
22394          * @param {String} id the id of the elment to get
22395          * @return {Object} The style property of the element
22396          * @deprecated use Roo.getDom instead
22397          * @static
22398          */
22399         getCss: function(id) {
22400             var el = Roo.getDom(id);
22401             return (el) ? el.style : null;
22402         },
22403
22404         /**
22405          * Inner class for cached elements
22406          * @class DragDropMgr.ElementWrapper
22407          * @for DragDropMgr
22408          * @private
22409          * @deprecated
22410          */
22411         ElementWrapper: function(el) {
22412                 /**
22413                  * The element
22414                  * @property el
22415                  */
22416                 this.el = el || null;
22417                 /**
22418                  * The element id
22419                  * @property id
22420                  */
22421                 this.id = this.el && el.id;
22422                 /**
22423                  * A reference to the style property
22424                  * @property css
22425                  */
22426                 this.css = this.el && el.style;
22427             },
22428
22429         /**
22430          * Returns the X position of an html element
22431          * @method getPosX
22432          * @param el the element for which to get the position
22433          * @return {int} the X coordinate
22434          * @for DragDropMgr
22435          * @deprecated use Roo.lib.Dom.getX instead
22436          * @static
22437          */
22438         getPosX: function(el) {
22439             return Roo.lib.Dom.getX(el);
22440         },
22441
22442         /**
22443          * Returns the Y position of an html element
22444          * @method getPosY
22445          * @param el the element for which to get the position
22446          * @return {int} the Y coordinate
22447          * @deprecated use Roo.lib.Dom.getY instead
22448          * @static
22449          */
22450         getPosY: function(el) {
22451             return Roo.lib.Dom.getY(el);
22452         },
22453
22454         /**
22455          * Swap two nodes.  In IE, we use the native method, for others we
22456          * emulate the IE behavior
22457          * @method swapNode
22458          * @param n1 the first node to swap
22459          * @param n2 the other node to swap
22460          * @static
22461          */
22462         swapNode: function(n1, n2) {
22463             if (n1.swapNode) {
22464                 n1.swapNode(n2);
22465             } else {
22466                 var p = n2.parentNode;
22467                 var s = n2.nextSibling;
22468
22469                 if (s == n1) {
22470                     p.insertBefore(n1, n2);
22471                 } else if (n2 == n1.nextSibling) {
22472                     p.insertBefore(n2, n1);
22473                 } else {
22474                     n1.parentNode.replaceChild(n2, n1);
22475                     p.insertBefore(n1, s);
22476                 }
22477             }
22478         },
22479
22480         /**
22481          * Returns the current scroll position
22482          * @method getScroll
22483          * @private
22484          * @static
22485          */
22486         getScroll: function () {
22487             var t, l, dde=document.documentElement, db=document.body;
22488             if (dde && (dde.scrollTop || dde.scrollLeft)) {
22489                 t = dde.scrollTop;
22490                 l = dde.scrollLeft;
22491             } else if (db) {
22492                 t = db.scrollTop;
22493                 l = db.scrollLeft;
22494             } else {
22495
22496             }
22497             return { top: t, left: l };
22498         },
22499
22500         /**
22501          * Returns the specified element style property
22502          * @method getStyle
22503          * @param {HTMLElement} el          the element
22504          * @param {string}      styleProp   the style property
22505          * @return {string} The value of the style property
22506          * @deprecated use Roo.lib.Dom.getStyle
22507          * @static
22508          */
22509         getStyle: function(el, styleProp) {
22510             return Roo.fly(el).getStyle(styleProp);
22511         },
22512
22513         /**
22514          * Gets the scrollTop
22515          * @method getScrollTop
22516          * @return {int} the document's scrollTop
22517          * @static
22518          */
22519         getScrollTop: function () { return this.getScroll().top; },
22520
22521         /**
22522          * Gets the scrollLeft
22523          * @method getScrollLeft
22524          * @return {int} the document's scrollTop
22525          * @static
22526          */
22527         getScrollLeft: function () { return this.getScroll().left; },
22528
22529         /**
22530          * Sets the x/y position of an element to the location of the
22531          * target element.
22532          * @method moveToEl
22533          * @param {HTMLElement} moveEl      The element to move
22534          * @param {HTMLElement} targetEl    The position reference element
22535          * @static
22536          */
22537         moveToEl: function (moveEl, targetEl) {
22538             var aCoord = Roo.lib.Dom.getXY(targetEl);
22539             Roo.lib.Dom.setXY(moveEl, aCoord);
22540         },
22541
22542         /**
22543          * Numeric array sort function
22544          * @method numericSort
22545          * @static
22546          */
22547         numericSort: function(a, b) { return (a - b); },
22548
22549         /**
22550          * Internal counter
22551          * @property _timeoutCount
22552          * @private
22553          * @static
22554          */
22555         _timeoutCount: 0,
22556
22557         /**
22558          * Trying to make the load order less important.  Without this we get
22559          * an error if this file is loaded before the Event Utility.
22560          * @method _addListeners
22561          * @private
22562          * @static
22563          */
22564         _addListeners: function() {
22565             var DDM = Roo.dd.DDM;
22566             if ( Roo.lib.Event && document ) {
22567                 DDM._onLoad();
22568             } else {
22569                 if (DDM._timeoutCount > 2000) {
22570                 } else {
22571                     setTimeout(DDM._addListeners, 10);
22572                     if (document && document.body) {
22573                         DDM._timeoutCount += 1;
22574                     }
22575                 }
22576             }
22577         },
22578
22579         /**
22580          * Recursively searches the immediate parent and all child nodes for
22581          * the handle element in order to determine wheter or not it was
22582          * clicked.
22583          * @method handleWasClicked
22584          * @param node the html element to inspect
22585          * @static
22586          */
22587         handleWasClicked: function(node, id) {
22588             if (this.isHandle(id, node.id)) {
22589                 return true;
22590             } else {
22591                 // check to see if this is a text node child of the one we want
22592                 var p = node.parentNode;
22593
22594                 while (p) {
22595                     if (this.isHandle(id, p.id)) {
22596                         return true;
22597                     } else {
22598                         p = p.parentNode;
22599                     }
22600                 }
22601             }
22602
22603             return false;
22604         }
22605
22606     };
22607
22608 }();
22609
22610 // shorter alias, save a few bytes
22611 Roo.dd.DDM = Roo.dd.DragDropMgr;
22612 Roo.dd.DDM._addListeners();
22613
22614 }/*
22615  * Based on:
22616  * Ext JS Library 1.1.1
22617  * Copyright(c) 2006-2007, Ext JS, LLC.
22618  *
22619  * Originally Released Under LGPL - original licence link has changed is not relivant.
22620  *
22621  * Fork - LGPL
22622  * <script type="text/javascript">
22623  */
22624
22625 /**
22626  * @class Roo.dd.DD
22627  * A DragDrop implementation where the linked element follows the
22628  * mouse cursor during a drag.
22629  * @extends Roo.dd.DragDrop
22630  * @constructor
22631  * @param {String} id the id of the linked element
22632  * @param {String} sGroup the group of related DragDrop items
22633  * @param {object} config an object containing configurable attributes
22634  *                Valid properties for DD:
22635  *                    scroll
22636  */
22637 Roo.dd.DD = function(id, sGroup, config) {
22638     if (id) {
22639         this.init(id, sGroup, config);
22640     }
22641 };
22642
22643 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
22644
22645     /**
22646      * When set to true, the utility automatically tries to scroll the browser
22647      * window wehn a drag and drop element is dragged near the viewport boundary.
22648      * Defaults to true.
22649      * @property scroll
22650      * @type boolean
22651      */
22652     scroll: true,
22653
22654     /**
22655      * Sets the pointer offset to the distance between the linked element's top
22656      * left corner and the location the element was clicked
22657      * @method autoOffset
22658      * @param {int} iPageX the X coordinate of the click
22659      * @param {int} iPageY the Y coordinate of the click
22660      */
22661     autoOffset: function(iPageX, iPageY) {
22662         var x = iPageX - this.startPageX;
22663         var y = iPageY - this.startPageY;
22664         this.setDelta(x, y);
22665     },
22666
22667     /**
22668      * Sets the pointer offset.  You can call this directly to force the
22669      * offset to be in a particular location (e.g., pass in 0,0 to set it
22670      * to the center of the object)
22671      * @method setDelta
22672      * @param {int} iDeltaX the distance from the left
22673      * @param {int} iDeltaY the distance from the top
22674      */
22675     setDelta: function(iDeltaX, iDeltaY) {
22676         this.deltaX = iDeltaX;
22677         this.deltaY = iDeltaY;
22678     },
22679
22680     /**
22681      * Sets the drag element to the location of the mousedown or click event,
22682      * maintaining the cursor location relative to the location on the element
22683      * that was clicked.  Override this if you want to place the element in a
22684      * location other than where the cursor is.
22685      * @method setDragElPos
22686      * @param {int} iPageX the X coordinate of the mousedown or drag event
22687      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22688      */
22689     setDragElPos: function(iPageX, iPageY) {
22690         // the first time we do this, we are going to check to make sure
22691         // the element has css positioning
22692
22693         var el = this.getDragEl();
22694         this.alignElWithMouse(el, iPageX, iPageY);
22695     },
22696
22697     /**
22698      * Sets the element to the location of the mousedown or click event,
22699      * maintaining the cursor location relative to the location on the element
22700      * that was clicked.  Override this if you want to place the element in a
22701      * location other than where the cursor is.
22702      * @method alignElWithMouse
22703      * @param {HTMLElement} el the element to move
22704      * @param {int} iPageX the X coordinate of the mousedown or drag event
22705      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22706      */
22707     alignElWithMouse: function(el, iPageX, iPageY) {
22708         var oCoord = this.getTargetCoord(iPageX, iPageY);
22709         var fly = el.dom ? el : Roo.fly(el);
22710         if (!this.deltaSetXY) {
22711             var aCoord = [oCoord.x, oCoord.y];
22712             fly.setXY(aCoord);
22713             var newLeft = fly.getLeft(true);
22714             var newTop  = fly.getTop(true);
22715             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
22716         } else {
22717             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
22718         }
22719
22720         this.cachePosition(oCoord.x, oCoord.y);
22721         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
22722         return oCoord;
22723     },
22724
22725     /**
22726      * Saves the most recent position so that we can reset the constraints and
22727      * tick marks on-demand.  We need to know this so that we can calculate the
22728      * number of pixels the element is offset from its original position.
22729      * @method cachePosition
22730      * @param iPageX the current x position (optional, this just makes it so we
22731      * don't have to look it up again)
22732      * @param iPageY the current y position (optional, this just makes it so we
22733      * don't have to look it up again)
22734      */
22735     cachePosition: function(iPageX, iPageY) {
22736         if (iPageX) {
22737             this.lastPageX = iPageX;
22738             this.lastPageY = iPageY;
22739         } else {
22740             var aCoord = Roo.lib.Dom.getXY(this.getEl());
22741             this.lastPageX = aCoord[0];
22742             this.lastPageY = aCoord[1];
22743         }
22744     },
22745
22746     /**
22747      * Auto-scroll the window if the dragged object has been moved beyond the
22748      * visible window boundary.
22749      * @method autoScroll
22750      * @param {int} x the drag element's x position
22751      * @param {int} y the drag element's y position
22752      * @param {int} h the height of the drag element
22753      * @param {int} w the width of the drag element
22754      * @private
22755      */
22756     autoScroll: function(x, y, h, w) {
22757
22758         if (this.scroll) {
22759             // The client height
22760             var clientH = Roo.lib.Dom.getViewWidth();
22761
22762             // The client width
22763             var clientW = Roo.lib.Dom.getViewHeight();
22764
22765             // The amt scrolled down
22766             var st = this.DDM.getScrollTop();
22767
22768             // The amt scrolled right
22769             var sl = this.DDM.getScrollLeft();
22770
22771             // Location of the bottom of the element
22772             var bot = h + y;
22773
22774             // Location of the right of the element
22775             var right = w + x;
22776
22777             // The distance from the cursor to the bottom of the visible area,
22778             // adjusted so that we don't scroll if the cursor is beyond the
22779             // element drag constraints
22780             var toBot = (clientH + st - y - this.deltaY);
22781
22782             // The distance from the cursor to the right of the visible area
22783             var toRight = (clientW + sl - x - this.deltaX);
22784
22785
22786             // How close to the edge the cursor must be before we scroll
22787             // var thresh = (document.all) ? 100 : 40;
22788             var thresh = 40;
22789
22790             // How many pixels to scroll per autoscroll op.  This helps to reduce
22791             // clunky scrolling. IE is more sensitive about this ... it needs this
22792             // value to be higher.
22793             var scrAmt = (document.all) ? 80 : 30;
22794
22795             // Scroll down if we are near the bottom of the visible page and the
22796             // obj extends below the crease
22797             if ( bot > clientH && toBot < thresh ) {
22798                 window.scrollTo(sl, st + scrAmt);
22799             }
22800
22801             // Scroll up if the window is scrolled down and the top of the object
22802             // goes above the top border
22803             if ( y < st && st > 0 && y - st < thresh ) {
22804                 window.scrollTo(sl, st - scrAmt);
22805             }
22806
22807             // Scroll right if the obj is beyond the right border and the cursor is
22808             // near the border.
22809             if ( right > clientW && toRight < thresh ) {
22810                 window.scrollTo(sl + scrAmt, st);
22811             }
22812
22813             // Scroll left if the window has been scrolled to the right and the obj
22814             // extends past the left border
22815             if ( x < sl && sl > 0 && x - sl < thresh ) {
22816                 window.scrollTo(sl - scrAmt, st);
22817             }
22818         }
22819     },
22820
22821     /**
22822      * Finds the location the element should be placed if we want to move
22823      * it to where the mouse location less the click offset would place us.
22824      * @method getTargetCoord
22825      * @param {int} iPageX the X coordinate of the click
22826      * @param {int} iPageY the Y coordinate of the click
22827      * @return an object that contains the coordinates (Object.x and Object.y)
22828      * @private
22829      */
22830     getTargetCoord: function(iPageX, iPageY) {
22831
22832
22833         var x = iPageX - this.deltaX;
22834         var y = iPageY - this.deltaY;
22835
22836         if (this.constrainX) {
22837             if (x < this.minX) { x = this.minX; }
22838             if (x > this.maxX) { x = this.maxX; }
22839         }
22840
22841         if (this.constrainY) {
22842             if (y < this.minY) { y = this.minY; }
22843             if (y > this.maxY) { y = this.maxY; }
22844         }
22845
22846         x = this.getTick(x, this.xTicks);
22847         y = this.getTick(y, this.yTicks);
22848
22849
22850         return {x:x, y:y};
22851     },
22852
22853     /*
22854      * Sets up config options specific to this class. Overrides
22855      * Roo.dd.DragDrop, but all versions of this method through the
22856      * inheritance chain are called
22857      */
22858     applyConfig: function() {
22859         Roo.dd.DD.superclass.applyConfig.call(this);
22860         this.scroll = (this.config.scroll !== false);
22861     },
22862
22863     /*
22864      * Event that fires prior to the onMouseDown event.  Overrides
22865      * Roo.dd.DragDrop.
22866      */
22867     b4MouseDown: function(e) {
22868         // this.resetConstraints();
22869         this.autoOffset(e.getPageX(),
22870                             e.getPageY());
22871     },
22872
22873     /*
22874      * Event that fires prior to the onDrag event.  Overrides
22875      * Roo.dd.DragDrop.
22876      */
22877     b4Drag: function(e) {
22878         this.setDragElPos(e.getPageX(),
22879                             e.getPageY());
22880     },
22881
22882     toString: function() {
22883         return ("DD " + this.id);
22884     }
22885
22886     //////////////////////////////////////////////////////////////////////////
22887     // Debugging ygDragDrop events that can be overridden
22888     //////////////////////////////////////////////////////////////////////////
22889     /*
22890     startDrag: function(x, y) {
22891     },
22892
22893     onDrag: function(e) {
22894     },
22895
22896     onDragEnter: function(e, id) {
22897     },
22898
22899     onDragOver: function(e, id) {
22900     },
22901
22902     onDragOut: function(e, id) {
22903     },
22904
22905     onDragDrop: function(e, id) {
22906     },
22907
22908     endDrag: function(e) {
22909     }
22910
22911     */
22912
22913 });/*
22914  * Based on:
22915  * Ext JS Library 1.1.1
22916  * Copyright(c) 2006-2007, Ext JS, LLC.
22917  *
22918  * Originally Released Under LGPL - original licence link has changed is not relivant.
22919  *
22920  * Fork - LGPL
22921  * <script type="text/javascript">
22922  */
22923
22924 /**
22925  * @class Roo.dd.DDProxy
22926  * A DragDrop implementation that inserts an empty, bordered div into
22927  * the document that follows the cursor during drag operations.  At the time of
22928  * the click, the frame div is resized to the dimensions of the linked html
22929  * element, and moved to the exact location of the linked element.
22930  *
22931  * References to the "frame" element refer to the single proxy element that
22932  * was created to be dragged in place of all DDProxy elements on the
22933  * page.
22934  *
22935  * @extends Roo.dd.DD
22936  * @constructor
22937  * @param {String} id the id of the linked html element
22938  * @param {String} sGroup the group of related DragDrop objects
22939  * @param {object} config an object containing configurable attributes
22940  *                Valid properties for DDProxy in addition to those in DragDrop:
22941  *                   resizeFrame, centerFrame, dragElId
22942  */
22943 Roo.dd.DDProxy = function(id, sGroup, config) {
22944     if (id) {
22945         this.init(id, sGroup, config);
22946         this.initFrame();
22947     }
22948 };
22949
22950 /**
22951  * The default drag frame div id
22952  * @property Roo.dd.DDProxy.dragElId
22953  * @type String
22954  * @static
22955  */
22956 Roo.dd.DDProxy.dragElId = "ygddfdiv";
22957
22958 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
22959
22960     /**
22961      * By default we resize the drag frame to be the same size as the element
22962      * we want to drag (this is to get the frame effect).  We can turn it off
22963      * if we want a different behavior.
22964      * @property resizeFrame
22965      * @type boolean
22966      */
22967     resizeFrame: true,
22968
22969     /**
22970      * By default the frame is positioned exactly where the drag element is, so
22971      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
22972      * you do not have constraints on the obj is to have the drag frame centered
22973      * around the cursor.  Set centerFrame to true for this effect.
22974      * @property centerFrame
22975      * @type boolean
22976      */
22977     centerFrame: false,
22978
22979     /**
22980      * Creates the proxy element if it does not yet exist
22981      * @method createFrame
22982      */
22983     createFrame: function() {
22984         var self = this;
22985         var body = document.body;
22986
22987         if (!body || !body.firstChild) {
22988             setTimeout( function() { self.createFrame(); }, 50 );
22989             return;
22990         }
22991
22992         var div = this.getDragEl();
22993
22994         if (!div) {
22995             div    = document.createElement("div");
22996             div.id = this.dragElId;
22997             var s  = div.style;
22998
22999             s.position   = "absolute";
23000             s.visibility = "hidden";
23001             s.cursor     = "move";
23002             s.border     = "2px solid #aaa";
23003             s.zIndex     = 999;
23004
23005             // appendChild can blow up IE if invoked prior to the window load event
23006             // while rendering a table.  It is possible there are other scenarios
23007             // that would cause this to happen as well.
23008             body.insertBefore(div, body.firstChild);
23009         }
23010     },
23011
23012     /**
23013      * Initialization for the drag frame element.  Must be called in the
23014      * constructor of all subclasses
23015      * @method initFrame
23016      */
23017     initFrame: function() {
23018         this.createFrame();
23019     },
23020
23021     applyConfig: function() {
23022         Roo.dd.DDProxy.superclass.applyConfig.call(this);
23023
23024         this.resizeFrame = (this.config.resizeFrame !== false);
23025         this.centerFrame = (this.config.centerFrame);
23026         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
23027     },
23028
23029     /**
23030      * Resizes the drag frame to the dimensions of the clicked object, positions
23031      * it over the object, and finally displays it
23032      * @method showFrame
23033      * @param {int} iPageX X click position
23034      * @param {int} iPageY Y click position
23035      * @private
23036      */
23037     showFrame: function(iPageX, iPageY) {
23038         var el = this.getEl();
23039         var dragEl = this.getDragEl();
23040         var s = dragEl.style;
23041
23042         this._resizeProxy();
23043
23044         if (this.centerFrame) {
23045             this.setDelta( Math.round(parseInt(s.width,  10)/2),
23046                            Math.round(parseInt(s.height, 10)/2) );
23047         }
23048
23049         this.setDragElPos(iPageX, iPageY);
23050
23051         Roo.fly(dragEl).show();
23052     },
23053
23054     /**
23055      * The proxy is automatically resized to the dimensions of the linked
23056      * element when a drag is initiated, unless resizeFrame is set to false
23057      * @method _resizeProxy
23058      * @private
23059      */
23060     _resizeProxy: function() {
23061         if (this.resizeFrame) {
23062             var el = this.getEl();
23063             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
23064         }
23065     },
23066
23067     // overrides Roo.dd.DragDrop
23068     b4MouseDown: function(e) {
23069         var x = e.getPageX();
23070         var y = e.getPageY();
23071         this.autoOffset(x, y);
23072         this.setDragElPos(x, y);
23073     },
23074
23075     // overrides Roo.dd.DragDrop
23076     b4StartDrag: function(x, y) {
23077         // show the drag frame
23078         this.showFrame(x, y);
23079     },
23080
23081     // overrides Roo.dd.DragDrop
23082     b4EndDrag: function(e) {
23083         Roo.fly(this.getDragEl()).hide();
23084     },
23085
23086     // overrides Roo.dd.DragDrop
23087     // By default we try to move the element to the last location of the frame.
23088     // This is so that the default behavior mirrors that of Roo.dd.DD.
23089     endDrag: function(e) {
23090
23091         var lel = this.getEl();
23092         var del = this.getDragEl();
23093
23094         // Show the drag frame briefly so we can get its position
23095         del.style.visibility = "";
23096
23097         this.beforeMove();
23098         // Hide the linked element before the move to get around a Safari
23099         // rendering bug.
23100         lel.style.visibility = "hidden";
23101         Roo.dd.DDM.moveToEl(lel, del);
23102         del.style.visibility = "hidden";
23103         lel.style.visibility = "";
23104
23105         this.afterDrag();
23106     },
23107
23108     beforeMove : function(){
23109
23110     },
23111
23112     afterDrag : function(){
23113
23114     },
23115
23116     toString: function() {
23117         return ("DDProxy " + this.id);
23118     }
23119
23120 });
23121 /*
23122  * Based on:
23123  * Ext JS Library 1.1.1
23124  * Copyright(c) 2006-2007, Ext JS, LLC.
23125  *
23126  * Originally Released Under LGPL - original licence link has changed is not relivant.
23127  *
23128  * Fork - LGPL
23129  * <script type="text/javascript">
23130  */
23131
23132  /**
23133  * @class Roo.dd.DDTarget
23134  * A DragDrop implementation that does not move, but can be a drop
23135  * target.  You would get the same result by simply omitting implementation
23136  * for the event callbacks, but this way we reduce the processing cost of the
23137  * event listener and the callbacks.
23138  * @extends Roo.dd.DragDrop
23139  * @constructor
23140  * @param {String} id the id of the element that is a drop target
23141  * @param {String} sGroup the group of related DragDrop objects
23142  * @param {object} config an object containing configurable attributes
23143  *                 Valid properties for DDTarget in addition to those in
23144  *                 DragDrop:
23145  *                    none
23146  */
23147 Roo.dd.DDTarget = function(id, sGroup, config) {
23148     if (id) {
23149         this.initTarget(id, sGroup, config);
23150     }
23151     if (config && (config.listeners || config.events)) { 
23152         Roo.dd.DragDrop.superclass.constructor.call(this,  { 
23153             listeners : config.listeners || {}, 
23154             events : config.events || {} 
23155         });    
23156     }
23157 };
23158
23159 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
23160 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
23161     toString: function() {
23162         return ("DDTarget " + this.id);
23163     }
23164 });
23165 /*
23166  * Based on:
23167  * Ext JS Library 1.1.1
23168  * Copyright(c) 2006-2007, Ext JS, LLC.
23169  *
23170  * Originally Released Under LGPL - original licence link has changed is not relivant.
23171  *
23172  * Fork - LGPL
23173  * <script type="text/javascript">
23174  */
23175  
23176
23177 /**
23178  * @class Roo.dd.ScrollManager
23179  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
23180  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
23181  * @static
23182  */
23183 Roo.dd.ScrollManager = function(){
23184     var ddm = Roo.dd.DragDropMgr;
23185     var els = {};
23186     var dragEl = null;
23187     var proc = {};
23188     
23189     
23190     
23191     var onStop = function(e){
23192         dragEl = null;
23193         clearProc();
23194     };
23195     
23196     var triggerRefresh = function(){
23197         if(ddm.dragCurrent){
23198              ddm.refreshCache(ddm.dragCurrent.groups);
23199         }
23200     };
23201     
23202     var doScroll = function(){
23203         if(ddm.dragCurrent){
23204             var dds = Roo.dd.ScrollManager;
23205             if(!dds.animate){
23206                 if(proc.el.scroll(proc.dir, dds.increment)){
23207                     triggerRefresh();
23208                 }
23209             }else{
23210                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
23211             }
23212         }
23213     };
23214     
23215     var clearProc = function(){
23216         if(proc.id){
23217             clearInterval(proc.id);
23218         }
23219         proc.id = 0;
23220         proc.el = null;
23221         proc.dir = "";
23222     };
23223     
23224     var startProc = function(el, dir){
23225          Roo.log('scroll startproc');
23226         clearProc();
23227         proc.el = el;
23228         proc.dir = dir;
23229         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
23230     };
23231     
23232     var onFire = function(e, isDrop){
23233        
23234         if(isDrop || !ddm.dragCurrent){ return; }
23235         var dds = Roo.dd.ScrollManager;
23236         if(!dragEl || dragEl != ddm.dragCurrent){
23237             dragEl = ddm.dragCurrent;
23238             // refresh regions on drag start
23239             dds.refreshCache();
23240         }
23241         
23242         var xy = Roo.lib.Event.getXY(e);
23243         var pt = new Roo.lib.Point(xy[0], xy[1]);
23244         for(var id in els){
23245             var el = els[id], r = el._region;
23246             if(r && r.contains(pt) && el.isScrollable()){
23247                 if(r.bottom - pt.y <= dds.thresh){
23248                     if(proc.el != el){
23249                         startProc(el, "down");
23250                     }
23251                     return;
23252                 }else if(r.right - pt.x <= dds.thresh){
23253                     if(proc.el != el){
23254                         startProc(el, "left");
23255                     }
23256                     return;
23257                 }else if(pt.y - r.top <= dds.thresh){
23258                     if(proc.el != el){
23259                         startProc(el, "up");
23260                     }
23261                     return;
23262                 }else if(pt.x - r.left <= dds.thresh){
23263                     if(proc.el != el){
23264                         startProc(el, "right");
23265                     }
23266                     return;
23267                 }
23268             }
23269         }
23270         clearProc();
23271     };
23272     
23273     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
23274     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
23275     
23276     return {
23277         /**
23278          * Registers new overflow element(s) to auto scroll
23279          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
23280          */
23281         register : function(el){
23282             if(el instanceof Array){
23283                 for(var i = 0, len = el.length; i < len; i++) {
23284                         this.register(el[i]);
23285                 }
23286             }else{
23287                 el = Roo.get(el);
23288                 els[el.id] = el;
23289             }
23290             Roo.dd.ScrollManager.els = els;
23291         },
23292         
23293         /**
23294          * Unregisters overflow element(s) so they are no longer scrolled
23295          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
23296          */
23297         unregister : function(el){
23298             if(el instanceof Array){
23299                 for(var i = 0, len = el.length; i < len; i++) {
23300                         this.unregister(el[i]);
23301                 }
23302             }else{
23303                 el = Roo.get(el);
23304                 delete els[el.id];
23305             }
23306         },
23307         
23308         /**
23309          * The number of pixels from the edge of a container the pointer needs to be to 
23310          * trigger scrolling (defaults to 25)
23311          * @type Number
23312          */
23313         thresh : 25,
23314         
23315         /**
23316          * The number of pixels to scroll in each scroll increment (defaults to 50)
23317          * @type Number
23318          */
23319         increment : 100,
23320         
23321         /**
23322          * The frequency of scrolls in milliseconds (defaults to 500)
23323          * @type Number
23324          */
23325         frequency : 500,
23326         
23327         /**
23328          * True to animate the scroll (defaults to true)
23329          * @type Boolean
23330          */
23331         animate: true,
23332         
23333         /**
23334          * The animation duration in seconds - 
23335          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
23336          * @type Number
23337          */
23338         animDuration: .4,
23339         
23340         /**
23341          * Manually trigger a cache refresh.
23342          */
23343         refreshCache : function(){
23344             for(var id in els){
23345                 if(typeof els[id] == 'object'){ // for people extending the object prototype
23346                     els[id]._region = els[id].getRegion();
23347                 }
23348             }
23349         }
23350     };
23351 }();/*
23352  * Based on:
23353  * Ext JS Library 1.1.1
23354  * Copyright(c) 2006-2007, Ext JS, LLC.
23355  *
23356  * Originally Released Under LGPL - original licence link has changed is not relivant.
23357  *
23358  * Fork - LGPL
23359  * <script type="text/javascript">
23360  */
23361  
23362
23363 /**
23364  * @class Roo.dd.Registry
23365  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
23366  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
23367  * @static
23368  */
23369 Roo.dd.Registry = function(){
23370     var elements = {}; 
23371     var handles = {}; 
23372     var autoIdSeed = 0;
23373
23374     var getId = function(el, autogen){
23375         if(typeof el == "string"){
23376             return el;
23377         }
23378         var id = el.id;
23379         if(!id && autogen !== false){
23380             id = "roodd-" + (++autoIdSeed);
23381             el.id = id;
23382         }
23383         return id;
23384     };
23385     
23386     return {
23387     /**
23388      * Register a drag drop element
23389      * @param {String|HTMLElement} element The id or DOM node to register
23390      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
23391      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
23392      * knows how to interpret, plus there are some specific properties known to the Registry that should be
23393      * populated in the data object (if applicable):
23394      * <pre>
23395 Value      Description<br />
23396 ---------  ------------------------------------------<br />
23397 handles    Array of DOM nodes that trigger dragging<br />
23398            for the element being registered<br />
23399 isHandle   True if the element passed in triggers<br />
23400            dragging itself, else false
23401 </pre>
23402      */
23403         register : function(el, data){
23404             data = data || {};
23405             if(typeof el == "string"){
23406                 el = document.getElementById(el);
23407             }
23408             data.ddel = el;
23409             elements[getId(el)] = data;
23410             if(data.isHandle !== false){
23411                 handles[data.ddel.id] = data;
23412             }
23413             if(data.handles){
23414                 var hs = data.handles;
23415                 for(var i = 0, len = hs.length; i < len; i++){
23416                         handles[getId(hs[i])] = data;
23417                 }
23418             }
23419         },
23420
23421     /**
23422      * Unregister a drag drop element
23423      * @param {String|HTMLElement}  element The id or DOM node to unregister
23424      */
23425         unregister : function(el){
23426             var id = getId(el, false);
23427             var data = elements[id];
23428             if(data){
23429                 delete elements[id];
23430                 if(data.handles){
23431                     var hs = data.handles;
23432                     for(var i = 0, len = hs.length; i < len; i++){
23433                         delete handles[getId(hs[i], false)];
23434                     }
23435                 }
23436             }
23437         },
23438
23439     /**
23440      * Returns the handle registered for a DOM Node by id
23441      * @param {String|HTMLElement} id The DOM node or id to look up
23442      * @return {Object} handle The custom handle data
23443      */
23444         getHandle : function(id){
23445             if(typeof id != "string"){ // must be element?
23446                 id = id.id;
23447             }
23448             return handles[id];
23449         },
23450
23451     /**
23452      * Returns the handle that is registered for the DOM node that is the target of the event
23453      * @param {Event} e The event
23454      * @return {Object} handle The custom handle data
23455      */
23456         getHandleFromEvent : function(e){
23457             var t = Roo.lib.Event.getTarget(e);
23458             return t ? handles[t.id] : null;
23459         },
23460
23461     /**
23462      * Returns a custom data object that is registered for a DOM node by id
23463      * @param {String|HTMLElement} id The DOM node or id to look up
23464      * @return {Object} data The custom data
23465      */
23466         getTarget : function(id){
23467             if(typeof id != "string"){ // must be element?
23468                 id = id.id;
23469             }
23470             return elements[id];
23471         },
23472
23473     /**
23474      * Returns a custom data object that is registered for the DOM node that is the target of the event
23475      * @param {Event} e The event
23476      * @return {Object} data The custom data
23477      */
23478         getTargetFromEvent : function(e){
23479             var t = Roo.lib.Event.getTarget(e);
23480             return t ? elements[t.id] || handles[t.id] : null;
23481         }
23482     };
23483 }();/*
23484  * Based on:
23485  * Ext JS Library 1.1.1
23486  * Copyright(c) 2006-2007, Ext JS, LLC.
23487  *
23488  * Originally Released Under LGPL - original licence link has changed is not relivant.
23489  *
23490  * Fork - LGPL
23491  * <script type="text/javascript">
23492  */
23493  
23494
23495 /**
23496  * @class Roo.dd.StatusProxy
23497  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
23498  * default drag proxy used by all Roo.dd components.
23499  * @constructor
23500  * @param {Object} config
23501  */
23502 Roo.dd.StatusProxy = function(config){
23503     Roo.apply(this, config);
23504     this.id = this.id || Roo.id();
23505     this.el = new Roo.Layer({
23506         dh: {
23507             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
23508                 {tag: "div", cls: "x-dd-drop-icon"},
23509                 {tag: "div", cls: "x-dd-drag-ghost"}
23510             ]
23511         }, 
23512         shadow: !config || config.shadow !== false
23513     });
23514     this.ghost = Roo.get(this.el.dom.childNodes[1]);
23515     this.dropStatus = this.dropNotAllowed;
23516 };
23517
23518 Roo.dd.StatusProxy.prototype = {
23519     /**
23520      * @cfg {String} dropAllowed
23521      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
23522      */
23523     dropAllowed : "x-dd-drop-ok",
23524     /**
23525      * @cfg {String} dropNotAllowed
23526      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
23527      */
23528     dropNotAllowed : "x-dd-drop-nodrop",
23529
23530     /**
23531      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
23532      * over the current target element.
23533      * @param {String} cssClass The css class for the new drop status indicator image
23534      */
23535     setStatus : function(cssClass){
23536         cssClass = cssClass || this.dropNotAllowed;
23537         if(this.dropStatus != cssClass){
23538             this.el.replaceClass(this.dropStatus, cssClass);
23539             this.dropStatus = cssClass;
23540         }
23541     },
23542
23543     /**
23544      * Resets the status indicator to the default dropNotAllowed value
23545      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
23546      */
23547     reset : function(clearGhost){
23548         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
23549         this.dropStatus = this.dropNotAllowed;
23550         if(clearGhost){
23551             this.ghost.update("");
23552         }
23553     },
23554
23555     /**
23556      * Updates the contents of the ghost element
23557      * @param {String} html The html that will replace the current innerHTML of the ghost element
23558      */
23559     update : function(html){
23560         if(typeof html == "string"){
23561             this.ghost.update(html);
23562         }else{
23563             this.ghost.update("");
23564             html.style.margin = "0";
23565             this.ghost.dom.appendChild(html);
23566         }
23567         // ensure float = none set?? cant remember why though.
23568         var el = this.ghost.dom.firstChild;
23569                 if(el){
23570                         Roo.fly(el).setStyle('float', 'none');
23571                 }
23572     },
23573     
23574     /**
23575      * Returns the underlying proxy {@link Roo.Layer}
23576      * @return {Roo.Layer} el
23577     */
23578     getEl : function(){
23579         return this.el;
23580     },
23581
23582     /**
23583      * Returns the ghost element
23584      * @return {Roo.Element} el
23585      */
23586     getGhost : function(){
23587         return this.ghost;
23588     },
23589
23590     /**
23591      * Hides the proxy
23592      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
23593      */
23594     hide : function(clear){
23595         this.el.hide();
23596         if(clear){
23597             this.reset(true);
23598         }
23599     },
23600
23601     /**
23602      * Stops the repair animation if it's currently running
23603      */
23604     stop : function(){
23605         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
23606             this.anim.stop();
23607         }
23608     },
23609
23610     /**
23611      * Displays this proxy
23612      */
23613     show : function(){
23614         this.el.show();
23615     },
23616
23617     /**
23618      * Force the Layer to sync its shadow and shim positions to the element
23619      */
23620     sync : function(){
23621         this.el.sync();
23622     },
23623
23624     /**
23625      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
23626      * invalid drop operation by the item being dragged.
23627      * @param {Array} xy The XY position of the element ([x, y])
23628      * @param {Function} callback The function to call after the repair is complete
23629      * @param {Object} scope The scope in which to execute the callback
23630      */
23631     repair : function(xy, callback, scope){
23632         this.callback = callback;
23633         this.scope = scope;
23634         if(xy && this.animRepair !== false){
23635             this.el.addClass("x-dd-drag-repair");
23636             this.el.hideUnders(true);
23637             this.anim = this.el.shift({
23638                 duration: this.repairDuration || .5,
23639                 easing: 'easeOut',
23640                 xy: xy,
23641                 stopFx: true,
23642                 callback: this.afterRepair,
23643                 scope: this
23644             });
23645         }else{
23646             this.afterRepair();
23647         }
23648     },
23649
23650     // private
23651     afterRepair : function(){
23652         this.hide(true);
23653         if(typeof this.callback == "function"){
23654             this.callback.call(this.scope || this);
23655         }
23656         this.callback = null;
23657         this.scope = null;
23658     }
23659 };/*
23660  * Based on:
23661  * Ext JS Library 1.1.1
23662  * Copyright(c) 2006-2007, Ext JS, LLC.
23663  *
23664  * Originally Released Under LGPL - original licence link has changed is not relivant.
23665  *
23666  * Fork - LGPL
23667  * <script type="text/javascript">
23668  */
23669
23670 /**
23671  * @class Roo.dd.DragSource
23672  * @extends Roo.dd.DDProxy
23673  * A simple class that provides the basic implementation needed to make any element draggable.
23674  * @constructor
23675  * @param {String/HTMLElement/Element} el The container element
23676  * @param {Object} config
23677  */
23678 Roo.dd.DragSource = function(el, config){
23679     this.el = Roo.get(el);
23680     this.dragData = {};
23681     
23682     Roo.apply(this, config);
23683     
23684     if(!this.proxy){
23685         this.proxy = new Roo.dd.StatusProxy();
23686     }
23687
23688     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
23689           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
23690     
23691     this.dragging = false;
23692 };
23693
23694 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
23695     /**
23696      * @cfg {String} dropAllowed
23697      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23698      */
23699     dropAllowed : "x-dd-drop-ok",
23700     /**
23701      * @cfg {String} dropNotAllowed
23702      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23703      */
23704     dropNotAllowed : "x-dd-drop-nodrop",
23705
23706     /**
23707      * Returns the data object associated with this drag source
23708      * @return {Object} data An object containing arbitrary data
23709      */
23710     getDragData : function(e){
23711         return this.dragData;
23712     },
23713
23714     // private
23715     onDragEnter : function(e, id){
23716         var target = Roo.dd.DragDropMgr.getDDById(id);
23717         this.cachedTarget = target;
23718         if(this.beforeDragEnter(target, e, id) !== false){
23719             if(target.isNotifyTarget){
23720                 var status = target.notifyEnter(this, e, this.dragData);
23721                 this.proxy.setStatus(status);
23722             }else{
23723                 this.proxy.setStatus(this.dropAllowed);
23724             }
23725             
23726             if(this.afterDragEnter){
23727                 /**
23728                  * An empty function by default, but provided so that you can perform a custom action
23729                  * when the dragged item enters the drop target by providing an implementation.
23730                  * @param {Roo.dd.DragDrop} target The drop target
23731                  * @param {Event} e The event object
23732                  * @param {String} id The id of the dragged element
23733                  * @method afterDragEnter
23734                  */
23735                 this.afterDragEnter(target, e, id);
23736             }
23737         }
23738     },
23739
23740     /**
23741      * An empty function by default, but provided so that you can perform a custom action
23742      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
23743      * @param {Roo.dd.DragDrop} target The drop target
23744      * @param {Event} e The event object
23745      * @param {String} id The id of the dragged element
23746      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23747      */
23748     beforeDragEnter : function(target, e, id){
23749         return true;
23750     },
23751
23752     // private
23753     alignElWithMouse: function() {
23754         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
23755         this.proxy.sync();
23756     },
23757
23758     // private
23759     onDragOver : function(e, id){
23760         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23761         if(this.beforeDragOver(target, e, id) !== false){
23762             if(target.isNotifyTarget){
23763                 var status = target.notifyOver(this, e, this.dragData);
23764                 this.proxy.setStatus(status);
23765             }
23766
23767             if(this.afterDragOver){
23768                 /**
23769                  * An empty function by default, but provided so that you can perform a custom action
23770                  * while the dragged item is over the drop target by providing an implementation.
23771                  * @param {Roo.dd.DragDrop} target The drop target
23772                  * @param {Event} e The event object
23773                  * @param {String} id The id of the dragged element
23774                  * @method afterDragOver
23775                  */
23776                 this.afterDragOver(target, e, id);
23777             }
23778         }
23779     },
23780
23781     /**
23782      * An empty function by default, but provided so that you can perform a custom action
23783      * while the dragged item is over the drop target and optionally cancel the onDragOver.
23784      * @param {Roo.dd.DragDrop} target The drop target
23785      * @param {Event} e The event object
23786      * @param {String} id The id of the dragged element
23787      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23788      */
23789     beforeDragOver : function(target, e, id){
23790         return true;
23791     },
23792
23793     // private
23794     onDragOut : function(e, id){
23795         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23796         if(this.beforeDragOut(target, e, id) !== false){
23797             if(target.isNotifyTarget){
23798                 target.notifyOut(this, e, this.dragData);
23799             }
23800             this.proxy.reset();
23801             if(this.afterDragOut){
23802                 /**
23803                  * An empty function by default, but provided so that you can perform a custom action
23804                  * after the dragged item is dragged out of the target without dropping.
23805                  * @param {Roo.dd.DragDrop} target The drop target
23806                  * @param {Event} e The event object
23807                  * @param {String} id The id of the dragged element
23808                  * @method afterDragOut
23809                  */
23810                 this.afterDragOut(target, e, id);
23811             }
23812         }
23813         this.cachedTarget = null;
23814     },
23815
23816     /**
23817      * An empty function by default, but provided so that you can perform a custom action before the dragged
23818      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
23819      * @param {Roo.dd.DragDrop} target The drop target
23820      * @param {Event} e The event object
23821      * @param {String} id The id of the dragged element
23822      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23823      */
23824     beforeDragOut : function(target, e, id){
23825         return true;
23826     },
23827     
23828     // private
23829     onDragDrop : function(e, id){
23830         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23831         if(this.beforeDragDrop(target, e, id) !== false){
23832             if(target.isNotifyTarget){
23833                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
23834                     this.onValidDrop(target, e, id);
23835                 }else{
23836                     this.onInvalidDrop(target, e, id);
23837                 }
23838             }else{
23839                 this.onValidDrop(target, e, id);
23840             }
23841             
23842             if(this.afterDragDrop){
23843                 /**
23844                  * An empty function by default, but provided so that you can perform a custom action
23845                  * after a valid drag drop has occurred by providing an implementation.
23846                  * @param {Roo.dd.DragDrop} target The drop target
23847                  * @param {Event} e The event object
23848                  * @param {String} id The id of the dropped element
23849                  * @method afterDragDrop
23850                  */
23851                 this.afterDragDrop(target, e, id);
23852             }
23853         }
23854         delete this.cachedTarget;
23855     },
23856
23857     /**
23858      * An empty function by default, but provided so that you can perform a custom action before the dragged
23859      * item is dropped onto the target and optionally cancel the onDragDrop.
23860      * @param {Roo.dd.DragDrop} target The drop target
23861      * @param {Event} e The event object
23862      * @param {String} id The id of the dragged element
23863      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
23864      */
23865     beforeDragDrop : function(target, e, id){
23866         return true;
23867     },
23868
23869     // private
23870     onValidDrop : function(target, e, id){
23871         this.hideProxy();
23872         if(this.afterValidDrop){
23873             /**
23874              * An empty function by default, but provided so that you can perform a custom action
23875              * after a valid drop has occurred by providing an implementation.
23876              * @param {Object} target The target DD 
23877              * @param {Event} e The event object
23878              * @param {String} id The id of the dropped element
23879              * @method afterInvalidDrop
23880              */
23881             this.afterValidDrop(target, e, id);
23882         }
23883     },
23884
23885     // private
23886     getRepairXY : function(e, data){
23887         return this.el.getXY();  
23888     },
23889
23890     // private
23891     onInvalidDrop : function(target, e, id){
23892         this.beforeInvalidDrop(target, e, id);
23893         if(this.cachedTarget){
23894             if(this.cachedTarget.isNotifyTarget){
23895                 this.cachedTarget.notifyOut(this, e, this.dragData);
23896             }
23897             this.cacheTarget = null;
23898         }
23899         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
23900
23901         if(this.afterInvalidDrop){
23902             /**
23903              * An empty function by default, but provided so that you can perform a custom action
23904              * after an invalid drop has occurred by providing an implementation.
23905              * @param {Event} e The event object
23906              * @param {String} id The id of the dropped element
23907              * @method afterInvalidDrop
23908              */
23909             this.afterInvalidDrop(e, id);
23910         }
23911     },
23912
23913     // private
23914     afterRepair : function(){
23915         if(Roo.enableFx){
23916             this.el.highlight(this.hlColor || "c3daf9");
23917         }
23918         this.dragging = false;
23919     },
23920
23921     /**
23922      * An empty function by default, but provided so that you can perform a custom action after an invalid
23923      * drop has occurred.
23924      * @param {Roo.dd.DragDrop} target The drop target
23925      * @param {Event} e The event object
23926      * @param {String} id The id of the dragged element
23927      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
23928      */
23929     beforeInvalidDrop : function(target, e, id){
23930         return true;
23931     },
23932
23933     // private
23934     handleMouseDown : function(e){
23935         if(this.dragging) {
23936             return;
23937         }
23938         var data = this.getDragData(e);
23939         if(data && this.onBeforeDrag(data, e) !== false){
23940             this.dragData = data;
23941             this.proxy.stop();
23942             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
23943         } 
23944     },
23945
23946     /**
23947      * An empty function by default, but provided so that you can perform a custom action before the initial
23948      * drag event begins and optionally cancel it.
23949      * @param {Object} data An object containing arbitrary data to be shared with drop targets
23950      * @param {Event} e The event object
23951      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23952      */
23953     onBeforeDrag : function(data, e){
23954         return true;
23955     },
23956
23957     /**
23958      * An empty function by default, but provided so that you can perform a custom action once the initial
23959      * drag event has begun.  The drag cannot be canceled from this function.
23960      * @param {Number} x The x position of the click on the dragged object
23961      * @param {Number} y The y position of the click on the dragged object
23962      */
23963     onStartDrag : Roo.emptyFn,
23964
23965     // private - YUI override
23966     startDrag : function(x, y){
23967         this.proxy.reset();
23968         this.dragging = true;
23969         this.proxy.update("");
23970         this.onInitDrag(x, y);
23971         this.proxy.show();
23972     },
23973
23974     // private
23975     onInitDrag : function(x, y){
23976         var clone = this.el.dom.cloneNode(true);
23977         clone.id = Roo.id(); // prevent duplicate ids
23978         this.proxy.update(clone);
23979         this.onStartDrag(x, y);
23980         return true;
23981     },
23982
23983     /**
23984      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
23985      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
23986      */
23987     getProxy : function(){
23988         return this.proxy;  
23989     },
23990
23991     /**
23992      * Hides the drag source's {@link Roo.dd.StatusProxy}
23993      */
23994     hideProxy : function(){
23995         this.proxy.hide();  
23996         this.proxy.reset(true);
23997         this.dragging = false;
23998     },
23999
24000     // private
24001     triggerCacheRefresh : function(){
24002         Roo.dd.DDM.refreshCache(this.groups);
24003     },
24004
24005     // private - override to prevent hiding
24006     b4EndDrag: function(e) {
24007     },
24008
24009     // private - override to prevent moving
24010     endDrag : function(e){
24011         this.onEndDrag(this.dragData, e);
24012     },
24013
24014     // private
24015     onEndDrag : function(data, e){
24016     },
24017     
24018     // private - pin to cursor
24019     autoOffset : function(x, y) {
24020         this.setDelta(-12, -20);
24021     }    
24022 });/*
24023  * Based on:
24024  * Ext JS Library 1.1.1
24025  * Copyright(c) 2006-2007, Ext JS, LLC.
24026  *
24027  * Originally Released Under LGPL - original licence link has changed is not relivant.
24028  *
24029  * Fork - LGPL
24030  * <script type="text/javascript">
24031  */
24032
24033
24034 /**
24035  * @class Roo.dd.DropTarget
24036  * @extends Roo.dd.DDTarget
24037  * A simple class that provides the basic implementation needed to make any element a drop target that can have
24038  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
24039  * @constructor
24040  * @param {String/HTMLElement/Element} el The container element
24041  * @param {Object} config
24042  */
24043 Roo.dd.DropTarget = function(el, config){
24044     this.el = Roo.get(el);
24045     
24046     var listeners = false; ;
24047     if (config && config.listeners) {
24048         listeners= config.listeners;
24049         delete config.listeners;
24050     }
24051     Roo.apply(this, config);
24052     
24053     if(this.containerScroll){
24054         Roo.dd.ScrollManager.register(this.el);
24055     }
24056     this.addEvents( {
24057          /**
24058          * @scope Roo.dd.DropTarget
24059          */
24060          
24061          /**
24062          * @event enter
24063          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
24064          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
24065          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
24066          * 
24067          * IMPORTANT : it should set  this.valid to true|false
24068          * 
24069          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24070          * @param {Event} e The event
24071          * @param {Object} data An object containing arbitrary data supplied by the drag source
24072          */
24073         "enter" : true,
24074         
24075          /**
24076          * @event over
24077          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
24078          * This method will be called on every mouse movement while the drag source is over the drop target.
24079          * This default implementation simply returns the dropAllowed config value.
24080          * 
24081          * IMPORTANT : it should set  this.valid to true|false
24082          * 
24083          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24084          * @param {Event} e The event
24085          * @param {Object} data An object containing arbitrary data supplied by the drag source
24086          
24087          */
24088         "over" : true,
24089         /**
24090          * @event out
24091          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
24092          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
24093          * overClass (if any) from the drop element.
24094          * 
24095          * 
24096          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24097          * @param {Event} e The event
24098          * @param {Object} data An object containing arbitrary data supplied by the drag source
24099          */
24100          "out" : true,
24101          
24102         /**
24103          * @event drop
24104          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
24105          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
24106          * implementation that does something to process the drop event and returns true so that the drag source's
24107          * repair action does not run.
24108          * 
24109          * IMPORTANT : it should set this.success
24110          * 
24111          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24112          * @param {Event} e The event
24113          * @param {Object} data An object containing arbitrary data supplied by the drag source
24114         */
24115          "drop" : true
24116     });
24117             
24118      
24119     Roo.dd.DropTarget.superclass.constructor.call(  this, 
24120         this.el.dom, 
24121         this.ddGroup || this.group,
24122         {
24123             isTarget: true,
24124             listeners : listeners || {} 
24125            
24126         
24127         }
24128     );
24129
24130 };
24131
24132 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
24133     /**
24134      * @cfg {String} overClass
24135      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
24136      */
24137      /**
24138      * @cfg {String} ddGroup
24139      * The drag drop group to handle drop events for
24140      */
24141      
24142     /**
24143      * @cfg {String} dropAllowed
24144      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
24145      */
24146     dropAllowed : "x-dd-drop-ok",
24147     /**
24148      * @cfg {String} dropNotAllowed
24149      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
24150      */
24151     dropNotAllowed : "x-dd-drop-nodrop",
24152     /**
24153      * @cfg {boolean} success
24154      * set this after drop listener.. 
24155      */
24156     success : false,
24157     /**
24158      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
24159      * if the drop point is valid for over/enter..
24160      */
24161     valid : false,
24162     // private
24163     isTarget : true,
24164
24165     // private
24166     isNotifyTarget : true,
24167     
24168     /**
24169      * @hide
24170      */
24171     notifyEnter : function(dd, e, data)
24172     {
24173         this.valid = true;
24174         this.fireEvent('enter', dd, e, data);
24175         if(this.overClass){
24176             this.el.addClass(this.overClass);
24177         }
24178         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
24179             this.valid ? this.dropAllowed : this.dropNotAllowed
24180         );
24181     },
24182
24183     /**
24184      * @hide
24185      */
24186     notifyOver : function(dd, e, data)
24187     {
24188         this.valid = true;
24189         this.fireEvent('over', dd, e, data);
24190         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
24191             this.valid ? this.dropAllowed : this.dropNotAllowed
24192         );
24193     },
24194
24195     /**
24196      * @hide
24197      */
24198     notifyOut : function(dd, e, data)
24199     {
24200         this.fireEvent('out', dd, e, data);
24201         if(this.overClass){
24202             this.el.removeClass(this.overClass);
24203         }
24204     },
24205
24206     /**
24207      * @hide
24208      */
24209     notifyDrop : function(dd, e, data)
24210     {
24211         this.success = false;
24212         this.fireEvent('drop', dd, e, data);
24213         return this.success;
24214     }
24215 });/*
24216  * Based on:
24217  * Ext JS Library 1.1.1
24218  * Copyright(c) 2006-2007, Ext JS, LLC.
24219  *
24220  * Originally Released Under LGPL - original licence link has changed is not relivant.
24221  *
24222  * Fork - LGPL
24223  * <script type="text/javascript">
24224  */
24225
24226
24227 /**
24228  * @class Roo.dd.DragZone
24229  * @extends Roo.dd.DragSource
24230  * This class provides a container DD instance that proxies for multiple child node sources.<br />
24231  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
24232  * @constructor
24233  * @param {String/HTMLElement/Element} el The container element
24234  * @param {Object} config
24235  */
24236 Roo.dd.DragZone = function(el, config){
24237     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
24238     if(this.containerScroll){
24239         Roo.dd.ScrollManager.register(this.el);
24240     }
24241 };
24242
24243 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
24244     /**
24245      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
24246      * for auto scrolling during drag operations.
24247      */
24248     /**
24249      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
24250      * method after a failed drop (defaults to "c3daf9" - light blue)
24251      */
24252
24253     /**
24254      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
24255      * for a valid target to drag based on the mouse down. Override this method
24256      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
24257      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
24258      * @param {EventObject} e The mouse down event
24259      * @return {Object} The dragData
24260      */
24261     getDragData : function(e){
24262         return Roo.dd.Registry.getHandleFromEvent(e);
24263     },
24264     
24265     /**
24266      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
24267      * this.dragData.ddel
24268      * @param {Number} x The x position of the click on the dragged object
24269      * @param {Number} y The y position of the click on the dragged object
24270      * @return {Boolean} true to continue the drag, false to cancel
24271      */
24272     onInitDrag : function(x, y){
24273         this.proxy.update(this.dragData.ddel.cloneNode(true));
24274         this.onStartDrag(x, y);
24275         return true;
24276     },
24277     
24278     /**
24279      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
24280      */
24281     afterRepair : function(){
24282         if(Roo.enableFx){
24283             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
24284         }
24285         this.dragging = false;
24286     },
24287
24288     /**
24289      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
24290      * the XY of this.dragData.ddel
24291      * @param {EventObject} e The mouse up event
24292      * @return {Array} The xy location (e.g. [100, 200])
24293      */
24294     getRepairXY : function(e){
24295         return Roo.Element.fly(this.dragData.ddel).getXY();  
24296     }
24297 });/*
24298  * Based on:
24299  * Ext JS Library 1.1.1
24300  * Copyright(c) 2006-2007, Ext JS, LLC.
24301  *
24302  * Originally Released Under LGPL - original licence link has changed is not relivant.
24303  *
24304  * Fork - LGPL
24305  * <script type="text/javascript">
24306  */
24307 /**
24308  * @class Roo.dd.DropZone
24309  * @extends Roo.dd.DropTarget
24310  * This class provides a container DD instance that proxies for multiple child node targets.<br />
24311  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
24312  * @constructor
24313  * @param {String/HTMLElement/Element} el The container element
24314  * @param {Object} config
24315  */
24316 Roo.dd.DropZone = function(el, config){
24317     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
24318 };
24319
24320 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
24321     /**
24322      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
24323      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
24324      * provide your own custom lookup.
24325      * @param {Event} e The event
24326      * @return {Object} data The custom data
24327      */
24328     getTargetFromEvent : function(e){
24329         return Roo.dd.Registry.getTargetFromEvent(e);
24330     },
24331
24332     /**
24333      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
24334      * that it has registered.  This method has no default implementation and should be overridden to provide
24335      * node-specific processing if necessary.
24336      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
24337      * {@link #getTargetFromEvent} for this node)
24338      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24339      * @param {Event} e The event
24340      * @param {Object} data An object containing arbitrary data supplied by the drag source
24341      */
24342     onNodeEnter : function(n, dd, e, data){
24343         
24344     },
24345
24346     /**
24347      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
24348      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
24349      * overridden to provide the proper feedback.
24350      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24351      * {@link #getTargetFromEvent} for this node)
24352      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24353      * @param {Event} e The event
24354      * @param {Object} data An object containing arbitrary data supplied by the drag source
24355      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24356      * underlying {@link Roo.dd.StatusProxy} can be updated
24357      */
24358     onNodeOver : function(n, dd, e, data){
24359         return this.dropAllowed;
24360     },
24361
24362     /**
24363      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
24364      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
24365      * node-specific processing if necessary.
24366      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24367      * {@link #getTargetFromEvent} for this node)
24368      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24369      * @param {Event} e The event
24370      * @param {Object} data An object containing arbitrary data supplied by the drag source
24371      */
24372     onNodeOut : function(n, dd, e, data){
24373         
24374     },
24375
24376     /**
24377      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
24378      * the drop node.  The default implementation returns false, so it should be overridden to provide the
24379      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
24380      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24381      * {@link #getTargetFromEvent} for this node)
24382      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24383      * @param {Event} e The event
24384      * @param {Object} data An object containing arbitrary data supplied by the drag source
24385      * @return {Boolean} True if the drop was valid, else false
24386      */
24387     onNodeDrop : function(n, dd, e, data){
24388         return false;
24389     },
24390
24391     /**
24392      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
24393      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
24394      * it should be overridden to provide the proper feedback if necessary.
24395      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24396      * @param {Event} e The event
24397      * @param {Object} data An object containing arbitrary data supplied by the drag source
24398      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24399      * underlying {@link Roo.dd.StatusProxy} can be updated
24400      */
24401     onContainerOver : function(dd, e, data){
24402         return this.dropNotAllowed;
24403     },
24404
24405     /**
24406      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
24407      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
24408      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
24409      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
24410      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24411      * @param {Event} e The event
24412      * @param {Object} data An object containing arbitrary data supplied by the drag source
24413      * @return {Boolean} True if the drop was valid, else false
24414      */
24415     onContainerDrop : function(dd, e, data){
24416         return false;
24417     },
24418
24419     /**
24420      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
24421      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
24422      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
24423      * you should override this method and provide a custom implementation.
24424      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24425      * @param {Event} e The event
24426      * @param {Object} data An object containing arbitrary data supplied by the drag source
24427      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24428      * underlying {@link Roo.dd.StatusProxy} can be updated
24429      */
24430     notifyEnter : function(dd, e, data){
24431         return this.dropNotAllowed;
24432     },
24433
24434     /**
24435      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
24436      * This method will be called on every mouse movement while the drag source is over the drop zone.
24437      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
24438      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
24439      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
24440      * registered node, it will call {@link #onContainerOver}.
24441      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24442      * @param {Event} e The event
24443      * @param {Object} data An object containing arbitrary data supplied by the drag source
24444      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24445      * underlying {@link Roo.dd.StatusProxy} can be updated
24446      */
24447     notifyOver : function(dd, e, data){
24448         var n = this.getTargetFromEvent(e);
24449         if(!n){ // not over valid drop target
24450             if(this.lastOverNode){
24451                 this.onNodeOut(this.lastOverNode, dd, e, data);
24452                 this.lastOverNode = null;
24453             }
24454             return this.onContainerOver(dd, e, data);
24455         }
24456         if(this.lastOverNode != n){
24457             if(this.lastOverNode){
24458                 this.onNodeOut(this.lastOverNode, dd, e, data);
24459             }
24460             this.onNodeEnter(n, dd, e, data);
24461             this.lastOverNode = n;
24462         }
24463         return this.onNodeOver(n, dd, e, data);
24464     },
24465
24466     /**
24467      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
24468      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
24469      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
24470      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24471      * @param {Event} e The event
24472      * @param {Object} data An object containing arbitrary data supplied by the drag zone
24473      */
24474     notifyOut : function(dd, e, data){
24475         if(this.lastOverNode){
24476             this.onNodeOut(this.lastOverNode, dd, e, data);
24477             this.lastOverNode = null;
24478         }
24479     },
24480
24481     /**
24482      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
24483      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
24484      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
24485      * otherwise it will call {@link #onContainerDrop}.
24486      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24487      * @param {Event} e The event
24488      * @param {Object} data An object containing arbitrary data supplied by the drag source
24489      * @return {Boolean} True if the drop was valid, else false
24490      */
24491     notifyDrop : function(dd, e, data){
24492         if(this.lastOverNode){
24493             this.onNodeOut(this.lastOverNode, dd, e, data);
24494             this.lastOverNode = null;
24495         }
24496         var n = this.getTargetFromEvent(e);
24497         return n ?
24498             this.onNodeDrop(n, dd, e, data) :
24499             this.onContainerDrop(dd, e, data);
24500     },
24501
24502     // private
24503     triggerCacheRefresh : function(){
24504         Roo.dd.DDM.refreshCache(this.groups);
24505     }  
24506 });/*
24507  * Based on:
24508  * Ext JS Library 1.1.1
24509  * Copyright(c) 2006-2007, Ext JS, LLC.
24510  *
24511  * Originally Released Under LGPL - original licence link has changed is not relivant.
24512  *
24513  * Fork - LGPL
24514  * <script type="text/javascript">
24515  */
24516
24517
24518 /**
24519  * @class Roo.data.SortTypes
24520  * @static
24521  * Defines the default sorting (casting?) comparison functions used when sorting data.
24522  */
24523 Roo.data.SortTypes = {
24524     /**
24525      * Default sort that does nothing
24526      * @param {Mixed} s The value being converted
24527      * @return {Mixed} The comparison value
24528      */
24529     none : function(s){
24530         return s;
24531     },
24532     
24533     /**
24534      * The regular expression used to strip tags
24535      * @type {RegExp}
24536      * @property
24537      */
24538     stripTagsRE : /<\/?[^>]+>/gi,
24539     
24540     /**
24541      * Strips all HTML tags to sort on text only
24542      * @param {Mixed} s The value being converted
24543      * @return {String} The comparison value
24544      */
24545     asText : function(s){
24546         return String(s).replace(this.stripTagsRE, "");
24547     },
24548     
24549     /**
24550      * Strips all HTML tags to sort on text only - Case insensitive
24551      * @param {Mixed} s The value being converted
24552      * @return {String} The comparison value
24553      */
24554     asUCText : function(s){
24555         return String(s).toUpperCase().replace(this.stripTagsRE, "");
24556     },
24557     
24558     /**
24559      * Case insensitive string
24560      * @param {Mixed} s The value being converted
24561      * @return {String} The comparison value
24562      */
24563     asUCString : function(s) {
24564         return String(s).toUpperCase();
24565     },
24566     
24567     /**
24568      * Date sorting
24569      * @param {Mixed} s The value being converted
24570      * @return {Number} The comparison value
24571      */
24572     asDate : function(s) {
24573         if(!s){
24574             return 0;
24575         }
24576         if(s instanceof Date){
24577             return s.getTime();
24578         }
24579         return Date.parse(String(s));
24580     },
24581     
24582     /**
24583      * Float sorting
24584      * @param {Mixed} s The value being converted
24585      * @return {Float} The comparison value
24586      */
24587     asFloat : function(s) {
24588         var val = parseFloat(String(s).replace(/,/g, ""));
24589         if(isNaN(val)) {
24590             val = 0;
24591         }
24592         return val;
24593     },
24594     
24595     /**
24596      * Integer sorting
24597      * @param {Mixed} s The value being converted
24598      * @return {Number} The comparison value
24599      */
24600     asInt : function(s) {
24601         var val = parseInt(String(s).replace(/,/g, ""));
24602         if(isNaN(val)) {
24603             val = 0;
24604         }
24605         return val;
24606     }
24607 };/*
24608  * Based on:
24609  * Ext JS Library 1.1.1
24610  * Copyright(c) 2006-2007, Ext JS, LLC.
24611  *
24612  * Originally Released Under LGPL - original licence link has changed is not relivant.
24613  *
24614  * Fork - LGPL
24615  * <script type="text/javascript">
24616  */
24617
24618 /**
24619 * @class Roo.data.Record
24620  * Instances of this class encapsulate both record <em>definition</em> information, and record
24621  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
24622  * to access Records cached in an {@link Roo.data.Store} object.<br>
24623  * <p>
24624  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
24625  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
24626  * objects.<br>
24627  * <p>
24628  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
24629  * @constructor
24630  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
24631  * {@link #create}. The parameters are the same.
24632  * @param {Array} data An associative Array of data values keyed by the field name.
24633  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
24634  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
24635  * not specified an integer id is generated.
24636  */
24637 Roo.data.Record = function(data, id){
24638     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
24639     this.data = data;
24640 };
24641
24642 /**
24643  * Generate a constructor for a specific record layout.
24644  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
24645  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
24646  * Each field definition object may contain the following properties: <ul>
24647  * <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,
24648  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
24649  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
24650  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
24651  * is being used, then this is a string containing the javascript expression to reference the data relative to 
24652  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
24653  * to the data item relative to the record element. If the mapping expression is the same as the field name,
24654  * this may be omitted.</p></li>
24655  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
24656  * <ul><li>auto (Default, implies no conversion)</li>
24657  * <li>string</li>
24658  * <li>int</li>
24659  * <li>float</li>
24660  * <li>boolean</li>
24661  * <li>date</li></ul></p></li>
24662  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
24663  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
24664  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
24665  * by the Reader into an object that will be stored in the Record. It is passed the
24666  * following parameters:<ul>
24667  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
24668  * </ul></p></li>
24669  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
24670  * </ul>
24671  * <br>usage:<br><pre><code>
24672 var TopicRecord = Roo.data.Record.create(
24673     {name: 'title', mapping: 'topic_title'},
24674     {name: 'author', mapping: 'username'},
24675     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
24676     {name: 'lastPost', mapping: 'post_time', type: 'date'},
24677     {name: 'lastPoster', mapping: 'user2'},
24678     {name: 'excerpt', mapping: 'post_text'}
24679 );
24680
24681 var myNewRecord = new TopicRecord({
24682     title: 'Do my job please',
24683     author: 'noobie',
24684     totalPosts: 1,
24685     lastPost: new Date(),
24686     lastPoster: 'Animal',
24687     excerpt: 'No way dude!'
24688 });
24689 myStore.add(myNewRecord);
24690 </code></pre>
24691  * @method create
24692  * @static
24693  */
24694 Roo.data.Record.create = function(o){
24695     var f = function(){
24696         f.superclass.constructor.apply(this, arguments);
24697     };
24698     Roo.extend(f, Roo.data.Record);
24699     var p = f.prototype;
24700     p.fields = new Roo.util.MixedCollection(false, function(field){
24701         return field.name;
24702     });
24703     for(var i = 0, len = o.length; i < len; i++){
24704         p.fields.add(new Roo.data.Field(o[i]));
24705     }
24706     f.getField = function(name){
24707         return p.fields.get(name);  
24708     };
24709     return f;
24710 };
24711
24712 Roo.data.Record.AUTO_ID = 1000;
24713 Roo.data.Record.EDIT = 'edit';
24714 Roo.data.Record.REJECT = 'reject';
24715 Roo.data.Record.COMMIT = 'commit';
24716
24717 Roo.data.Record.prototype = {
24718     /**
24719      * Readonly flag - true if this record has been modified.
24720      * @type Boolean
24721      */
24722     dirty : false,
24723     editing : false,
24724     error: null,
24725     modified: null,
24726
24727     // private
24728     join : function(store){
24729         this.store = store;
24730     },
24731
24732     /**
24733      * Set the named field to the specified value.
24734      * @param {String} name The name of the field to set.
24735      * @param {Object} value The value to set the field to.
24736      */
24737     set : function(name, value){
24738         if(this.data[name] == value){
24739             return;
24740         }
24741         this.dirty = true;
24742         if(!this.modified){
24743             this.modified = {};
24744         }
24745         if(typeof this.modified[name] == 'undefined'){
24746             this.modified[name] = this.data[name];
24747         }
24748         this.data[name] = value;
24749         if(!this.editing && this.store){
24750             this.store.afterEdit(this);
24751         }       
24752     },
24753
24754     /**
24755      * Get the value of the named field.
24756      * @param {String} name The name of the field to get the value of.
24757      * @return {Object} The value of the field.
24758      */
24759     get : function(name){
24760         return this.data[name]; 
24761     },
24762
24763     // private
24764     beginEdit : function(){
24765         this.editing = true;
24766         this.modified = {}; 
24767     },
24768
24769     // private
24770     cancelEdit : function(){
24771         this.editing = false;
24772         delete this.modified;
24773     },
24774
24775     // private
24776     endEdit : function(){
24777         this.editing = false;
24778         if(this.dirty && this.store){
24779             this.store.afterEdit(this);
24780         }
24781     },
24782
24783     /**
24784      * Usually called by the {@link Roo.data.Store} which owns the Record.
24785      * Rejects all changes made to the Record since either creation, or the last commit operation.
24786      * Modified fields are reverted to their original values.
24787      * <p>
24788      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24789      * of reject operations.
24790      */
24791     reject : function(){
24792         var m = this.modified;
24793         for(var n in m){
24794             if(typeof m[n] != "function"){
24795                 this.data[n] = m[n];
24796             }
24797         }
24798         this.dirty = false;
24799         delete this.modified;
24800         this.editing = false;
24801         if(this.store){
24802             this.store.afterReject(this);
24803         }
24804     },
24805
24806     /**
24807      * Usually called by the {@link Roo.data.Store} which owns the Record.
24808      * Commits all changes made to the Record since either creation, or the last commit operation.
24809      * <p>
24810      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24811      * of commit operations.
24812      */
24813     commit : function(){
24814         this.dirty = false;
24815         delete this.modified;
24816         this.editing = false;
24817         if(this.store){
24818             this.store.afterCommit(this);
24819         }
24820     },
24821
24822     // private
24823     hasError : function(){
24824         return this.error != null;
24825     },
24826
24827     // private
24828     clearError : function(){
24829         this.error = null;
24830     },
24831
24832     /**
24833      * Creates a copy of this record.
24834      * @param {String} id (optional) A new record id if you don't want to use this record's id
24835      * @return {Record}
24836      */
24837     copy : function(newId) {
24838         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
24839     }
24840 };/*
24841  * Based on:
24842  * Ext JS Library 1.1.1
24843  * Copyright(c) 2006-2007, Ext JS, LLC.
24844  *
24845  * Originally Released Under LGPL - original licence link has changed is not relivant.
24846  *
24847  * Fork - LGPL
24848  * <script type="text/javascript">
24849  */
24850
24851
24852
24853 /**
24854  * @class Roo.data.Store
24855  * @extends Roo.util.Observable
24856  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
24857  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
24858  * <p>
24859  * 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
24860  * has no knowledge of the format of the data returned by the Proxy.<br>
24861  * <p>
24862  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
24863  * instances from the data object. These records are cached and made available through accessor functions.
24864  * @constructor
24865  * Creates a new Store.
24866  * @param {Object} config A config object containing the objects needed for the Store to access data,
24867  * and read the data into Records.
24868  */
24869 Roo.data.Store = function(config){
24870     this.data = new Roo.util.MixedCollection(false);
24871     this.data.getKey = function(o){
24872         return o.id;
24873     };
24874     this.baseParams = {};
24875     // private
24876     this.paramNames = {
24877         "start" : "start",
24878         "limit" : "limit",
24879         "sort" : "sort",
24880         "dir" : "dir",
24881         "multisort" : "_multisort"
24882     };
24883
24884     if(config && config.data){
24885         this.inlineData = config.data;
24886         delete config.data;
24887     }
24888
24889     Roo.apply(this, config);
24890     
24891     if(this.reader){ // reader passed
24892         this.reader = Roo.factory(this.reader, Roo.data);
24893         this.reader.xmodule = this.xmodule || false;
24894         if(!this.recordType){
24895             this.recordType = this.reader.recordType;
24896         }
24897         if(this.reader.onMetaChange){
24898             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
24899         }
24900     }
24901
24902     if(this.recordType){
24903         this.fields = this.recordType.prototype.fields;
24904     }
24905     this.modified = [];
24906
24907     this.addEvents({
24908         /**
24909          * @event datachanged
24910          * Fires when the data cache has changed, and a widget which is using this Store
24911          * as a Record cache should refresh its view.
24912          * @param {Store} this
24913          */
24914         datachanged : true,
24915         /**
24916          * @event metachange
24917          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
24918          * @param {Store} this
24919          * @param {Object} meta The JSON metadata
24920          */
24921         metachange : true,
24922         /**
24923          * @event add
24924          * Fires when Records have been added to the Store
24925          * @param {Store} this
24926          * @param {Roo.data.Record[]} records The array of Records added
24927          * @param {Number} index The index at which the record(s) were added
24928          */
24929         add : true,
24930         /**
24931          * @event remove
24932          * Fires when a Record has been removed from the Store
24933          * @param {Store} this
24934          * @param {Roo.data.Record} record The Record that was removed
24935          * @param {Number} index The index at which the record was removed
24936          */
24937         remove : true,
24938         /**
24939          * @event update
24940          * Fires when a Record has been updated
24941          * @param {Store} this
24942          * @param {Roo.data.Record} record The Record that was updated
24943          * @param {String} operation The update operation being performed.  Value may be one of:
24944          * <pre><code>
24945  Roo.data.Record.EDIT
24946  Roo.data.Record.REJECT
24947  Roo.data.Record.COMMIT
24948          * </code></pre>
24949          */
24950         update : true,
24951         /**
24952          * @event clear
24953          * Fires when the data cache has been cleared.
24954          * @param {Store} this
24955          */
24956         clear : true,
24957         /**
24958          * @event beforeload
24959          * Fires before a request is made for a new data object.  If the beforeload handler returns false
24960          * the load action will be canceled.
24961          * @param {Store} this
24962          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24963          */
24964         beforeload : true,
24965         /**
24966          * @event beforeloadadd
24967          * Fires after a new set of Records has been loaded.
24968          * @param {Store} this
24969          * @param {Roo.data.Record[]} records The Records that were loaded
24970          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24971          */
24972         beforeloadadd : true,
24973         /**
24974          * @event load
24975          * Fires after a new set of Records has been loaded, before they are added to the store.
24976          * @param {Store} this
24977          * @param {Roo.data.Record[]} records The Records that were loaded
24978          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24979          * @params {Object} return from reader
24980          */
24981         load : true,
24982         /**
24983          * @event loadexception
24984          * Fires if an exception occurs in the Proxy during loading.
24985          * Called with the signature of the Proxy's "loadexception" event.
24986          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
24987          * 
24988          * @param {Proxy} 
24989          * @param {Object} ret return data from JsonData.reader() - success, totalRecords, records
24990          * @param {Object} opts - load Options
24991          * @param {Object} jsonData from your request (normally this contains the Exception)
24992          */
24993         loadexception : true
24994     });
24995     
24996     if(this.proxy){
24997         this.proxy = Roo.factory(this.proxy, Roo.data);
24998         this.proxy.xmodule = this.xmodule || false;
24999         this.relayEvents(this.proxy,  ["loadexception"]);
25000     }
25001     this.sortToggle = {};
25002     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
25003
25004     Roo.data.Store.superclass.constructor.call(this);
25005
25006     if(this.inlineData){
25007         this.loadData(this.inlineData);
25008         delete this.inlineData;
25009     }
25010 };
25011
25012 Roo.extend(Roo.data.Store, Roo.util.Observable, {
25013      /**
25014     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
25015     * without a remote query - used by combo/forms at present.
25016     */
25017     
25018     /**
25019     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
25020     */
25021     /**
25022     * @cfg {Array} data Inline data to be loaded when the store is initialized.
25023     */
25024     /**
25025     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
25026     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
25027     */
25028     /**
25029     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
25030     * on any HTTP request
25031     */
25032     /**
25033     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
25034     */
25035     /**
25036     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
25037     */
25038     multiSort: false,
25039     /**
25040     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
25041     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
25042     */
25043     remoteSort : false,
25044
25045     /**
25046     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
25047      * loaded or when a record is removed. (defaults to false).
25048     */
25049     pruneModifiedRecords : false,
25050
25051     // private
25052     lastOptions : null,
25053
25054     /**
25055      * Add Records to the Store and fires the add event.
25056      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
25057      */
25058     add : function(records){
25059         records = [].concat(records);
25060         for(var i = 0, len = records.length; i < len; i++){
25061             records[i].join(this);
25062         }
25063         var index = this.data.length;
25064         this.data.addAll(records);
25065         this.fireEvent("add", this, records, index);
25066     },
25067
25068     /**
25069      * Remove a Record from the Store and fires the remove event.
25070      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
25071      */
25072     remove : function(record){
25073         var index = this.data.indexOf(record);
25074         this.data.removeAt(index);
25075  
25076         if(this.pruneModifiedRecords){
25077             this.modified.remove(record);
25078         }
25079         this.fireEvent("remove", this, record, index);
25080     },
25081
25082     /**
25083      * Remove all Records from the Store and fires the clear event.
25084      */
25085     removeAll : function(){
25086         this.data.clear();
25087         if(this.pruneModifiedRecords){
25088             this.modified = [];
25089         }
25090         this.fireEvent("clear", this);
25091     },
25092
25093     /**
25094      * Inserts Records to the Store at the given index and fires the add event.
25095      * @param {Number} index The start index at which to insert the passed Records.
25096      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
25097      */
25098     insert : function(index, records){
25099         records = [].concat(records);
25100         for(var i = 0, len = records.length; i < len; i++){
25101             this.data.insert(index, records[i]);
25102             records[i].join(this);
25103         }
25104         this.fireEvent("add", this, records, index);
25105     },
25106
25107     /**
25108      * Get the index within the cache of the passed Record.
25109      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
25110      * @return {Number} The index of the passed Record. Returns -1 if not found.
25111      */
25112     indexOf : function(record){
25113         return this.data.indexOf(record);
25114     },
25115
25116     /**
25117      * Get the index within the cache of the Record with the passed id.
25118      * @param {String} id The id of the Record to find.
25119      * @return {Number} The index of the Record. Returns -1 if not found.
25120      */
25121     indexOfId : function(id){
25122         return this.data.indexOfKey(id);
25123     },
25124
25125     /**
25126      * Get the Record with the specified id.
25127      * @param {String} id The id of the Record to find.
25128      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
25129      */
25130     getById : function(id){
25131         return this.data.key(id);
25132     },
25133
25134     /**
25135      * Get the Record at the specified index.
25136      * @param {Number} index The index of the Record to find.
25137      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
25138      */
25139     getAt : function(index){
25140         return this.data.itemAt(index);
25141     },
25142
25143     /**
25144      * Returns a range of Records between specified indices.
25145      * @param {Number} startIndex (optional) The starting index (defaults to 0)
25146      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
25147      * @return {Roo.data.Record[]} An array of Records
25148      */
25149     getRange : function(start, end){
25150         return this.data.getRange(start, end);
25151     },
25152
25153     // private
25154     storeOptions : function(o){
25155         o = Roo.apply({}, o);
25156         delete o.callback;
25157         delete o.scope;
25158         this.lastOptions = o;
25159     },
25160
25161     /**
25162      * Loads the Record cache from the configured Proxy using the configured Reader.
25163      * <p>
25164      * If using remote paging, then the first load call must specify the <em>start</em>
25165      * and <em>limit</em> properties in the options.params property to establish the initial
25166      * position within the dataset, and the number of Records to cache on each read from the Proxy.
25167      * <p>
25168      * <strong>It is important to note that for remote data sources, loading is asynchronous,
25169      * and this call will return before the new data has been loaded. Perform any post-processing
25170      * in a callback function, or in a "load" event handler.</strong>
25171      * <p>
25172      * @param {Object} options An object containing properties which control loading options:<ul>
25173      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
25174      * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
25175      * <pre>
25176                 {
25177                     data : data,  // array of key=>value data like JsonReader
25178                     total : data.length,
25179                     success : true
25180                     
25181                 }
25182         </pre>
25183             }.</li>
25184      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
25185      * passed the following arguments:<ul>
25186      * <li>r : Roo.data.Record[]</li>
25187      * <li>options: Options object from the load call</li>
25188      * <li>success: Boolean success indicator</li></ul></li>
25189      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
25190      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
25191      * </ul>
25192      */
25193     load : function(options){
25194         options = options || {};
25195         if(this.fireEvent("beforeload", this, options) !== false){
25196             this.storeOptions(options);
25197             var p = Roo.apply(options.params || {}, this.baseParams);
25198             // if meta was not loaded from remote source.. try requesting it.
25199             if (!this.reader.metaFromRemote) {
25200                 p._requestMeta = 1;
25201             }
25202             if(this.sortInfo && this.remoteSort){
25203                 var pn = this.paramNames;
25204                 p[pn["sort"]] = this.sortInfo.field;
25205                 p[pn["dir"]] = this.sortInfo.direction;
25206             }
25207             if (this.multiSort) {
25208                 var pn = this.paramNames;
25209                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
25210             }
25211             
25212             this.proxy.load(p, this.reader, this.loadRecords, this, options);
25213         }
25214     },
25215
25216     /**
25217      * Reloads the Record cache from the configured Proxy using the configured Reader and
25218      * the options from the last load operation performed.
25219      * @param {Object} options (optional) An object containing properties which may override the options
25220      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
25221      * the most recently used options are reused).
25222      */
25223     reload : function(options){
25224         this.load(Roo.applyIf(options||{}, this.lastOptions));
25225     },
25226
25227     // private
25228     // Called as a callback by the Reader during a load operation.
25229     loadRecords : function(o, options, success){
25230          
25231         if(!o){
25232             if(success !== false){
25233                 this.fireEvent("load", this, [], options, o);
25234             }
25235             if(options.callback){
25236                 options.callback.call(options.scope || this, [], options, false);
25237             }
25238             return;
25239         }
25240         // if data returned failure - throw an exception.
25241         if (o.success === false) {
25242             // show a message if no listener is registered.
25243             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
25244                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
25245             }
25246             // loadmask wil be hooked into this..
25247             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
25248             return;
25249         }
25250         var r = o.records, t = o.totalRecords || r.length;
25251         
25252         this.fireEvent("beforeloadadd", this, r, options, o);
25253         
25254         if(!options || options.add !== true){
25255             if(this.pruneModifiedRecords){
25256                 this.modified = [];
25257             }
25258             for(var i = 0, len = r.length; i < len; i++){
25259                 r[i].join(this);
25260             }
25261             if(this.snapshot){
25262                 this.data = this.snapshot;
25263                 delete this.snapshot;
25264             }
25265             this.data.clear();
25266             this.data.addAll(r);
25267             this.totalLength = t;
25268             this.applySort();
25269             this.fireEvent("datachanged", this);
25270         }else{
25271             this.totalLength = Math.max(t, this.data.length+r.length);
25272             this.add(r);
25273         }
25274         
25275         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
25276                 
25277             var e = new Roo.data.Record({});
25278
25279             e.set(this.parent.displayField, this.parent.emptyTitle);
25280             e.set(this.parent.valueField, '');
25281
25282             this.insert(0, e);
25283         }
25284             
25285         this.fireEvent("load", this, r, options, o);
25286         if(options.callback){
25287             options.callback.call(options.scope || this, r, options, true);
25288         }
25289     },
25290
25291
25292     /**
25293      * Loads data from a passed data block. A Reader which understands the format of the data
25294      * must have been configured in the constructor.
25295      * @param {Object} data The data block from which to read the Records.  The format of the data expected
25296      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
25297      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
25298      */
25299     loadData : function(o, append){
25300         var r = this.reader.readRecords(o);
25301         this.loadRecords(r, {add: append}, true);
25302     },
25303     
25304      /**
25305      * using 'cn' the nested child reader read the child array into it's child stores.
25306      * @param {Object} rec The record with a 'children array
25307      */
25308     loadDataFromChildren : function(rec)
25309     {
25310         this.loadData(this.reader.toLoadData(rec));
25311     },
25312     
25313
25314     /**
25315      * Gets the number of cached records.
25316      * <p>
25317      * <em>If using paging, this may not be the total size of the dataset. If the data object
25318      * used by the Reader contains the dataset size, then the getTotalCount() function returns
25319      * the data set size</em>
25320      */
25321     getCount : function(){
25322         return this.data.length || 0;
25323     },
25324
25325     /**
25326      * Gets the total number of records in the dataset as returned by the server.
25327      * <p>
25328      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
25329      * the dataset size</em>
25330      */
25331     getTotalCount : function(){
25332         return this.totalLength || 0;
25333     },
25334
25335     /**
25336      * Returns the sort state of the Store as an object with two properties:
25337      * <pre><code>
25338  field {String} The name of the field by which the Records are sorted
25339  direction {String} The sort order, "ASC" or "DESC"
25340      * </code></pre>
25341      */
25342     getSortState : function(){
25343         return this.sortInfo;
25344     },
25345
25346     // private
25347     applySort : function(){
25348         if(this.sortInfo && !this.remoteSort){
25349             var s = this.sortInfo, f = s.field;
25350             var st = this.fields.get(f).sortType;
25351             var fn = function(r1, r2){
25352                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
25353                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
25354             };
25355             this.data.sort(s.direction, fn);
25356             if(this.snapshot && this.snapshot != this.data){
25357                 this.snapshot.sort(s.direction, fn);
25358             }
25359         }
25360     },
25361
25362     /**
25363      * Sets the default sort column and order to be used by the next load operation.
25364      * @param {String} fieldName The name of the field to sort by.
25365      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
25366      */
25367     setDefaultSort : function(field, dir){
25368         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
25369     },
25370
25371     /**
25372      * Sort the Records.
25373      * If remote sorting is used, the sort is performed on the server, and the cache is
25374      * reloaded. If local sorting is used, the cache is sorted internally.
25375      * @param {String} fieldName The name of the field to sort by.
25376      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
25377      */
25378     sort : function(fieldName, dir){
25379         var f = this.fields.get(fieldName);
25380         if(!dir){
25381             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
25382             
25383             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
25384                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
25385             }else{
25386                 dir = f.sortDir;
25387             }
25388         }
25389         this.sortToggle[f.name] = dir;
25390         this.sortInfo = {field: f.name, direction: dir};
25391         if(!this.remoteSort){
25392             this.applySort();
25393             this.fireEvent("datachanged", this);
25394         }else{
25395             this.load(this.lastOptions);
25396         }
25397     },
25398
25399     /**
25400      * Calls the specified function for each of the Records in the cache.
25401      * @param {Function} fn The function to call. The Record is passed as the first parameter.
25402      * Returning <em>false</em> aborts and exits the iteration.
25403      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
25404      */
25405     each : function(fn, scope){
25406         this.data.each(fn, scope);
25407     },
25408
25409     /**
25410      * Gets all records modified since the last commit.  Modified records are persisted across load operations
25411      * (e.g., during paging).
25412      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
25413      */
25414     getModifiedRecords : function(){
25415         return this.modified;
25416     },
25417
25418     // private
25419     createFilterFn : function(property, value, anyMatch){
25420         if(!value.exec){ // not a regex
25421             value = String(value);
25422             if(value.length == 0){
25423                 return false;
25424             }
25425             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
25426         }
25427         return function(r){
25428             return value.test(r.data[property]);
25429         };
25430     },
25431
25432     /**
25433      * Sums the value of <i>property</i> for each record between start and end and returns the result.
25434      * @param {String} property A field on your records
25435      * @param {Number} start The record index to start at (defaults to 0)
25436      * @param {Number} end The last record index to include (defaults to length - 1)
25437      * @return {Number} The sum
25438      */
25439     sum : function(property, start, end){
25440         var rs = this.data.items, v = 0;
25441         start = start || 0;
25442         end = (end || end === 0) ? end : rs.length-1;
25443
25444         for(var i = start; i <= end; i++){
25445             v += (rs[i].data[property] || 0);
25446         }
25447         return v;
25448     },
25449
25450     /**
25451      * Filter the records by a specified property.
25452      * @param {String} field A field on your records
25453      * @param {String/RegExp} value Either a string that the field
25454      * should start with or a RegExp to test against the field
25455      * @param {Boolean} anyMatch True to match any part not just the beginning
25456      */
25457     filter : function(property, value, anyMatch){
25458         var fn = this.createFilterFn(property, value, anyMatch);
25459         return fn ? this.filterBy(fn) : this.clearFilter();
25460     },
25461
25462     /**
25463      * Filter by a function. The specified function will be called with each
25464      * record in this data source. If the function returns true the record is included,
25465      * otherwise it is filtered.
25466      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
25467      * @param {Object} scope (optional) The scope of the function (defaults to this)
25468      */
25469     filterBy : function(fn, scope){
25470         this.snapshot = this.snapshot || this.data;
25471         this.data = this.queryBy(fn, scope||this);
25472         this.fireEvent("datachanged", this);
25473     },
25474
25475     /**
25476      * Query the records by a specified property.
25477      * @param {String} field A field on your records
25478      * @param {String/RegExp} value Either a string that the field
25479      * should start with or a RegExp to test against the field
25480      * @param {Boolean} anyMatch True to match any part not just the beginning
25481      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
25482      */
25483     query : function(property, value, anyMatch){
25484         var fn = this.createFilterFn(property, value, anyMatch);
25485         return fn ? this.queryBy(fn) : this.data.clone();
25486     },
25487
25488     /**
25489      * Query by a function. The specified function will be called with each
25490      * record in this data source. If the function returns true the record is included
25491      * in the results.
25492      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
25493      * @param {Object} scope (optional) The scope of the function (defaults to this)
25494       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
25495      **/
25496     queryBy : function(fn, scope){
25497         var data = this.snapshot || this.data;
25498         return data.filterBy(fn, scope||this);
25499     },
25500
25501     /**
25502      * Collects unique values for a particular dataIndex from this store.
25503      * @param {String} dataIndex The property to collect
25504      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
25505      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
25506      * @return {Array} An array of the unique values
25507      **/
25508     collect : function(dataIndex, allowNull, bypassFilter){
25509         var d = (bypassFilter === true && this.snapshot) ?
25510                 this.snapshot.items : this.data.items;
25511         var v, sv, r = [], l = {};
25512         for(var i = 0, len = d.length; i < len; i++){
25513             v = d[i].data[dataIndex];
25514             sv = String(v);
25515             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
25516                 l[sv] = true;
25517                 r[r.length] = v;
25518             }
25519         }
25520         return r;
25521     },
25522
25523     /**
25524      * Revert to a view of the Record cache with no filtering applied.
25525      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
25526      */
25527     clearFilter : function(suppressEvent){
25528         if(this.snapshot && this.snapshot != this.data){
25529             this.data = this.snapshot;
25530             delete this.snapshot;
25531             if(suppressEvent !== true){
25532                 this.fireEvent("datachanged", this);
25533             }
25534         }
25535     },
25536
25537     // private
25538     afterEdit : function(record){
25539         if(this.modified.indexOf(record) == -1){
25540             this.modified.push(record);
25541         }
25542         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
25543     },
25544     
25545     // private
25546     afterReject : function(record){
25547         this.modified.remove(record);
25548         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
25549     },
25550
25551     // private
25552     afterCommit : function(record){
25553         this.modified.remove(record);
25554         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
25555     },
25556
25557     /**
25558      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
25559      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
25560      */
25561     commitChanges : function(){
25562         var m = this.modified.slice(0);
25563         this.modified = [];
25564         for(var i = 0, len = m.length; i < len; i++){
25565             m[i].commit();
25566         }
25567     },
25568
25569     /**
25570      * Cancel outstanding changes on all changed records.
25571      */
25572     rejectChanges : function(){
25573         var m = this.modified.slice(0);
25574         this.modified = [];
25575         for(var i = 0, len = m.length; i < len; i++){
25576             m[i].reject();
25577         }
25578     },
25579
25580     onMetaChange : function(meta, rtype, o){
25581         this.recordType = rtype;
25582         this.fields = rtype.prototype.fields;
25583         delete this.snapshot;
25584         this.sortInfo = meta.sortInfo || this.sortInfo;
25585         this.modified = [];
25586         this.fireEvent('metachange', this, this.reader.meta);
25587     },
25588     
25589     moveIndex : function(data, type)
25590     {
25591         var index = this.indexOf(data);
25592         
25593         var newIndex = index + type;
25594         
25595         this.remove(data);
25596         
25597         this.insert(newIndex, data);
25598         
25599     }
25600 });/*
25601  * Based on:
25602  * Ext JS Library 1.1.1
25603  * Copyright(c) 2006-2007, Ext JS, LLC.
25604  *
25605  * Originally Released Under LGPL - original licence link has changed is not relivant.
25606  *
25607  * Fork - LGPL
25608  * <script type="text/javascript">
25609  */
25610
25611 /**
25612  * @class Roo.data.SimpleStore
25613  * @extends Roo.data.Store
25614  * Small helper class to make creating Stores from Array data easier.
25615  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
25616  * @cfg {Array} fields An array of field definition objects, or field name strings.
25617  * @cfg {Object} an existing reader (eg. copied from another store)
25618  * @cfg {Array} data The multi-dimensional array of data
25619  * @cfg {Roo.data.DataProxy} proxy [not-required]  
25620  * @cfg {Roo.data.Reader} reader  [not-required] 
25621  * @constructor
25622  * @param {Object} config
25623  */
25624 Roo.data.SimpleStore = function(config)
25625 {
25626     Roo.data.SimpleStore.superclass.constructor.call(this, {
25627         isLocal : true,
25628         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
25629                 id: config.id
25630             },
25631             Roo.data.Record.create(config.fields)
25632         ),
25633         proxy : new Roo.data.MemoryProxy(config.data)
25634     });
25635     this.load();
25636 };
25637 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
25638  * Based on:
25639  * Ext JS Library 1.1.1
25640  * Copyright(c) 2006-2007, Ext JS, LLC.
25641  *
25642  * Originally Released Under LGPL - original licence link has changed is not relivant.
25643  *
25644  * Fork - LGPL
25645  * <script type="text/javascript">
25646  */
25647
25648 /**
25649 /**
25650  * @extends Roo.data.Store
25651  * @class Roo.data.JsonStore
25652  * Small helper class to make creating Stores for JSON data easier. <br/>
25653 <pre><code>
25654 var store = new Roo.data.JsonStore({
25655     url: 'get-images.php',
25656     root: 'images',
25657     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
25658 });
25659 </code></pre>
25660  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
25661  * JsonReader and HttpProxy (unless inline data is provided).</b>
25662  * @cfg {Array} fields An array of field definition objects, or field name strings.
25663  * @constructor
25664  * @param {Object} config
25665  */
25666 Roo.data.JsonStore = function(c){
25667     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
25668         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
25669         reader: new Roo.data.JsonReader(c, c.fields)
25670     }));
25671 };
25672 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
25673  * Based on:
25674  * Ext JS Library 1.1.1
25675  * Copyright(c) 2006-2007, Ext JS, LLC.
25676  *
25677  * Originally Released Under LGPL - original licence link has changed is not relivant.
25678  *
25679  * Fork - LGPL
25680  * <script type="text/javascript">
25681  */
25682
25683  
25684 Roo.data.Field = function(config){
25685     if(typeof config == "string"){
25686         config = {name: config};
25687     }
25688     Roo.apply(this, config);
25689     
25690     if(!this.type){
25691         this.type = "auto";
25692     }
25693     
25694     var st = Roo.data.SortTypes;
25695     // named sortTypes are supported, here we look them up
25696     if(typeof this.sortType == "string"){
25697         this.sortType = st[this.sortType];
25698     }
25699     
25700     // set default sortType for strings and dates
25701     if(!this.sortType){
25702         switch(this.type){
25703             case "string":
25704                 this.sortType = st.asUCString;
25705                 break;
25706             case "date":
25707                 this.sortType = st.asDate;
25708                 break;
25709             default:
25710                 this.sortType = st.none;
25711         }
25712     }
25713
25714     // define once
25715     var stripRe = /[\$,%]/g;
25716
25717     // prebuilt conversion function for this field, instead of
25718     // switching every time we're reading a value
25719     if(!this.convert){
25720         var cv, dateFormat = this.dateFormat;
25721         switch(this.type){
25722             case "":
25723             case "auto":
25724             case undefined:
25725                 cv = function(v){ return v; };
25726                 break;
25727             case "string":
25728                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
25729                 break;
25730             case "int":
25731                 cv = function(v){
25732                     return v !== undefined && v !== null && v !== '' ?
25733                            parseInt(String(v).replace(stripRe, ""), 10) : '';
25734                     };
25735                 break;
25736             case "float":
25737                 cv = function(v){
25738                     return v !== undefined && v !== null && v !== '' ?
25739                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
25740                     };
25741                 break;
25742             case "bool":
25743             case "boolean":
25744                 cv = function(v){ return v === true || v === "true" || v == 1; };
25745                 break;
25746             case "date":
25747                 cv = function(v){
25748                     if(!v){
25749                         return '';
25750                     }
25751                     if(v instanceof Date){
25752                         return v;
25753                     }
25754                     if(dateFormat){
25755                         if(dateFormat == "timestamp"){
25756                             return new Date(v*1000);
25757                         }
25758                         return Date.parseDate(v, dateFormat);
25759                     }
25760                     var parsed = Date.parse(v);
25761                     return parsed ? new Date(parsed) : null;
25762                 };
25763              break;
25764             
25765         }
25766         this.convert = cv;
25767     }
25768 };
25769
25770 Roo.data.Field.prototype = {
25771     dateFormat: null,
25772     defaultValue: "",
25773     mapping: null,
25774     sortType : null,
25775     sortDir : "ASC"
25776 };/*
25777  * Based on:
25778  * Ext JS Library 1.1.1
25779  * Copyright(c) 2006-2007, Ext JS, LLC.
25780  *
25781  * Originally Released Under LGPL - original licence link has changed is not relivant.
25782  *
25783  * Fork - LGPL
25784  * <script type="text/javascript">
25785  */
25786  
25787 // Base class for reading structured data from a data source.  This class is intended to be
25788 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
25789
25790 /**
25791  * @class Roo.data.DataReader
25792  * @abstract
25793  * Base class for reading structured data from a data source.  This class is intended to be
25794  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
25795  */
25796
25797 Roo.data.DataReader = function(meta, recordType){
25798     
25799     this.meta = meta;
25800     
25801     this.recordType = recordType instanceof Array ? 
25802         Roo.data.Record.create(recordType) : recordType;
25803 };
25804
25805 Roo.data.DataReader.prototype = {
25806     
25807     
25808     readerType : 'Data',
25809      /**
25810      * Create an empty record
25811      * @param {Object} data (optional) - overlay some values
25812      * @return {Roo.data.Record} record created.
25813      */
25814     newRow :  function(d) {
25815         var da =  {};
25816         this.recordType.prototype.fields.each(function(c) {
25817             switch( c.type) {
25818                 case 'int' : da[c.name] = 0; break;
25819                 case 'date' : da[c.name] = new Date(); break;
25820                 case 'float' : da[c.name] = 0.0; break;
25821                 case 'boolean' : da[c.name] = false; break;
25822                 default : da[c.name] = ""; break;
25823             }
25824             
25825         });
25826         return new this.recordType(Roo.apply(da, d));
25827     }
25828     
25829     
25830 };/*
25831  * Based on:
25832  * Ext JS Library 1.1.1
25833  * Copyright(c) 2006-2007, Ext JS, LLC.
25834  *
25835  * Originally Released Under LGPL - original licence link has changed is not relivant.
25836  *
25837  * Fork - LGPL
25838  * <script type="text/javascript">
25839  */
25840
25841 /**
25842  * @class Roo.data.DataProxy
25843  * @extends Roo.util.Observable
25844  * @abstract
25845  * This class is an abstract base class for implementations which provide retrieval of
25846  * unformatted data objects.<br>
25847  * <p>
25848  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
25849  * (of the appropriate type which knows how to parse the data object) to provide a block of
25850  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
25851  * <p>
25852  * Custom implementations must implement the load method as described in
25853  * {@link Roo.data.HttpProxy#load}.
25854  */
25855 Roo.data.DataProxy = function(){
25856     this.addEvents({
25857         /**
25858          * @event beforeload
25859          * Fires before a network request is made to retrieve a data object.
25860          * @param {Object} This DataProxy object.
25861          * @param {Object} params The params parameter to the load function.
25862          */
25863         beforeload : true,
25864         /**
25865          * @event load
25866          * Fires before the load method's callback is called.
25867          * @param {Object} This DataProxy object.
25868          * @param {Object} o The data object.
25869          * @param {Object} arg The callback argument object passed to the load function.
25870          */
25871         load : true,
25872         /**
25873          * @event loadexception
25874          * Fires if an Exception occurs during data retrieval.
25875          * @param {Object} This DataProxy object.
25876          * @param {Object} o The data object.
25877          * @param {Object} arg The callback argument object passed to the load function.
25878          * @param {Object} e The Exception.
25879          */
25880         loadexception : true
25881     });
25882     Roo.data.DataProxy.superclass.constructor.call(this);
25883 };
25884
25885 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
25886
25887     /**
25888      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
25889      */
25890 /*
25891  * Based on:
25892  * Ext JS Library 1.1.1
25893  * Copyright(c) 2006-2007, Ext JS, LLC.
25894  *
25895  * Originally Released Under LGPL - original licence link has changed is not relivant.
25896  *
25897  * Fork - LGPL
25898  * <script type="text/javascript">
25899  */
25900 /**
25901  * @class Roo.data.MemoryProxy
25902  * @extends Roo.data.DataProxy
25903  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
25904  * to the Reader when its load method is called.
25905  * @constructor
25906  * @param {Object} config  A config object containing the objects needed for the Store to access data,
25907  */
25908 Roo.data.MemoryProxy = function(config){
25909     var data = config;
25910     if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
25911         data = config.data;
25912     }
25913     Roo.data.MemoryProxy.superclass.constructor.call(this);
25914     this.data = data;
25915 };
25916
25917 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
25918     
25919     /**
25920      *  @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
25921      */
25922     /**
25923      * Load data from the requested source (in this case an in-memory
25924      * data object passed to the constructor), read the data object into
25925      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25926      * process that block using the passed callback.
25927      * @param {Object} params This parameter is not used by the MemoryProxy class.
25928      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25929      * object into a block of Roo.data.Records.
25930      * @param {Function} callback The function into which to pass the block of Roo.data.records.
25931      * The function must be passed <ul>
25932      * <li>The Record block object</li>
25933      * <li>The "arg" argument from the load function</li>
25934      * <li>A boolean success indicator</li>
25935      * </ul>
25936      * @param {Object} scope The scope in which to call the callback
25937      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25938      */
25939     load : function(params, reader, callback, scope, arg){
25940         params = params || {};
25941         var result;
25942         try {
25943             result = reader.readRecords(params.data ? params.data :this.data);
25944         }catch(e){
25945             this.fireEvent("loadexception", this, arg, null, e);
25946             callback.call(scope, null, arg, false);
25947             return;
25948         }
25949         callback.call(scope, result, arg, true);
25950     },
25951     
25952     // private
25953     update : function(params, records){
25954         
25955     }
25956 });/*
25957  * Based on:
25958  * Ext JS Library 1.1.1
25959  * Copyright(c) 2006-2007, Ext JS, LLC.
25960  *
25961  * Originally Released Under LGPL - original licence link has changed is not relivant.
25962  *
25963  * Fork - LGPL
25964  * <script type="text/javascript">
25965  */
25966 /**
25967  * @class Roo.data.HttpProxy
25968  * @extends Roo.data.DataProxy
25969  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
25970  * configured to reference a certain URL.<br><br>
25971  * <p>
25972  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
25973  * from which the running page was served.<br><br>
25974  * <p>
25975  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
25976  * <p>
25977  * Be aware that to enable the browser to parse an XML document, the server must set
25978  * the Content-Type header in the HTTP response to "text/xml".
25979  * @constructor
25980  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
25981  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
25982  * will be used to make the request.
25983  */
25984 Roo.data.HttpProxy = function(conn){
25985     Roo.data.HttpProxy.superclass.constructor.call(this);
25986     // is conn a conn config or a real conn?
25987     this.conn = conn;
25988     this.useAjax = !conn || !conn.events;
25989   
25990 };
25991
25992 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
25993     // thse are take from connection...
25994     
25995     /**
25996      * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
25997      */
25998     /**
25999      * @cfg {Object} extraParams  An object containing properties which are used as
26000      * extra parameters to each request made by this object. (defaults to undefined)
26001      */
26002     /**
26003      * @cfg {Object} defaultHeaders   An object containing request headers which are added
26004      *  to each request made by this object. (defaults to undefined)
26005      */
26006     /**
26007      * @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)
26008      */
26009     /**
26010      * @cfg {Number} timeout The timeout in milliseconds to be used for requests. (defaults to 30000)
26011      */
26012      /**
26013      * @cfg {Boolean} autoAbort Whether this request should abort any pending requests. (defaults to false)
26014      * @type Boolean
26015      */
26016   
26017
26018     /**
26019      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
26020      * @type Boolean
26021      */
26022     /**
26023      * Return the {@link Roo.data.Connection} object being used by this Proxy.
26024      * @return {Connection} The Connection object. This object may be used to subscribe to events on
26025      * a finer-grained basis than the DataProxy events.
26026      */
26027     getConnection : function(){
26028         return this.useAjax ? Roo.Ajax : this.conn;
26029     },
26030
26031     /**
26032      * Load data from the configured {@link Roo.data.Connection}, read the data object into
26033      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
26034      * process that block using the passed callback.
26035      * @param {Object} params An object containing properties which are to be used as HTTP parameters
26036      * for the request to the remote server.
26037      * @param {Roo.data.DataReader} reader The Reader object which converts the data
26038      * object into a block of Roo.data.Records.
26039      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
26040      * The function must be passed <ul>
26041      * <li>The Record block object</li>
26042      * <li>The "arg" argument from the load function</li>
26043      * <li>A boolean success indicator</li>
26044      * </ul>
26045      * @param {Object} scope The scope in which to call the callback
26046      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
26047      */
26048     load : function(params, reader, callback, scope, arg){
26049         if(this.fireEvent("beforeload", this, params) !== false){
26050             var  o = {
26051                 params : params || {},
26052                 request: {
26053                     callback : callback,
26054                     scope : scope,
26055                     arg : arg
26056                 },
26057                 reader: reader,
26058                 callback : this.loadResponse,
26059                 scope: this
26060             };
26061             if(this.useAjax){
26062                 Roo.applyIf(o, this.conn);
26063                 if(this.activeRequest){
26064                     Roo.Ajax.abort(this.activeRequest);
26065                 }
26066                 this.activeRequest = Roo.Ajax.request(o);
26067             }else{
26068                 this.conn.request(o);
26069             }
26070         }else{
26071             callback.call(scope||this, null, arg, false);
26072         }
26073     },
26074
26075     // private
26076     loadResponse : function(o, success, response){
26077         delete this.activeRequest;
26078         if(!success){
26079             this.fireEvent("loadexception", this, o, response);
26080             o.request.callback.call(o.request.scope, null, o.request.arg, false);
26081             return;
26082         }
26083         var result;
26084         try {
26085             result = o.reader.read(response);
26086         }catch(e){
26087             o.success = false;
26088             o.raw = { errorMsg : response.responseText };
26089             this.fireEvent("loadexception", this, o, response, e);
26090             o.request.callback.call(o.request.scope, o, o.request.arg, false);
26091             return;
26092         }
26093         
26094         this.fireEvent("load", this, o, o.request.arg);
26095         o.request.callback.call(o.request.scope, result, o.request.arg, true);
26096     },
26097
26098     // private
26099     update : function(dataSet){
26100
26101     },
26102
26103     // private
26104     updateResponse : function(dataSet){
26105
26106     }
26107 });/*
26108  * Based on:
26109  * Ext JS Library 1.1.1
26110  * Copyright(c) 2006-2007, Ext JS, LLC.
26111  *
26112  * Originally Released Under LGPL - original licence link has changed is not relivant.
26113  *
26114  * Fork - LGPL
26115  * <script type="text/javascript">
26116  */
26117
26118 /**
26119  * @class Roo.data.ScriptTagProxy
26120  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
26121  * other than the originating domain of the running page.<br><br>
26122  * <p>
26123  * <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
26124  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
26125  * <p>
26126  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
26127  * source code that is used as the source inside a &lt;script> tag.<br><br>
26128  * <p>
26129  * In order for the browser to process the returned data, the server must wrap the data object
26130  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
26131  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
26132  * depending on whether the callback name was passed:
26133  * <p>
26134  * <pre><code>
26135 boolean scriptTag = false;
26136 String cb = request.getParameter("callback");
26137 if (cb != null) {
26138     scriptTag = true;
26139     response.setContentType("text/javascript");
26140 } else {
26141     response.setContentType("application/x-json");
26142 }
26143 Writer out = response.getWriter();
26144 if (scriptTag) {
26145     out.write(cb + "(");
26146 }
26147 out.print(dataBlock.toJsonString());
26148 if (scriptTag) {
26149     out.write(");");
26150 }
26151 </pre></code>
26152  *
26153  * @constructor
26154  * @param {Object} config A configuration object.
26155  */
26156 Roo.data.ScriptTagProxy = function(config){
26157     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
26158     Roo.apply(this, config);
26159     this.head = document.getElementsByTagName("head")[0];
26160 };
26161
26162 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
26163
26164 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
26165     /**
26166      * @cfg {String} url The URL from which to request the data object.
26167      */
26168     /**
26169      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
26170      */
26171     timeout : 30000,
26172     /**
26173      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
26174      * the server the name of the callback function set up by the load call to process the returned data object.
26175      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
26176      * javascript output which calls this named function passing the data object as its only parameter.
26177      */
26178     callbackParam : "callback",
26179     /**
26180      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
26181      * name to the request.
26182      */
26183     nocache : true,
26184
26185     /**
26186      * Load data from the configured URL, read the data object into
26187      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
26188      * process that block using the passed callback.
26189      * @param {Object} params An object containing properties which are to be used as HTTP parameters
26190      * for the request to the remote server.
26191      * @param {Roo.data.DataReader} reader The Reader object which converts the data
26192      * object into a block of Roo.data.Records.
26193      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
26194      * The function must be passed <ul>
26195      * <li>The Record block object</li>
26196      * <li>The "arg" argument from the load function</li>
26197      * <li>A boolean success indicator</li>
26198      * </ul>
26199      * @param {Object} scope The scope in which to call the callback
26200      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
26201      */
26202     load : function(params, reader, callback, scope, arg){
26203         if(this.fireEvent("beforeload", this, params) !== false){
26204
26205             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
26206
26207             var url = this.url;
26208             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
26209             if(this.nocache){
26210                 url += "&_dc=" + (new Date().getTime());
26211             }
26212             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
26213             var trans = {
26214                 id : transId,
26215                 cb : "stcCallback"+transId,
26216                 scriptId : "stcScript"+transId,
26217                 params : params,
26218                 arg : arg,
26219                 url : url,
26220                 callback : callback,
26221                 scope : scope,
26222                 reader : reader
26223             };
26224             var conn = this;
26225
26226             window[trans.cb] = function(o){
26227                 conn.handleResponse(o, trans);
26228             };
26229
26230             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
26231
26232             if(this.autoAbort !== false){
26233                 this.abort();
26234             }
26235
26236             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
26237
26238             var script = document.createElement("script");
26239             script.setAttribute("src", url);
26240             script.setAttribute("type", "text/javascript");
26241             script.setAttribute("id", trans.scriptId);
26242             this.head.appendChild(script);
26243
26244             this.trans = trans;
26245         }else{
26246             callback.call(scope||this, null, arg, false);
26247         }
26248     },
26249
26250     // private
26251     isLoading : function(){
26252         return this.trans ? true : false;
26253     },
26254
26255     /**
26256      * Abort the current server request.
26257      */
26258     abort : function(){
26259         if(this.isLoading()){
26260             this.destroyTrans(this.trans);
26261         }
26262     },
26263
26264     // private
26265     destroyTrans : function(trans, isLoaded){
26266         this.head.removeChild(document.getElementById(trans.scriptId));
26267         clearTimeout(trans.timeoutId);
26268         if(isLoaded){
26269             window[trans.cb] = undefined;
26270             try{
26271                 delete window[trans.cb];
26272             }catch(e){}
26273         }else{
26274             // if hasn't been loaded, wait for load to remove it to prevent script error
26275             window[trans.cb] = function(){
26276                 window[trans.cb] = undefined;
26277                 try{
26278                     delete window[trans.cb];
26279                 }catch(e){}
26280             };
26281         }
26282     },
26283
26284     // private
26285     handleResponse : function(o, trans){
26286         this.trans = false;
26287         this.destroyTrans(trans, true);
26288         var result;
26289         try {
26290             result = trans.reader.readRecords(o);
26291         }catch(e){
26292             this.fireEvent("loadexception", this, o, trans.arg, e);
26293             trans.callback.call(trans.scope||window, null, trans.arg, false);
26294             return;
26295         }
26296         this.fireEvent("load", this, o, trans.arg);
26297         trans.callback.call(trans.scope||window, result, trans.arg, true);
26298     },
26299
26300     // private
26301     handleFailure : function(trans){
26302         this.trans = false;
26303         this.destroyTrans(trans, false);
26304         this.fireEvent("loadexception", this, null, trans.arg);
26305         trans.callback.call(trans.scope||window, null, trans.arg, false);
26306     }
26307 });/*
26308  * Based on:
26309  * Ext JS Library 1.1.1
26310  * Copyright(c) 2006-2007, Ext JS, LLC.
26311  *
26312  * Originally Released Under LGPL - original licence link has changed is not relivant.
26313  *
26314  * Fork - LGPL
26315  * <script type="text/javascript">
26316  */
26317
26318 /**
26319  * @class Roo.data.JsonReader
26320  * @extends Roo.data.DataReader
26321  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
26322  * based on mappings in a provided Roo.data.Record constructor.
26323  * 
26324  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
26325  * in the reply previously. 
26326  * 
26327  * <p>
26328  * Example code:
26329  * <pre><code>
26330 var RecordDef = Roo.data.Record.create([
26331     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
26332     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
26333 ]);
26334 var myReader = new Roo.data.JsonReader({
26335     totalProperty: "results",    // The property which contains the total dataset size (optional)
26336     root: "rows",                // The property which contains an Array of row objects
26337     id: "id"                     // The property within each row object that provides an ID for the record (optional)
26338 }, RecordDef);
26339 </code></pre>
26340  * <p>
26341  * This would consume a JSON file like this:
26342  * <pre><code>
26343 { 'results': 2, 'rows': [
26344     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
26345     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
26346 }
26347 </code></pre>
26348  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
26349  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
26350  * paged from the remote server.
26351  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
26352  * @cfg {String} root name of the property which contains the Array of row objects.
26353  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26354  * @cfg {Array} fields Array of field definition objects
26355  * @constructor
26356  * Create a new JsonReader
26357  * @param {Object} meta Metadata configuration options
26358  * @param {Object} recordType Either an Array of field definition objects,
26359  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
26360  */
26361 Roo.data.JsonReader = function(meta, recordType){
26362     
26363     meta = meta || {};
26364     // set some defaults:
26365     Roo.applyIf(meta, {
26366         totalProperty: 'total',
26367         successProperty : 'success',
26368         root : 'data',
26369         id : 'id'
26370     });
26371     
26372     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26373 };
26374 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
26375     
26376     readerType : 'Json',
26377     
26378     /**
26379      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
26380      * Used by Store query builder to append _requestMeta to params.
26381      * 
26382      */
26383     metaFromRemote : false,
26384     /**
26385      * This method is only used by a DataProxy which has retrieved data from a remote server.
26386      * @param {Object} response The XHR object which contains the JSON data in its responseText.
26387      * @return {Object} data A data block which is used by an Roo.data.Store object as
26388      * a cache of Roo.data.Records.
26389      */
26390     read : function(response){
26391         var json = response.responseText;
26392        
26393         var o = /* eval:var:o */ eval("("+json+")");
26394         if(!o) {
26395             throw {message: "JsonReader.read: Json object not found"};
26396         }
26397         
26398         if(o.metaData){
26399             
26400             delete this.ef;
26401             this.metaFromRemote = true;
26402             this.meta = o.metaData;
26403             this.recordType = Roo.data.Record.create(o.metaData.fields);
26404             this.onMetaChange(this.meta, this.recordType, o);
26405         }
26406         return this.readRecords(o);
26407     },
26408
26409     // private function a store will implement
26410     onMetaChange : function(meta, recordType, o){
26411
26412     },
26413
26414     /**
26415          * @ignore
26416          */
26417     simpleAccess: function(obj, subsc) {
26418         return obj[subsc];
26419     },
26420
26421         /**
26422          * @ignore
26423          */
26424     getJsonAccessor: function(){
26425         var re = /[\[\.]/;
26426         return function(expr) {
26427             try {
26428                 return(re.test(expr))
26429                     ? new Function("obj", "return obj." + expr)
26430                     : function(obj){
26431                         return obj[expr];
26432                     };
26433             } catch(e){}
26434             return Roo.emptyFn;
26435         };
26436     }(),
26437
26438     /**
26439      * Create a data block containing Roo.data.Records from an XML document.
26440      * @param {Object} o An object which contains an Array of row objects in the property specified
26441      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
26442      * which contains the total size of the dataset.
26443      * @return {Object} data A data block which is used by an Roo.data.Store object as
26444      * a cache of Roo.data.Records.
26445      */
26446     readRecords : function(o){
26447         /**
26448          * After any data loads, the raw JSON data is available for further custom processing.
26449          * @type Object
26450          */
26451         this.o = o;
26452         var s = this.meta, Record = this.recordType,
26453             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
26454
26455 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
26456         if (!this.ef) {
26457             if(s.totalProperty) {
26458                     this.getTotal = this.getJsonAccessor(s.totalProperty);
26459                 }
26460                 if(s.successProperty) {
26461                     this.getSuccess = this.getJsonAccessor(s.successProperty);
26462                 }
26463                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
26464                 if (s.id) {
26465                         var g = this.getJsonAccessor(s.id);
26466                         this.getId = function(rec) {
26467                                 var r = g(rec);  
26468                                 return (r === undefined || r === "") ? null : r;
26469                         };
26470                 } else {
26471                         this.getId = function(){return null;};
26472                 }
26473             this.ef = [];
26474             for(var jj = 0; jj < fl; jj++){
26475                 f = fi[jj];
26476                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
26477                 this.ef[jj] = this.getJsonAccessor(map);
26478             }
26479         }
26480
26481         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
26482         if(s.totalProperty){
26483             var vt = parseInt(this.getTotal(o), 10);
26484             if(!isNaN(vt)){
26485                 totalRecords = vt;
26486             }
26487         }
26488         if(s.successProperty){
26489             var vs = this.getSuccess(o);
26490             if(vs === false || vs === 'false'){
26491                 success = false;
26492             }
26493         }
26494         var records = [];
26495         for(var i = 0; i < c; i++){
26496             var n = root[i];
26497             var values = {};
26498             var id = this.getId(n);
26499             for(var j = 0; j < fl; j++){
26500                 f = fi[j];
26501                                 var v = this.ef[j](n);
26502                                 if (!f.convert) {
26503                                         Roo.log('missing convert for ' + f.name);
26504                                         Roo.log(f);
26505                                         continue;
26506                                 }
26507                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
26508             }
26509                         if (!Record) {
26510                                 return {
26511                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
26512                                         success : false,
26513                                         records : [],
26514                                         totalRecords : 0
26515                                 };
26516                         }
26517             var record = new Record(values, id);
26518             record.json = n;
26519             records[i] = record;
26520         }
26521         return {
26522             raw : o,
26523             success : success,
26524             records : records,
26525             totalRecords : totalRecords
26526         };
26527     },
26528     // used when loading children.. @see loadDataFromChildren
26529     toLoadData: function(rec)
26530     {
26531         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26532         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26533         return { data : data, total : data.length };
26534         
26535     }
26536 });/*
26537  * Based on:
26538  * Ext JS Library 1.1.1
26539  * Copyright(c) 2006-2007, Ext JS, LLC.
26540  *
26541  * Originally Released Under LGPL - original licence link has changed is not relivant.
26542  *
26543  * Fork - LGPL
26544  * <script type="text/javascript">
26545  */
26546
26547 /**
26548  * @class Roo.data.XmlReader
26549  * @extends Roo.data.DataReader
26550  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
26551  * based on mappings in a provided Roo.data.Record constructor.<br><br>
26552  * <p>
26553  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
26554  * header in the HTTP response must be set to "text/xml".</em>
26555  * <p>
26556  * Example code:
26557  * <pre><code>
26558 var RecordDef = Roo.data.Record.create([
26559    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
26560    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
26561 ]);
26562 var myReader = new Roo.data.XmlReader({
26563    totalRecords: "results", // The element which contains the total dataset size (optional)
26564    record: "row",           // The repeated element which contains row information
26565    id: "id"                 // The element within the row that provides an ID for the record (optional)
26566 }, RecordDef);
26567 </code></pre>
26568  * <p>
26569  * This would consume an XML file like this:
26570  * <pre><code>
26571 &lt;?xml?>
26572 &lt;dataset>
26573  &lt;results>2&lt;/results>
26574  &lt;row>
26575    &lt;id>1&lt;/id>
26576    &lt;name>Bill&lt;/name>
26577    &lt;occupation>Gardener&lt;/occupation>
26578  &lt;/row>
26579  &lt;row>
26580    &lt;id>2&lt;/id>
26581    &lt;name>Ben&lt;/name>
26582    &lt;occupation>Horticulturalist&lt;/occupation>
26583  &lt;/row>
26584 &lt;/dataset>
26585 </code></pre>
26586  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
26587  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
26588  * paged from the remote server.
26589  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
26590  * @cfg {String} success The DomQuery path to the success attribute used by forms.
26591  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
26592  * a record identifier value.
26593  * @constructor
26594  * Create a new XmlReader
26595  * @param {Object} meta Metadata configuration options
26596  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
26597  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
26598  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
26599  */
26600 Roo.data.XmlReader = function(meta, recordType){
26601     meta = meta || {};
26602     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26603 };
26604 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
26605     
26606     readerType : 'Xml',
26607     
26608     /**
26609      * This method is only used by a DataProxy which has retrieved data from a remote server.
26610          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
26611          * to contain a method called 'responseXML' that returns an XML document object.
26612      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26613      * a cache of Roo.data.Records.
26614      */
26615     read : function(response){
26616         var doc = response.responseXML;
26617         if(!doc) {
26618             throw {message: "XmlReader.read: XML Document not available"};
26619         }
26620         return this.readRecords(doc);
26621     },
26622
26623     /**
26624      * Create a data block containing Roo.data.Records from an XML document.
26625          * @param {Object} doc A parsed XML document.
26626      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26627      * a cache of Roo.data.Records.
26628      */
26629     readRecords : function(doc){
26630         /**
26631          * After any data loads/reads, the raw XML Document is available for further custom processing.
26632          * @type XMLDocument
26633          */
26634         this.xmlData = doc;
26635         var root = doc.documentElement || doc;
26636         var q = Roo.DomQuery;
26637         var recordType = this.recordType, fields = recordType.prototype.fields;
26638         var sid = this.meta.id;
26639         var totalRecords = 0, success = true;
26640         if(this.meta.totalRecords){
26641             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
26642         }
26643         
26644         if(this.meta.success){
26645             var sv = q.selectValue(this.meta.success, root, true);
26646             success = sv !== false && sv !== 'false';
26647         }
26648         var records = [];
26649         var ns = q.select(this.meta.record, root);
26650         for(var i = 0, len = ns.length; i < len; i++) {
26651                 var n = ns[i];
26652                 var values = {};
26653                 var id = sid ? q.selectValue(sid, n) : undefined;
26654                 for(var j = 0, jlen = fields.length; j < jlen; j++){
26655                     var f = fields.items[j];
26656                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
26657                     v = f.convert(v);
26658                     values[f.name] = v;
26659                 }
26660                 var record = new recordType(values, id);
26661                 record.node = n;
26662                 records[records.length] = record;
26663             }
26664
26665             return {
26666                 success : success,
26667                 records : records,
26668                 totalRecords : totalRecords || records.length
26669             };
26670     }
26671 });/*
26672  * Based on:
26673  * Ext JS Library 1.1.1
26674  * Copyright(c) 2006-2007, Ext JS, LLC.
26675  *
26676  * Originally Released Under LGPL - original licence link has changed is not relivant.
26677  *
26678  * Fork - LGPL
26679  * <script type="text/javascript">
26680  */
26681
26682 /**
26683  * @class Roo.data.ArrayReader
26684  * @extends Roo.data.DataReader
26685  * Data reader class to create an Array of Roo.data.Record objects from an Array.
26686  * Each element of that Array represents a row of data fields. The
26687  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
26688  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
26689  * <p>
26690  * Example code:.
26691  * <pre><code>
26692 var RecordDef = Roo.data.Record.create([
26693     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
26694     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
26695 ]);
26696 var myReader = new Roo.data.ArrayReader({
26697     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
26698 }, RecordDef);
26699 </code></pre>
26700  * <p>
26701  * This would consume an Array like this:
26702  * <pre><code>
26703 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
26704   </code></pre>
26705  
26706  * @constructor
26707  * Create a new JsonReader
26708  * @param {Object} meta Metadata configuration options.
26709  * @param {Object|Array} recordType Either an Array of field definition objects
26710  * 
26711  * @cfg {Array} fields Array of field definition objects
26712  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26713  * as specified to {@link Roo.data.Record#create},
26714  * or an {@link Roo.data.Record} object
26715  *
26716  * 
26717  * created using {@link Roo.data.Record#create}.
26718  */
26719 Roo.data.ArrayReader = function(meta, recordType)
26720 {    
26721     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26722 };
26723
26724 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
26725     
26726       /**
26727      * Create a data block containing Roo.data.Records from an XML document.
26728      * @param {Object} o An Array of row objects which represents the dataset.
26729      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
26730      * a cache of Roo.data.Records.
26731      */
26732     readRecords : function(o)
26733     {
26734         var sid = this.meta ? this.meta.id : null;
26735         var recordType = this.recordType, fields = recordType.prototype.fields;
26736         var records = [];
26737         var root = o;
26738         for(var i = 0; i < root.length; i++){
26739             var n = root[i];
26740             var values = {};
26741             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
26742             for(var j = 0, jlen = fields.length; j < jlen; j++){
26743                 var f = fields.items[j];
26744                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
26745                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
26746                 v = f.convert(v);
26747                 values[f.name] = v;
26748             }
26749             var record = new recordType(values, id);
26750             record.json = n;
26751             records[records.length] = record;
26752         }
26753         return {
26754             records : records,
26755             totalRecords : records.length
26756         };
26757     },
26758     // used when loading children.. @see loadDataFromChildren
26759     toLoadData: function(rec)
26760     {
26761         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26762         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26763         
26764     }
26765     
26766     
26767 });/*
26768  * Based on:
26769  * Ext JS Library 1.1.1
26770  * Copyright(c) 2006-2007, Ext JS, LLC.
26771  *
26772  * Originally Released Under LGPL - original licence link has changed is not relivant.
26773  *
26774  * Fork - LGPL
26775  * <script type="text/javascript">
26776  */
26777
26778
26779 /**
26780  * @class Roo.data.Tree
26781  * @extends Roo.util.Observable
26782  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
26783  * in the tree have most standard DOM functionality.
26784  * @constructor
26785  * @param {Node} root (optional) The root node
26786  */
26787 Roo.data.Tree = function(root){
26788    this.nodeHash = {};
26789    /**
26790     * The root node for this tree
26791     * @type Node
26792     */
26793    this.root = null;
26794    if(root){
26795        this.setRootNode(root);
26796    }
26797    this.addEvents({
26798        /**
26799         * @event append
26800         * Fires when a new child node is appended to a node in this tree.
26801         * @param {Tree} tree The owner tree
26802         * @param {Node} parent The parent node
26803         * @param {Node} node The newly appended node
26804         * @param {Number} index The index of the newly appended node
26805         */
26806        "append" : true,
26807        /**
26808         * @event remove
26809         * Fires when a child node is removed from a node in this tree.
26810         * @param {Tree} tree The owner tree
26811         * @param {Node} parent The parent node
26812         * @param {Node} node The child node removed
26813         */
26814        "remove" : true,
26815        /**
26816         * @event move
26817         * Fires when a node is moved to a new location in the tree
26818         * @param {Tree} tree The owner tree
26819         * @param {Node} node The node moved
26820         * @param {Node} oldParent The old parent of this node
26821         * @param {Node} newParent The new parent of this node
26822         * @param {Number} index The index it was moved to
26823         */
26824        "move" : true,
26825        /**
26826         * @event insert
26827         * Fires when a new child node is inserted in a node in this tree.
26828         * @param {Tree} tree The owner tree
26829         * @param {Node} parent The parent node
26830         * @param {Node} node The child node inserted
26831         * @param {Node} refNode The child node the node was inserted before
26832         */
26833        "insert" : true,
26834        /**
26835         * @event beforeappend
26836         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
26837         * @param {Tree} tree The owner tree
26838         * @param {Node} parent The parent node
26839         * @param {Node} node The child node to be appended
26840         */
26841        "beforeappend" : true,
26842        /**
26843         * @event beforeremove
26844         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
26845         * @param {Tree} tree The owner tree
26846         * @param {Node} parent The parent node
26847         * @param {Node} node The child node to be removed
26848         */
26849        "beforeremove" : true,
26850        /**
26851         * @event beforemove
26852         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
26853         * @param {Tree} tree The owner tree
26854         * @param {Node} node The node being moved
26855         * @param {Node} oldParent The parent of the node
26856         * @param {Node} newParent The new parent the node is moving to
26857         * @param {Number} index The index it is being moved to
26858         */
26859        "beforemove" : true,
26860        /**
26861         * @event beforeinsert
26862         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
26863         * @param {Tree} tree The owner tree
26864         * @param {Node} parent The parent node
26865         * @param {Node} node The child node to be inserted
26866         * @param {Node} refNode The child node the node is being inserted before
26867         */
26868        "beforeinsert" : true
26869    });
26870
26871     Roo.data.Tree.superclass.constructor.call(this);
26872 };
26873
26874 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
26875     pathSeparator: "/",
26876
26877     proxyNodeEvent : function(){
26878         return this.fireEvent.apply(this, arguments);
26879     },
26880
26881     /**
26882      * Returns the root node for this tree.
26883      * @return {Node}
26884      */
26885     getRootNode : function(){
26886         return this.root;
26887     },
26888
26889     /**
26890      * Sets the root node for this tree.
26891      * @param {Node} node
26892      * @return {Node}
26893      */
26894     setRootNode : function(node){
26895         this.root = node;
26896         node.ownerTree = this;
26897         node.isRoot = true;
26898         this.registerNode(node);
26899         return node;
26900     },
26901
26902     /**
26903      * Gets a node in this tree by its id.
26904      * @param {String} id
26905      * @return {Node}
26906      */
26907     getNodeById : function(id){
26908         return this.nodeHash[id];
26909     },
26910
26911     registerNode : function(node){
26912         this.nodeHash[node.id] = node;
26913     },
26914
26915     unregisterNode : function(node){
26916         delete this.nodeHash[node.id];
26917     },
26918
26919     toString : function(){
26920         return "[Tree"+(this.id?" "+this.id:"")+"]";
26921     }
26922 });
26923
26924 /**
26925  * @class Roo.data.Node
26926  * @extends Roo.util.Observable
26927  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
26928  * @cfg {String} id The id for this node. If one is not specified, one is generated.
26929  * @constructor
26930  * @param {Object} attributes The attributes/config for the node
26931  */
26932 Roo.data.Node = function(attributes){
26933     /**
26934      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
26935      * @type {Object}
26936      */
26937     this.attributes = attributes || {};
26938     this.leaf = this.attributes.leaf;
26939     /**
26940      * The node id. @type String
26941      */
26942     this.id = this.attributes.id;
26943     if(!this.id){
26944         this.id = Roo.id(null, "ynode-");
26945         this.attributes.id = this.id;
26946     }
26947      
26948     
26949     /**
26950      * All child nodes of this node. @type Array
26951      */
26952     this.childNodes = [];
26953     if(!this.childNodes.indexOf){ // indexOf is a must
26954         this.childNodes.indexOf = function(o){
26955             for(var i = 0, len = this.length; i < len; i++){
26956                 if(this[i] == o) {
26957                     return i;
26958                 }
26959             }
26960             return -1;
26961         };
26962     }
26963     /**
26964      * The parent node for this node. @type Node
26965      */
26966     this.parentNode = null;
26967     /**
26968      * The first direct child node of this node, or null if this node has no child nodes. @type Node
26969      */
26970     this.firstChild = null;
26971     /**
26972      * The last direct child node of this node, or null if this node has no child nodes. @type Node
26973      */
26974     this.lastChild = null;
26975     /**
26976      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
26977      */
26978     this.previousSibling = null;
26979     /**
26980      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
26981      */
26982     this.nextSibling = null;
26983
26984     this.addEvents({
26985        /**
26986         * @event append
26987         * Fires when a new child node is appended
26988         * @param {Tree} tree The owner tree
26989         * @param {Node} this This node
26990         * @param {Node} node The newly appended node
26991         * @param {Number} index The index of the newly appended node
26992         */
26993        "append" : true,
26994        /**
26995         * @event remove
26996         * Fires when a child node is removed
26997         * @param {Tree} tree The owner tree
26998         * @param {Node} this This node
26999         * @param {Node} node The removed node
27000         */
27001        "remove" : true,
27002        /**
27003         * @event move
27004         * Fires when this node is moved to a new location in the tree
27005         * @param {Tree} tree The owner tree
27006         * @param {Node} this This node
27007         * @param {Node} oldParent The old parent of this node
27008         * @param {Node} newParent The new parent of this node
27009         * @param {Number} index The index it was moved to
27010         */
27011        "move" : true,
27012        /**
27013         * @event insert
27014         * Fires when a new child node is inserted.
27015         * @param {Tree} tree The owner tree
27016         * @param {Node} this This node
27017         * @param {Node} node The child node inserted
27018         * @param {Node} refNode The child node the node was inserted before
27019         */
27020        "insert" : true,
27021        /**
27022         * @event beforeappend
27023         * Fires before a new child is appended, return false to cancel the append.
27024         * @param {Tree} tree The owner tree
27025         * @param {Node} this This node
27026         * @param {Node} node The child node to be appended
27027         */
27028        "beforeappend" : true,
27029        /**
27030         * @event beforeremove
27031         * Fires before a child is removed, return false to cancel the remove.
27032         * @param {Tree} tree The owner tree
27033         * @param {Node} this This node
27034         * @param {Node} node The child node to be removed
27035         */
27036        "beforeremove" : true,
27037        /**
27038         * @event beforemove
27039         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
27040         * @param {Tree} tree The owner tree
27041         * @param {Node} this This node
27042         * @param {Node} oldParent The parent of this node
27043         * @param {Node} newParent The new parent this node is moving to
27044         * @param {Number} index The index it is being moved to
27045         */
27046        "beforemove" : true,
27047        /**
27048         * @event beforeinsert
27049         * Fires before a new child is inserted, return false to cancel the insert.
27050         * @param {Tree} tree The owner tree
27051         * @param {Node} this This node
27052         * @param {Node} node The child node to be inserted
27053         * @param {Node} refNode The child node the node is being inserted before
27054         */
27055        "beforeinsert" : true
27056    });
27057     this.listeners = this.attributes.listeners;
27058     Roo.data.Node.superclass.constructor.call(this);
27059 };
27060
27061 Roo.extend(Roo.data.Node, Roo.util.Observable, {
27062     fireEvent : function(evtName){
27063         // first do standard event for this node
27064         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
27065             return false;
27066         }
27067         // then bubble it up to the tree if the event wasn't cancelled
27068         var ot = this.getOwnerTree();
27069         if(ot){
27070             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
27071                 return false;
27072             }
27073         }
27074         return true;
27075     },
27076
27077     /**
27078      * Returns true if this node is a leaf
27079      * @return {Boolean}
27080      */
27081     isLeaf : function(){
27082         return this.leaf === true;
27083     },
27084
27085     // private
27086     setFirstChild : function(node){
27087         this.firstChild = node;
27088     },
27089
27090     //private
27091     setLastChild : function(node){
27092         this.lastChild = node;
27093     },
27094
27095
27096     /**
27097      * Returns true if this node is the last child of its parent
27098      * @return {Boolean}
27099      */
27100     isLast : function(){
27101        return (!this.parentNode ? true : this.parentNode.lastChild == this);
27102     },
27103
27104     /**
27105      * Returns true if this node is the first child of its parent
27106      * @return {Boolean}
27107      */
27108     isFirst : function(){
27109        return (!this.parentNode ? true : this.parentNode.firstChild == this);
27110     },
27111
27112     hasChildNodes : function(){
27113         return !this.isLeaf() && this.childNodes.length > 0;
27114     },
27115
27116     /**
27117      * Insert node(s) as the last child node of this node.
27118      * @param {Node/Array} node The node or Array of nodes to append
27119      * @return {Node} The appended node if single append, or null if an array was passed
27120      */
27121     appendChild : function(node){
27122         var multi = false;
27123         if(node instanceof Array){
27124             multi = node;
27125         }else if(arguments.length > 1){
27126             multi = arguments;
27127         }
27128         
27129         // if passed an array or multiple args do them one by one
27130         if(multi){
27131             for(var i = 0, len = multi.length; i < len; i++) {
27132                 this.appendChild(multi[i]);
27133             }
27134         }else{
27135             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
27136                 return false;
27137             }
27138             var index = this.childNodes.length;
27139             var oldParent = node.parentNode;
27140             // it's a move, make sure we move it cleanly
27141             if(oldParent){
27142                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
27143                     return false;
27144                 }
27145                 oldParent.removeChild(node);
27146             }
27147             
27148             index = this.childNodes.length;
27149             if(index == 0){
27150                 this.setFirstChild(node);
27151             }
27152             this.childNodes.push(node);
27153             node.parentNode = this;
27154             var ps = this.childNodes[index-1];
27155             if(ps){
27156                 node.previousSibling = ps;
27157                 ps.nextSibling = node;
27158             }else{
27159                 node.previousSibling = null;
27160             }
27161             node.nextSibling = null;
27162             this.setLastChild(node);
27163             node.setOwnerTree(this.getOwnerTree());
27164             this.fireEvent("append", this.ownerTree, this, node, index);
27165             if(this.ownerTree) {
27166                 this.ownerTree.fireEvent("appendnode", this, node, index);
27167             }
27168             if(oldParent){
27169                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
27170             }
27171             return node;
27172         }
27173     },
27174
27175     /**
27176      * Removes a child node from this node.
27177      * @param {Node} node The node to remove
27178      * @return {Node} The removed node
27179      */
27180     removeChild : function(node){
27181         var index = this.childNodes.indexOf(node);
27182         if(index == -1){
27183             return false;
27184         }
27185         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
27186             return false;
27187         }
27188
27189         // remove it from childNodes collection
27190         this.childNodes.splice(index, 1);
27191
27192         // update siblings
27193         if(node.previousSibling){
27194             node.previousSibling.nextSibling = node.nextSibling;
27195         }
27196         if(node.nextSibling){
27197             node.nextSibling.previousSibling = node.previousSibling;
27198         }
27199
27200         // update child refs
27201         if(this.firstChild == node){
27202             this.setFirstChild(node.nextSibling);
27203         }
27204         if(this.lastChild == node){
27205             this.setLastChild(node.previousSibling);
27206         }
27207
27208         node.setOwnerTree(null);
27209         // clear any references from the node
27210         node.parentNode = null;
27211         node.previousSibling = null;
27212         node.nextSibling = null;
27213         this.fireEvent("remove", this.ownerTree, this, node);
27214         return node;
27215     },
27216
27217     /**
27218      * Inserts the first node before the second node in this nodes childNodes collection.
27219      * @param {Node} node The node to insert
27220      * @param {Node} refNode The node to insert before (if null the node is appended)
27221      * @return {Node} The inserted node
27222      */
27223     insertBefore : function(node, refNode){
27224         if(!refNode){ // like standard Dom, refNode can be null for append
27225             return this.appendChild(node);
27226         }
27227         // nothing to do
27228         if(node == refNode){
27229             return false;
27230         }
27231
27232         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
27233             return false;
27234         }
27235         var index = this.childNodes.indexOf(refNode);
27236         var oldParent = node.parentNode;
27237         var refIndex = index;
27238
27239         // when moving internally, indexes will change after remove
27240         if(oldParent == this && this.childNodes.indexOf(node) < index){
27241             refIndex--;
27242         }
27243
27244         // it's a move, make sure we move it cleanly
27245         if(oldParent){
27246             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
27247                 return false;
27248             }
27249             oldParent.removeChild(node);
27250         }
27251         if(refIndex == 0){
27252             this.setFirstChild(node);
27253         }
27254         this.childNodes.splice(refIndex, 0, node);
27255         node.parentNode = this;
27256         var ps = this.childNodes[refIndex-1];
27257         if(ps){
27258             node.previousSibling = ps;
27259             ps.nextSibling = node;
27260         }else{
27261             node.previousSibling = null;
27262         }
27263         node.nextSibling = refNode;
27264         refNode.previousSibling = node;
27265         node.setOwnerTree(this.getOwnerTree());
27266         this.fireEvent("insert", this.ownerTree, this, node, refNode);
27267         if(oldParent){
27268             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
27269         }
27270         return node;
27271     },
27272
27273     /**
27274      * Returns the child node at the specified index.
27275      * @param {Number} index
27276      * @return {Node}
27277      */
27278     item : function(index){
27279         return this.childNodes[index];
27280     },
27281
27282     /**
27283      * Replaces one child node in this node with another.
27284      * @param {Node} newChild The replacement node
27285      * @param {Node} oldChild The node to replace
27286      * @return {Node} The replaced node
27287      */
27288     replaceChild : function(newChild, oldChild){
27289         this.insertBefore(newChild, oldChild);
27290         this.removeChild(oldChild);
27291         return oldChild;
27292     },
27293
27294     /**
27295      * Returns the index of a child node
27296      * @param {Node} node
27297      * @return {Number} The index of the node or -1 if it was not found
27298      */
27299     indexOf : function(child){
27300         return this.childNodes.indexOf(child);
27301     },
27302
27303     /**
27304      * Returns the tree this node is in.
27305      * @return {Tree}
27306      */
27307     getOwnerTree : function(){
27308         // if it doesn't have one, look for one
27309         if(!this.ownerTree){
27310             var p = this;
27311             while(p){
27312                 if(p.ownerTree){
27313                     this.ownerTree = p.ownerTree;
27314                     break;
27315                 }
27316                 p = p.parentNode;
27317             }
27318         }
27319         return this.ownerTree;
27320     },
27321
27322     /**
27323      * Returns depth of this node (the root node has a depth of 0)
27324      * @return {Number}
27325      */
27326     getDepth : function(){
27327         var depth = 0;
27328         var p = this;
27329         while(p.parentNode){
27330             ++depth;
27331             p = p.parentNode;
27332         }
27333         return depth;
27334     },
27335
27336     // private
27337     setOwnerTree : function(tree){
27338         // if it's move, we need to update everyone
27339         if(tree != this.ownerTree){
27340             if(this.ownerTree){
27341                 this.ownerTree.unregisterNode(this);
27342             }
27343             this.ownerTree = tree;
27344             var cs = this.childNodes;
27345             for(var i = 0, len = cs.length; i < len; i++) {
27346                 cs[i].setOwnerTree(tree);
27347             }
27348             if(tree){
27349                 tree.registerNode(this);
27350             }
27351         }
27352     },
27353
27354     /**
27355      * Returns the path for this node. The path can be used to expand or select this node programmatically.
27356      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
27357      * @return {String} The path
27358      */
27359     getPath : function(attr){
27360         attr = attr || "id";
27361         var p = this.parentNode;
27362         var b = [this.attributes[attr]];
27363         while(p){
27364             b.unshift(p.attributes[attr]);
27365             p = p.parentNode;
27366         }
27367         var sep = this.getOwnerTree().pathSeparator;
27368         return sep + b.join(sep);
27369     },
27370
27371     /**
27372      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
27373      * function call will be the scope provided or the current node. The arguments to the function
27374      * will be the args provided or the current node. If the function returns false at any point,
27375      * the bubble is stopped.
27376      * @param {Function} fn The function to call
27377      * @param {Object} scope (optional) The scope of the function (defaults to current node)
27378      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27379      */
27380     bubble : function(fn, scope, args){
27381         var p = this;
27382         while(p){
27383             if(fn.call(scope || p, args || p) === false){
27384                 break;
27385             }
27386             p = p.parentNode;
27387         }
27388     },
27389
27390     /**
27391      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
27392      * function call will be the scope provided or the current node. The arguments to the function
27393      * will be the args provided or the current node. If the function returns false at any point,
27394      * the cascade is stopped on that branch.
27395      * @param {Function} fn The function to call
27396      * @param {Object} scope (optional) The scope of the function (defaults to current node)
27397      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27398      */
27399     cascade : function(fn, scope, args){
27400         if(fn.call(scope || this, args || this) !== false){
27401             var cs = this.childNodes;
27402             for(var i = 0, len = cs.length; i < len; i++) {
27403                 cs[i].cascade(fn, scope, args);
27404             }
27405         }
27406     },
27407
27408     /**
27409      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
27410      * function call will be the scope provided or the current node. The arguments to the function
27411      * will be the args provided or the current node. If the function returns false at any point,
27412      * the iteration stops.
27413      * @param {Function} fn The function to call
27414      * @param {Object} scope (optional) The scope of the function (defaults to current node)
27415      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27416      */
27417     eachChild : function(fn, scope, args){
27418         var cs = this.childNodes;
27419         for(var i = 0, len = cs.length; i < len; i++) {
27420                 if(fn.call(scope || this, args || cs[i]) === false){
27421                     break;
27422                 }
27423         }
27424     },
27425
27426     /**
27427      * Finds the first child that has the attribute with the specified value.
27428      * @param {String} attribute The attribute name
27429      * @param {Mixed} value The value to search for
27430      * @return {Node} The found child or null if none was found
27431      */
27432     findChild : function(attribute, value){
27433         var cs = this.childNodes;
27434         for(var i = 0, len = cs.length; i < len; i++) {
27435                 if(cs[i].attributes[attribute] == value){
27436                     return cs[i];
27437                 }
27438         }
27439         return null;
27440     },
27441
27442     /**
27443      * Finds the first child by a custom function. The child matches if the function passed
27444      * returns true.
27445      * @param {Function} fn
27446      * @param {Object} scope (optional)
27447      * @return {Node} The found child or null if none was found
27448      */
27449     findChildBy : function(fn, scope){
27450         var cs = this.childNodes;
27451         for(var i = 0, len = cs.length; i < len; i++) {
27452                 if(fn.call(scope||cs[i], cs[i]) === true){
27453                     return cs[i];
27454                 }
27455         }
27456         return null;
27457     },
27458
27459     /**
27460      * Sorts this nodes children using the supplied sort function
27461      * @param {Function} fn
27462      * @param {Object} scope (optional)
27463      */
27464     sort : function(fn, scope){
27465         var cs = this.childNodes;
27466         var len = cs.length;
27467         if(len > 0){
27468             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
27469             cs.sort(sortFn);
27470             for(var i = 0; i < len; i++){
27471                 var n = cs[i];
27472                 n.previousSibling = cs[i-1];
27473                 n.nextSibling = cs[i+1];
27474                 if(i == 0){
27475                     this.setFirstChild(n);
27476                 }
27477                 if(i == len-1){
27478                     this.setLastChild(n);
27479                 }
27480             }
27481         }
27482     },
27483
27484     /**
27485      * Returns true if this node is an ancestor (at any point) of the passed node.
27486      * @param {Node} node
27487      * @return {Boolean}
27488      */
27489     contains : function(node){
27490         return node.isAncestor(this);
27491     },
27492
27493     /**
27494      * Returns true if the passed node is an ancestor (at any point) of this node.
27495      * @param {Node} node
27496      * @return {Boolean}
27497      */
27498     isAncestor : function(node){
27499         var p = this.parentNode;
27500         while(p){
27501             if(p == node){
27502                 return true;
27503             }
27504             p = p.parentNode;
27505         }
27506         return false;
27507     },
27508
27509     toString : function(){
27510         return "[Node"+(this.id?" "+this.id:"")+"]";
27511     }
27512 });/*
27513  * Based on:
27514  * Ext JS Library 1.1.1
27515  * Copyright(c) 2006-2007, Ext JS, LLC.
27516  *
27517  * Originally Released Under LGPL - original licence link has changed is not relivant.
27518  *
27519  * Fork - LGPL
27520  * <script type="text/javascript">
27521  */
27522
27523
27524 /**
27525  * @class Roo.Shadow
27526  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
27527  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
27528  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
27529  * @constructor
27530  * Create a new Shadow
27531  * @param {Object} config The config object
27532  */
27533 Roo.Shadow = function(config){
27534     Roo.apply(this, config);
27535     if(typeof this.mode != "string"){
27536         this.mode = this.defaultMode;
27537     }
27538     var o = this.offset, a = {h: 0};
27539     var rad = Math.floor(this.offset/2);
27540     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
27541         case "drop":
27542             a.w = 0;
27543             a.l = a.t = o;
27544             a.t -= 1;
27545             if(Roo.isIE){
27546                 a.l -= this.offset + rad;
27547                 a.t -= this.offset + rad;
27548                 a.w -= rad;
27549                 a.h -= rad;
27550                 a.t += 1;
27551             }
27552         break;
27553         case "sides":
27554             a.w = (o*2);
27555             a.l = -o;
27556             a.t = o-1;
27557             if(Roo.isIE){
27558                 a.l -= (this.offset - rad);
27559                 a.t -= this.offset + rad;
27560                 a.l += 1;
27561                 a.w -= (this.offset - rad)*2;
27562                 a.w -= rad + 1;
27563                 a.h -= 1;
27564             }
27565         break;
27566         case "frame":
27567             a.w = a.h = (o*2);
27568             a.l = a.t = -o;
27569             a.t += 1;
27570             a.h -= 2;
27571             if(Roo.isIE){
27572                 a.l -= (this.offset - rad);
27573                 a.t -= (this.offset - rad);
27574                 a.l += 1;
27575                 a.w -= (this.offset + rad + 1);
27576                 a.h -= (this.offset + rad);
27577                 a.h += 1;
27578             }
27579         break;
27580     };
27581
27582     this.adjusts = a;
27583 };
27584
27585 Roo.Shadow.prototype = {
27586     /**
27587      * @cfg {String} mode
27588      * The shadow display mode.  Supports the following options:<br />
27589      * sides: Shadow displays on both sides and bottom only<br />
27590      * frame: Shadow displays equally on all four sides<br />
27591      * drop: Traditional bottom-right drop shadow (default)
27592      */
27593     mode: false,
27594     /**
27595      * @cfg {String} offset
27596      * The number of pixels to offset the shadow from the element (defaults to 4)
27597      */
27598     offset: 4,
27599
27600     // private
27601     defaultMode: "drop",
27602
27603     /**
27604      * Displays the shadow under the target element
27605      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
27606      */
27607     show : function(target){
27608         target = Roo.get(target);
27609         if(!this.el){
27610             this.el = Roo.Shadow.Pool.pull();
27611             if(this.el.dom.nextSibling != target.dom){
27612                 this.el.insertBefore(target);
27613             }
27614         }
27615         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
27616         if(Roo.isIE){
27617             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
27618         }
27619         this.realign(
27620             target.getLeft(true),
27621             target.getTop(true),
27622             target.getWidth(),
27623             target.getHeight()
27624         );
27625         this.el.dom.style.display = "block";
27626     },
27627
27628     /**
27629      * Returns true if the shadow is visible, else false
27630      */
27631     isVisible : function(){
27632         return this.el ? true : false;  
27633     },
27634
27635     /**
27636      * Direct alignment when values are already available. Show must be called at least once before
27637      * calling this method to ensure it is initialized.
27638      * @param {Number} left The target element left position
27639      * @param {Number} top The target element top position
27640      * @param {Number} width The target element width
27641      * @param {Number} height The target element height
27642      */
27643     realign : function(l, t, w, h){
27644         if(!this.el){
27645             return;
27646         }
27647         var a = this.adjusts, d = this.el.dom, s = d.style;
27648         var iea = 0;
27649         s.left = (l+a.l)+"px";
27650         s.top = (t+a.t)+"px";
27651         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
27652  
27653         if(s.width != sws || s.height != shs){
27654             s.width = sws;
27655             s.height = shs;
27656             if(!Roo.isIE){
27657                 var cn = d.childNodes;
27658                 var sww = Math.max(0, (sw-12))+"px";
27659                 cn[0].childNodes[1].style.width = sww;
27660                 cn[1].childNodes[1].style.width = sww;
27661                 cn[2].childNodes[1].style.width = sww;
27662                 cn[1].style.height = Math.max(0, (sh-12))+"px";
27663             }
27664         }
27665     },
27666
27667     /**
27668      * Hides this shadow
27669      */
27670     hide : function(){
27671         if(this.el){
27672             this.el.dom.style.display = "none";
27673             Roo.Shadow.Pool.push(this.el);
27674             delete this.el;
27675         }
27676     },
27677
27678     /**
27679      * Adjust the z-index of this shadow
27680      * @param {Number} zindex The new z-index
27681      */
27682     setZIndex : function(z){
27683         this.zIndex = z;
27684         if(this.el){
27685             this.el.setStyle("z-index", z);
27686         }
27687     }
27688 };
27689
27690 // Private utility class that manages the internal Shadow cache
27691 Roo.Shadow.Pool = function(){
27692     var p = [];
27693     var markup = Roo.isIE ?
27694                  '<div class="x-ie-shadow"></div>' :
27695                  '<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>';
27696     return {
27697         pull : function(){
27698             var sh = p.shift();
27699             if(!sh){
27700                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
27701                 sh.autoBoxAdjust = false;
27702             }
27703             return sh;
27704         },
27705
27706         push : function(sh){
27707             p.push(sh);
27708         }
27709     };
27710 }();/*
27711  * Based on:
27712  * Ext JS Library 1.1.1
27713  * Copyright(c) 2006-2007, Ext JS, LLC.
27714  *
27715  * Originally Released Under LGPL - original licence link has changed is not relivant.
27716  *
27717  * Fork - LGPL
27718  * <script type="text/javascript">
27719  */
27720
27721
27722 /**
27723  * @class Roo.SplitBar
27724  * @extends Roo.util.Observable
27725  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
27726  * <br><br>
27727  * Usage:
27728  * <pre><code>
27729 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
27730                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
27731 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
27732 split.minSize = 100;
27733 split.maxSize = 600;
27734 split.animate = true;
27735 split.on('moved', splitterMoved);
27736 </code></pre>
27737  * @constructor
27738  * Create a new SplitBar
27739  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
27740  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
27741  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27742  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
27743                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
27744                         position of the SplitBar).
27745  */
27746 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
27747     
27748     /** @private */
27749     this.el = Roo.get(dragElement, true);
27750     this.el.dom.unselectable = "on";
27751     /** @private */
27752     this.resizingEl = Roo.get(resizingElement, true);
27753
27754     /**
27755      * @private
27756      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27757      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
27758      * @type Number
27759      */
27760     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
27761     
27762     /**
27763      * The minimum size of the resizing element. (Defaults to 0)
27764      * @type Number
27765      */
27766     this.minSize = 0;
27767     
27768     /**
27769      * The maximum size of the resizing element. (Defaults to 2000)
27770      * @type Number
27771      */
27772     this.maxSize = 2000;
27773     
27774     /**
27775      * Whether to animate the transition to the new size
27776      * @type Boolean
27777      */
27778     this.animate = false;
27779     
27780     /**
27781      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
27782      * @type Boolean
27783      */
27784     this.useShim = false;
27785     
27786     /** @private */
27787     this.shim = null;
27788     
27789     if(!existingProxy){
27790         /** @private */
27791         this.proxy = Roo.SplitBar.createProxy(this.orientation);
27792     }else{
27793         this.proxy = Roo.get(existingProxy).dom;
27794     }
27795     /** @private */
27796     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
27797     
27798     /** @private */
27799     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
27800     
27801     /** @private */
27802     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
27803     
27804     /** @private */
27805     this.dragSpecs = {};
27806     
27807     /**
27808      * @private The adapter to use to positon and resize elements
27809      */
27810     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
27811     this.adapter.init(this);
27812     
27813     if(this.orientation == Roo.SplitBar.HORIZONTAL){
27814         /** @private */
27815         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
27816         this.el.addClass("x-splitbar-h");
27817     }else{
27818         /** @private */
27819         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
27820         this.el.addClass("x-splitbar-v");
27821     }
27822     
27823     this.addEvents({
27824         /**
27825          * @event resize
27826          * Fires when the splitter is moved (alias for {@link #event-moved})
27827          * @param {Roo.SplitBar} this
27828          * @param {Number} newSize the new width or height
27829          */
27830         "resize" : true,
27831         /**
27832          * @event moved
27833          * Fires when the splitter is moved
27834          * @param {Roo.SplitBar} this
27835          * @param {Number} newSize the new width or height
27836          */
27837         "moved" : true,
27838         /**
27839          * @event beforeresize
27840          * Fires before the splitter is dragged
27841          * @param {Roo.SplitBar} this
27842          */
27843         "beforeresize" : true,
27844
27845         "beforeapply" : true
27846     });
27847
27848     Roo.util.Observable.call(this);
27849 };
27850
27851 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
27852     onStartProxyDrag : function(x, y){
27853         this.fireEvent("beforeresize", this);
27854         if(!this.overlay){
27855             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
27856             o.unselectable();
27857             o.enableDisplayMode("block");
27858             // all splitbars share the same overlay
27859             Roo.SplitBar.prototype.overlay = o;
27860         }
27861         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27862         this.overlay.show();
27863         Roo.get(this.proxy).setDisplayed("block");
27864         var size = this.adapter.getElementSize(this);
27865         this.activeMinSize = this.getMinimumSize();;
27866         this.activeMaxSize = this.getMaximumSize();;
27867         var c1 = size - this.activeMinSize;
27868         var c2 = Math.max(this.activeMaxSize - size, 0);
27869         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27870             this.dd.resetConstraints();
27871             this.dd.setXConstraint(
27872                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
27873                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
27874             );
27875             this.dd.setYConstraint(0, 0);
27876         }else{
27877             this.dd.resetConstraints();
27878             this.dd.setXConstraint(0, 0);
27879             this.dd.setYConstraint(
27880                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
27881                 this.placement == Roo.SplitBar.TOP ? c2 : c1
27882             );
27883          }
27884         this.dragSpecs.startSize = size;
27885         this.dragSpecs.startPoint = [x, y];
27886         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
27887     },
27888     
27889     /** 
27890      * @private Called after the drag operation by the DDProxy
27891      */
27892     onEndProxyDrag : function(e){
27893         Roo.get(this.proxy).setDisplayed(false);
27894         var endPoint = Roo.lib.Event.getXY(e);
27895         if(this.overlay){
27896             this.overlay.hide();
27897         }
27898         var newSize;
27899         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27900             newSize = this.dragSpecs.startSize + 
27901                 (this.placement == Roo.SplitBar.LEFT ?
27902                     endPoint[0] - this.dragSpecs.startPoint[0] :
27903                     this.dragSpecs.startPoint[0] - endPoint[0]
27904                 );
27905         }else{
27906             newSize = this.dragSpecs.startSize + 
27907                 (this.placement == Roo.SplitBar.TOP ?
27908                     endPoint[1] - this.dragSpecs.startPoint[1] :
27909                     this.dragSpecs.startPoint[1] - endPoint[1]
27910                 );
27911         }
27912         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
27913         if(newSize != this.dragSpecs.startSize){
27914             if(this.fireEvent('beforeapply', this, newSize) !== false){
27915                 this.adapter.setElementSize(this, newSize);
27916                 this.fireEvent("moved", this, newSize);
27917                 this.fireEvent("resize", this, newSize);
27918             }
27919         }
27920     },
27921     
27922     /**
27923      * Get the adapter this SplitBar uses
27924      * @return The adapter object
27925      */
27926     getAdapter : function(){
27927         return this.adapter;
27928     },
27929     
27930     /**
27931      * Set the adapter this SplitBar uses
27932      * @param {Object} adapter A SplitBar adapter object
27933      */
27934     setAdapter : function(adapter){
27935         this.adapter = adapter;
27936         this.adapter.init(this);
27937     },
27938     
27939     /**
27940      * Gets the minimum size for the resizing element
27941      * @return {Number} The minimum size
27942      */
27943     getMinimumSize : function(){
27944         return this.minSize;
27945     },
27946     
27947     /**
27948      * Sets the minimum size for the resizing element
27949      * @param {Number} minSize The minimum size
27950      */
27951     setMinimumSize : function(minSize){
27952         this.minSize = minSize;
27953     },
27954     
27955     /**
27956      * Gets the maximum size for the resizing element
27957      * @return {Number} The maximum size
27958      */
27959     getMaximumSize : function(){
27960         return this.maxSize;
27961     },
27962     
27963     /**
27964      * Sets the maximum size for the resizing element
27965      * @param {Number} maxSize The maximum size
27966      */
27967     setMaximumSize : function(maxSize){
27968         this.maxSize = maxSize;
27969     },
27970     
27971     /**
27972      * Sets the initialize size for the resizing element
27973      * @param {Number} size The initial size
27974      */
27975     setCurrentSize : function(size){
27976         var oldAnimate = this.animate;
27977         this.animate = false;
27978         this.adapter.setElementSize(this, size);
27979         this.animate = oldAnimate;
27980     },
27981     
27982     /**
27983      * Destroy this splitbar. 
27984      * @param {Boolean} removeEl True to remove the element
27985      */
27986     destroy : function(removeEl){
27987         if(this.shim){
27988             this.shim.remove();
27989         }
27990         this.dd.unreg();
27991         this.proxy.parentNode.removeChild(this.proxy);
27992         if(removeEl){
27993             this.el.remove();
27994         }
27995     }
27996 });
27997
27998 /**
27999  * @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.
28000  */
28001 Roo.SplitBar.createProxy = function(dir){
28002     var proxy = new Roo.Element(document.createElement("div"));
28003     proxy.unselectable();
28004     var cls = 'x-splitbar-proxy';
28005     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
28006     document.body.appendChild(proxy.dom);
28007     return proxy.dom;
28008 };
28009
28010 /** 
28011  * @class Roo.SplitBar.BasicLayoutAdapter
28012  * Default Adapter. It assumes the splitter and resizing element are not positioned
28013  * elements and only gets/sets the width of the element. Generally used for table based layouts.
28014  */
28015 Roo.SplitBar.BasicLayoutAdapter = function(){
28016 };
28017
28018 Roo.SplitBar.BasicLayoutAdapter.prototype = {
28019     // do nothing for now
28020     init : function(s){
28021     
28022     },
28023     /**
28024      * Called before drag operations to get the current size of the resizing element. 
28025      * @param {Roo.SplitBar} s The SplitBar using this adapter
28026      */
28027      getElementSize : function(s){
28028         if(s.orientation == Roo.SplitBar.HORIZONTAL){
28029             return s.resizingEl.getWidth();
28030         }else{
28031             return s.resizingEl.getHeight();
28032         }
28033     },
28034     
28035     /**
28036      * Called after drag operations to set the size of the resizing element.
28037      * @param {Roo.SplitBar} s The SplitBar using this adapter
28038      * @param {Number} newSize The new size to set
28039      * @param {Function} onComplete A function to be invoked when resizing is complete
28040      */
28041     setElementSize : function(s, newSize, onComplete){
28042         if(s.orientation == Roo.SplitBar.HORIZONTAL){
28043             if(!s.animate){
28044                 s.resizingEl.setWidth(newSize);
28045                 if(onComplete){
28046                     onComplete(s, newSize);
28047                 }
28048             }else{
28049                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
28050             }
28051         }else{
28052             
28053             if(!s.animate){
28054                 s.resizingEl.setHeight(newSize);
28055                 if(onComplete){
28056                     onComplete(s, newSize);
28057                 }
28058             }else{
28059                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
28060             }
28061         }
28062     }
28063 };
28064
28065 /** 
28066  *@class Roo.SplitBar.AbsoluteLayoutAdapter
28067  * @extends Roo.SplitBar.BasicLayoutAdapter
28068  * Adapter that  moves the splitter element to align with the resized sizing element. 
28069  * Used with an absolute positioned SplitBar.
28070  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
28071  * document.body, make sure you assign an id to the body element.
28072  */
28073 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
28074     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
28075     this.container = Roo.get(container);
28076 };
28077
28078 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
28079     init : function(s){
28080         this.basic.init(s);
28081     },
28082     
28083     getElementSize : function(s){
28084         return this.basic.getElementSize(s);
28085     },
28086     
28087     setElementSize : function(s, newSize, onComplete){
28088         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
28089     },
28090     
28091     moveSplitter : function(s){
28092         var yes = Roo.SplitBar;
28093         switch(s.placement){
28094             case yes.LEFT:
28095                 s.el.setX(s.resizingEl.getRight());
28096                 break;
28097             case yes.RIGHT:
28098                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
28099                 break;
28100             case yes.TOP:
28101                 s.el.setY(s.resizingEl.getBottom());
28102                 break;
28103             case yes.BOTTOM:
28104                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
28105                 break;
28106         }
28107     }
28108 };
28109
28110 /**
28111  * Orientation constant - Create a vertical SplitBar
28112  * @static
28113  * @type Number
28114  */
28115 Roo.SplitBar.VERTICAL = 1;
28116
28117 /**
28118  * Orientation constant - Create a horizontal SplitBar
28119  * @static
28120  * @type Number
28121  */
28122 Roo.SplitBar.HORIZONTAL = 2;
28123
28124 /**
28125  * Placement constant - The resizing element is to the left of the splitter element
28126  * @static
28127  * @type Number
28128  */
28129 Roo.SplitBar.LEFT = 1;
28130
28131 /**
28132  * Placement constant - The resizing element is to the right of the splitter element
28133  * @static
28134  * @type Number
28135  */
28136 Roo.SplitBar.RIGHT = 2;
28137
28138 /**
28139  * Placement constant - The resizing element is positioned above the splitter element
28140  * @static
28141  * @type Number
28142  */
28143 Roo.SplitBar.TOP = 3;
28144
28145 /**
28146  * Placement constant - The resizing element is positioned under splitter element
28147  * @static
28148  * @type Number
28149  */
28150 Roo.SplitBar.BOTTOM = 4;
28151 /*
28152  * Based on:
28153  * Ext JS Library 1.1.1
28154  * Copyright(c) 2006-2007, Ext JS, LLC.
28155  *
28156  * Originally Released Under LGPL - original licence link has changed is not relivant.
28157  *
28158  * Fork - LGPL
28159  * <script type="text/javascript">
28160  */
28161
28162 /**
28163  * @class Roo.View
28164  * @extends Roo.util.Observable
28165  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
28166  * This class also supports single and multi selection modes. <br>
28167  * Create a data model bound view:
28168  <pre><code>
28169  var store = new Roo.data.Store(...);
28170
28171  var view = new Roo.View({
28172     el : "my-element",
28173     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
28174  
28175     singleSelect: true,
28176     selectedClass: "ydataview-selected",
28177     store: store
28178  });
28179
28180  // listen for node click?
28181  view.on("click", function(vw, index, node, e){
28182  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
28183  });
28184
28185  // load XML data
28186  dataModel.load("foobar.xml");
28187  </code></pre>
28188  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
28189  * <br><br>
28190  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
28191  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
28192  * 
28193  * Note: old style constructor is still suported (container, template, config)
28194  * 
28195  * @constructor
28196  * Create a new View
28197  * @param {Object} config The config object
28198  * 
28199  */
28200 Roo.View = function(config, depreciated_tpl, depreciated_config){
28201     
28202     this.parent = false;
28203     
28204     if (typeof(depreciated_tpl) == 'undefined') {
28205         // new way.. - universal constructor.
28206         Roo.apply(this, config);
28207         this.el  = Roo.get(this.el);
28208     } else {
28209         // old format..
28210         this.el  = Roo.get(config);
28211         this.tpl = depreciated_tpl;
28212         Roo.apply(this, depreciated_config);
28213     }
28214     this.wrapEl  = this.el.wrap().wrap();
28215     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
28216     
28217     
28218     if(typeof(this.tpl) == "string"){
28219         this.tpl = new Roo.Template(this.tpl);
28220     } else {
28221         // support xtype ctors..
28222         this.tpl = new Roo.factory(this.tpl, Roo);
28223     }
28224     
28225     
28226     this.tpl.compile();
28227     
28228     /** @private */
28229     this.addEvents({
28230         /**
28231          * @event beforeclick
28232          * Fires before a click is processed. Returns false to cancel the default action.
28233          * @param {Roo.View} this
28234          * @param {Number} index The index of the target node
28235          * @param {HTMLElement} node The target node
28236          * @param {Roo.EventObject} e The raw event object
28237          */
28238             "beforeclick" : true,
28239         /**
28240          * @event click
28241          * Fires when a template node is clicked.
28242          * @param {Roo.View} this
28243          * @param {Number} index The index of the target node
28244          * @param {HTMLElement} node The target node
28245          * @param {Roo.EventObject} e The raw event object
28246          */
28247             "click" : true,
28248         /**
28249          * @event dblclick
28250          * Fires when a template node is double clicked.
28251          * @param {Roo.View} this
28252          * @param {Number} index The index of the target node
28253          * @param {HTMLElement} node The target node
28254          * @param {Roo.EventObject} e The raw event object
28255          */
28256             "dblclick" : true,
28257         /**
28258          * @event contextmenu
28259          * Fires when a template node is right clicked.
28260          * @param {Roo.View} this
28261          * @param {Number} index The index of the target node
28262          * @param {HTMLElement} node The target node
28263          * @param {Roo.EventObject} e The raw event object
28264          */
28265             "contextmenu" : true,
28266         /**
28267          * @event selectionchange
28268          * Fires when the selected nodes change.
28269          * @param {Roo.View} this
28270          * @param {Array} selections Array of the selected nodes
28271          */
28272             "selectionchange" : true,
28273     
28274         /**
28275          * @event beforeselect
28276          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
28277          * @param {Roo.View} this
28278          * @param {HTMLElement} node The node to be selected
28279          * @param {Array} selections Array of currently selected nodes
28280          */
28281             "beforeselect" : true,
28282         /**
28283          * @event preparedata
28284          * Fires on every row to render, to allow you to change the data.
28285          * @param {Roo.View} this
28286          * @param {Object} data to be rendered (change this)
28287          */
28288           "preparedata" : true
28289           
28290           
28291         });
28292
28293
28294
28295     this.el.on({
28296         "click": this.onClick,
28297         "dblclick": this.onDblClick,
28298         "contextmenu": this.onContextMenu,
28299         scope:this
28300     });
28301
28302     this.selections = [];
28303     this.nodes = [];
28304     this.cmp = new Roo.CompositeElementLite([]);
28305     if(this.store){
28306         this.store = Roo.factory(this.store, Roo.data);
28307         this.setStore(this.store, true);
28308     }
28309     
28310     if ( this.footer && this.footer.xtype) {
28311            
28312          var fctr = this.wrapEl.appendChild(document.createElement("div"));
28313         
28314         this.footer.dataSource = this.store;
28315         this.footer.container = fctr;
28316         this.footer = Roo.factory(this.footer, Roo);
28317         fctr.insertFirst(this.el);
28318         
28319         // this is a bit insane - as the paging toolbar seems to detach the el..
28320 //        dom.parentNode.parentNode.parentNode
28321          // they get detached?
28322     }
28323     
28324     
28325     Roo.View.superclass.constructor.call(this);
28326     
28327     
28328 };
28329
28330 Roo.extend(Roo.View, Roo.util.Observable, {
28331     
28332      /**
28333      * @cfg {Roo.data.Store} store Data store to load data from.
28334      */
28335     store : false,
28336     
28337     /**
28338      * @cfg {String|Roo.Element} el The container element.
28339      */
28340     el : '',
28341     
28342     /**
28343      * @cfg {String|Roo.Template} tpl The template used by this View 
28344      */
28345     tpl : false,
28346     /**
28347      * @cfg {String} dataName the named area of the template to use as the data area
28348      *                          Works with domtemplates roo-name="name"
28349      */
28350     dataName: false,
28351     /**
28352      * @cfg {String} selectedClass The css class to add to selected nodes
28353      */
28354     selectedClass : "x-view-selected",
28355      /**
28356      * @cfg {String} emptyText The empty text to show when nothing is loaded.
28357      */
28358     emptyText : "",
28359     
28360     /**
28361      * @cfg {String} text to display on mask (default Loading)
28362      */
28363     mask : false,
28364     /**
28365      * @cfg {Boolean} multiSelect Allow multiple selection
28366      */
28367     multiSelect : false,
28368     /**
28369      * @cfg {Boolean} singleSelect Allow single selection
28370      */
28371     singleSelect:  false,
28372     
28373     /**
28374      * @cfg {Boolean} toggleSelect - selecting 
28375      */
28376     toggleSelect : false,
28377     
28378     /**
28379      * @cfg {Boolean} tickable - selecting 
28380      */
28381     tickable : false,
28382     
28383     /**
28384      * Returns the element this view is bound to.
28385      * @return {Roo.Element}
28386      */
28387     getEl : function(){
28388         return this.wrapEl;
28389     },
28390     
28391     
28392
28393     /**
28394      * Refreshes the view. - called by datachanged on the store. - do not call directly.
28395      */
28396     refresh : function(){
28397         //Roo.log('refresh');
28398         var t = this.tpl;
28399         
28400         // if we are using something like 'domtemplate', then
28401         // the what gets used is:
28402         // t.applySubtemplate(NAME, data, wrapping data..)
28403         // the outer template then get' applied with
28404         //     the store 'extra data'
28405         // and the body get's added to the
28406         //      roo-name="data" node?
28407         //      <span class='roo-tpl-{name}'></span> ?????
28408         
28409         
28410         
28411         this.clearSelections();
28412         this.el.update("");
28413         var html = [];
28414         var records = this.store.getRange();
28415         if(records.length < 1) {
28416             
28417             // is this valid??  = should it render a template??
28418             
28419             this.el.update(this.emptyText);
28420             return;
28421         }
28422         var el = this.el;
28423         if (this.dataName) {
28424             this.el.update(t.apply(this.store.meta)); //????
28425             el = this.el.child('.roo-tpl-' + this.dataName);
28426         }
28427         
28428         for(var i = 0, len = records.length; i < len; i++){
28429             var data = this.prepareData(records[i].data, i, records[i]);
28430             this.fireEvent("preparedata", this, data, i, records[i]);
28431             
28432             var d = Roo.apply({}, data);
28433             
28434             if(this.tickable){
28435                 Roo.apply(d, {'roo-id' : Roo.id()});
28436                 
28437                 var _this = this;
28438             
28439                 Roo.each(this.parent.item, function(item){
28440                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
28441                         return;
28442                     }
28443                     Roo.apply(d, {'roo-data-checked' : 'checked'});
28444                 });
28445             }
28446             
28447             html[html.length] = Roo.util.Format.trim(
28448                 this.dataName ?
28449                     t.applySubtemplate(this.dataName, d, this.store.meta) :
28450                     t.apply(d)
28451             );
28452         }
28453         
28454         
28455         
28456         el.update(html.join(""));
28457         this.nodes = el.dom.childNodes;
28458         this.updateIndexes(0);
28459     },
28460     
28461
28462     /**
28463      * Function to override to reformat the data that is sent to
28464      * the template for each node.
28465      * DEPRICATED - use the preparedata event handler.
28466      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
28467      * a JSON object for an UpdateManager bound view).
28468      */
28469     prepareData : function(data, index, record)
28470     {
28471         this.fireEvent("preparedata", this, data, index, record);
28472         return data;
28473     },
28474
28475     onUpdate : function(ds, record){
28476         // Roo.log('on update');   
28477         this.clearSelections();
28478         var index = this.store.indexOf(record);
28479         var n = this.nodes[index];
28480         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
28481         n.parentNode.removeChild(n);
28482         this.updateIndexes(index, index);
28483     },
28484
28485     
28486     
28487 // --------- FIXME     
28488     onAdd : function(ds, records, index)
28489     {
28490         //Roo.log(['on Add', ds, records, index] );        
28491         this.clearSelections();
28492         if(this.nodes.length == 0){
28493             this.refresh();
28494             return;
28495         }
28496         var n = this.nodes[index];
28497         for(var i = 0, len = records.length; i < len; i++){
28498             var d = this.prepareData(records[i].data, i, records[i]);
28499             if(n){
28500                 this.tpl.insertBefore(n, d);
28501             }else{
28502                 
28503                 this.tpl.append(this.el, d);
28504             }
28505         }
28506         this.updateIndexes(index);
28507     },
28508
28509     onRemove : function(ds, record, index){
28510        // Roo.log('onRemove');
28511         this.clearSelections();
28512         var el = this.dataName  ?
28513             this.el.child('.roo-tpl-' + this.dataName) :
28514             this.el; 
28515         
28516         el.dom.removeChild(this.nodes[index]);
28517         this.updateIndexes(index);
28518     },
28519
28520     /**
28521      * Refresh an individual node.
28522      * @param {Number} index
28523      */
28524     refreshNode : function(index){
28525         this.onUpdate(this.store, this.store.getAt(index));
28526     },
28527
28528     updateIndexes : function(startIndex, endIndex){
28529         var ns = this.nodes;
28530         startIndex = startIndex || 0;
28531         endIndex = endIndex || ns.length - 1;
28532         for(var i = startIndex; i <= endIndex; i++){
28533             ns[i].nodeIndex = i;
28534         }
28535     },
28536
28537     /**
28538      * Changes the data store this view uses and refresh the view.
28539      * @param {Store} store
28540      */
28541     setStore : function(store, initial){
28542         if(!initial && this.store){
28543             this.store.un("datachanged", this.refresh);
28544             this.store.un("add", this.onAdd);
28545             this.store.un("remove", this.onRemove);
28546             this.store.un("update", this.onUpdate);
28547             this.store.un("clear", this.refresh);
28548             this.store.un("beforeload", this.onBeforeLoad);
28549             this.store.un("load", this.onLoad);
28550             this.store.un("loadexception", this.onLoad);
28551         }
28552         if(store){
28553           
28554             store.on("datachanged", this.refresh, this);
28555             store.on("add", this.onAdd, this);
28556             store.on("remove", this.onRemove, this);
28557             store.on("update", this.onUpdate, this);
28558             store.on("clear", this.refresh, this);
28559             store.on("beforeload", this.onBeforeLoad, this);
28560             store.on("load", this.onLoad, this);
28561             store.on("loadexception", this.onLoad, this);
28562         }
28563         
28564         if(store){
28565             this.refresh();
28566         }
28567     },
28568     /**
28569      * onbeforeLoad - masks the loading area.
28570      *
28571      */
28572     onBeforeLoad : function(store,opts)
28573     {
28574          //Roo.log('onBeforeLoad');   
28575         if (!opts.add) {
28576             this.el.update("");
28577         }
28578         this.el.mask(this.mask ? this.mask : "Loading" ); 
28579     },
28580     onLoad : function ()
28581     {
28582         this.el.unmask();
28583     },
28584     
28585
28586     /**
28587      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
28588      * @param {HTMLElement} node
28589      * @return {HTMLElement} The template node
28590      */
28591     findItemFromChild : function(node){
28592         var el = this.dataName  ?
28593             this.el.child('.roo-tpl-' + this.dataName,true) :
28594             this.el.dom; 
28595         
28596         if(!node || node.parentNode == el){
28597                     return node;
28598             }
28599             var p = node.parentNode;
28600             while(p && p != el){
28601             if(p.parentNode == el){
28602                 return p;
28603             }
28604             p = p.parentNode;
28605         }
28606             return null;
28607     },
28608
28609     /** @ignore */
28610     onClick : function(e){
28611         var item = this.findItemFromChild(e.getTarget());
28612         if(item){
28613             var index = this.indexOf(item);
28614             if(this.onItemClick(item, index, e) !== false){
28615                 this.fireEvent("click", this, index, item, e);
28616             }
28617         }else{
28618             this.clearSelections();
28619         }
28620     },
28621
28622     /** @ignore */
28623     onContextMenu : function(e){
28624         var item = this.findItemFromChild(e.getTarget());
28625         if(item){
28626             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
28627         }
28628     },
28629
28630     /** @ignore */
28631     onDblClick : function(e){
28632         var item = this.findItemFromChild(e.getTarget());
28633         if(item){
28634             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
28635         }
28636     },
28637
28638     onItemClick : function(item, index, e)
28639     {
28640         if(this.fireEvent("beforeclick", this, index, item, e) === false){
28641             return false;
28642         }
28643         if (this.toggleSelect) {
28644             var m = this.isSelected(item) ? 'unselect' : 'select';
28645             //Roo.log(m);
28646             var _t = this;
28647             _t[m](item, true, false);
28648             return true;
28649         }
28650         if(this.multiSelect || this.singleSelect){
28651             if(this.multiSelect && e.shiftKey && this.lastSelection){
28652                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
28653             }else{
28654                 this.select(item, this.multiSelect && e.ctrlKey);
28655                 this.lastSelection = item;
28656             }
28657             
28658             if(!this.tickable){
28659                 e.preventDefault();
28660             }
28661             
28662         }
28663         return true;
28664     },
28665
28666     /**
28667      * Get the number of selected nodes.
28668      * @return {Number}
28669      */
28670     getSelectionCount : function(){
28671         return this.selections.length;
28672     },
28673
28674     /**
28675      * Get the currently selected nodes.
28676      * @return {Array} An array of HTMLElements
28677      */
28678     getSelectedNodes : function(){
28679         return this.selections;
28680     },
28681
28682     /**
28683      * Get the indexes of the selected nodes.
28684      * @return {Array}
28685      */
28686     getSelectedIndexes : function(){
28687         var indexes = [], s = this.selections;
28688         for(var i = 0, len = s.length; i < len; i++){
28689             indexes.push(s[i].nodeIndex);
28690         }
28691         return indexes;
28692     },
28693
28694     /**
28695      * Clear all selections
28696      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
28697      */
28698     clearSelections : function(suppressEvent){
28699         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
28700             this.cmp.elements = this.selections;
28701             this.cmp.removeClass(this.selectedClass);
28702             this.selections = [];
28703             if(!suppressEvent){
28704                 this.fireEvent("selectionchange", this, this.selections);
28705             }
28706         }
28707     },
28708
28709     /**
28710      * Returns true if the passed node is selected
28711      * @param {HTMLElement/Number} node The node or node index
28712      * @return {Boolean}
28713      */
28714     isSelected : function(node){
28715         var s = this.selections;
28716         if(s.length < 1){
28717             return false;
28718         }
28719         node = this.getNode(node);
28720         return s.indexOf(node) !== -1;
28721     },
28722
28723     /**
28724      * Selects nodes.
28725      * @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
28726      * @param {Boolean} keepExisting (optional) true to keep existing selections
28727      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28728      */
28729     select : function(nodeInfo, keepExisting, suppressEvent){
28730         if(nodeInfo instanceof Array){
28731             if(!keepExisting){
28732                 this.clearSelections(true);
28733             }
28734             for(var i = 0, len = nodeInfo.length; i < len; i++){
28735                 this.select(nodeInfo[i], true, true);
28736             }
28737             return;
28738         } 
28739         var node = this.getNode(nodeInfo);
28740         if(!node || this.isSelected(node)){
28741             return; // already selected.
28742         }
28743         if(!keepExisting){
28744             this.clearSelections(true);
28745         }
28746         
28747         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28748             Roo.fly(node).addClass(this.selectedClass);
28749             this.selections.push(node);
28750             if(!suppressEvent){
28751                 this.fireEvent("selectionchange", this, this.selections);
28752             }
28753         }
28754         
28755         
28756     },
28757       /**
28758      * Unselects nodes.
28759      * @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
28760      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
28761      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28762      */
28763     unselect : function(nodeInfo, keepExisting, suppressEvent)
28764     {
28765         if(nodeInfo instanceof Array){
28766             Roo.each(this.selections, function(s) {
28767                 this.unselect(s, nodeInfo);
28768             }, this);
28769             return;
28770         }
28771         var node = this.getNode(nodeInfo);
28772         if(!node || !this.isSelected(node)){
28773             //Roo.log("not selected");
28774             return; // not selected.
28775         }
28776         // fireevent???
28777         var ns = [];
28778         Roo.each(this.selections, function(s) {
28779             if (s == node ) {
28780                 Roo.fly(node).removeClass(this.selectedClass);
28781
28782                 return;
28783             }
28784             ns.push(s);
28785         },this);
28786         
28787         this.selections= ns;
28788         this.fireEvent("selectionchange", this, this.selections);
28789     },
28790
28791     /**
28792      * Gets a template node.
28793      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28794      * @return {HTMLElement} The node or null if it wasn't found
28795      */
28796     getNode : function(nodeInfo){
28797         if(typeof nodeInfo == "string"){
28798             return document.getElementById(nodeInfo);
28799         }else if(typeof nodeInfo == "number"){
28800             return this.nodes[nodeInfo];
28801         }
28802         return nodeInfo;
28803     },
28804
28805     /**
28806      * Gets a range template nodes.
28807      * @param {Number} startIndex
28808      * @param {Number} endIndex
28809      * @return {Array} An array of nodes
28810      */
28811     getNodes : function(start, end){
28812         var ns = this.nodes;
28813         start = start || 0;
28814         end = typeof end == "undefined" ? ns.length - 1 : end;
28815         var nodes = [];
28816         if(start <= end){
28817             for(var i = start; i <= end; i++){
28818                 nodes.push(ns[i]);
28819             }
28820         } else{
28821             for(var i = start; i >= end; i--){
28822                 nodes.push(ns[i]);
28823             }
28824         }
28825         return nodes;
28826     },
28827
28828     /**
28829      * Finds the index of the passed node
28830      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28831      * @return {Number} The index of the node or -1
28832      */
28833     indexOf : function(node){
28834         node = this.getNode(node);
28835         if(typeof node.nodeIndex == "number"){
28836             return node.nodeIndex;
28837         }
28838         var ns = this.nodes;
28839         for(var i = 0, len = ns.length; i < len; i++){
28840             if(ns[i] == node){
28841                 return i;
28842             }
28843         }
28844         return -1;
28845     }
28846 });
28847 /*
28848  * Based on:
28849  * Ext JS Library 1.1.1
28850  * Copyright(c) 2006-2007, Ext JS, LLC.
28851  *
28852  * Originally Released Under LGPL - original licence link has changed is not relivant.
28853  *
28854  * Fork - LGPL
28855  * <script type="text/javascript">
28856  */
28857
28858 /**
28859  * @class Roo.JsonView
28860  * @extends Roo.View
28861  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
28862 <pre><code>
28863 var view = new Roo.JsonView({
28864     container: "my-element",
28865     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
28866     multiSelect: true, 
28867     jsonRoot: "data" 
28868 });
28869
28870 // listen for node click?
28871 view.on("click", function(vw, index, node, e){
28872     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
28873 });
28874
28875 // direct load of JSON data
28876 view.load("foobar.php");
28877
28878 // Example from my blog list
28879 var tpl = new Roo.Template(
28880     '&lt;div class="entry"&gt;' +
28881     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
28882     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
28883     "&lt;/div&gt;&lt;hr /&gt;"
28884 );
28885
28886 var moreView = new Roo.JsonView({
28887     container :  "entry-list", 
28888     template : tpl,
28889     jsonRoot: "posts"
28890 });
28891 moreView.on("beforerender", this.sortEntries, this);
28892 moreView.load({
28893     url: "/blog/get-posts.php",
28894     params: "allposts=true",
28895     text: "Loading Blog Entries..."
28896 });
28897 </code></pre>
28898
28899 * Note: old code is supported with arguments : (container, template, config)
28900
28901
28902  * @constructor
28903  * Create a new JsonView
28904  * 
28905  * @param {Object} config The config object
28906  * 
28907  */
28908 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
28909     
28910     
28911     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
28912
28913     var um = this.el.getUpdateManager();
28914     um.setRenderer(this);
28915     um.on("update", this.onLoad, this);
28916     um.on("failure", this.onLoadException, this);
28917
28918     /**
28919      * @event beforerender
28920      * Fires before rendering of the downloaded JSON data.
28921      * @param {Roo.JsonView} this
28922      * @param {Object} data The JSON data loaded
28923      */
28924     /**
28925      * @event load
28926      * Fires when data is loaded.
28927      * @param {Roo.JsonView} this
28928      * @param {Object} data The JSON data loaded
28929      * @param {Object} response The raw Connect response object
28930      */
28931     /**
28932      * @event loadexception
28933      * Fires when loading fails.
28934      * @param {Roo.JsonView} this
28935      * @param {Object} response The raw Connect response object
28936      */
28937     this.addEvents({
28938         'beforerender' : true,
28939         'load' : true,
28940         'loadexception' : true
28941     });
28942 };
28943 Roo.extend(Roo.JsonView, Roo.View, {
28944     /**
28945      * @type {String} The root property in the loaded JSON object that contains the data
28946      */
28947     jsonRoot : "",
28948
28949     /**
28950      * Refreshes the view.
28951      */
28952     refresh : function(){
28953         this.clearSelections();
28954         this.el.update("");
28955         var html = [];
28956         var o = this.jsonData;
28957         if(o && o.length > 0){
28958             for(var i = 0, len = o.length; i < len; i++){
28959                 var data = this.prepareData(o[i], i, o);
28960                 html[html.length] = this.tpl.apply(data);
28961             }
28962         }else{
28963             html.push(this.emptyText);
28964         }
28965         this.el.update(html.join(""));
28966         this.nodes = this.el.dom.childNodes;
28967         this.updateIndexes(0);
28968     },
28969
28970     /**
28971      * 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.
28972      * @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:
28973      <pre><code>
28974      view.load({
28975          url: "your-url.php",
28976          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
28977          callback: yourFunction,
28978          scope: yourObject, //(optional scope)
28979          discardUrl: false,
28980          nocache: false,
28981          text: "Loading...",
28982          timeout: 30,
28983          scripts: false
28984      });
28985      </code></pre>
28986      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
28987      * 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.
28988      * @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}
28989      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
28990      * @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.
28991      */
28992     load : function(){
28993         var um = this.el.getUpdateManager();
28994         um.update.apply(um, arguments);
28995     },
28996
28997     // note - render is a standard framework call...
28998     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
28999     render : function(el, response){
29000         
29001         this.clearSelections();
29002         this.el.update("");
29003         var o;
29004         try{
29005             if (response != '') {
29006                 o = Roo.util.JSON.decode(response.responseText);
29007                 if(this.jsonRoot){
29008                     
29009                     o = o[this.jsonRoot];
29010                 }
29011             }
29012         } catch(e){
29013         }
29014         /**
29015          * The current JSON data or null
29016          */
29017         this.jsonData = o;
29018         this.beforeRender();
29019         this.refresh();
29020     },
29021
29022 /**
29023  * Get the number of records in the current JSON dataset
29024  * @return {Number}
29025  */
29026     getCount : function(){
29027         return this.jsonData ? this.jsonData.length : 0;
29028     },
29029
29030 /**
29031  * Returns the JSON object for the specified node(s)
29032  * @param {HTMLElement/Array} node The node or an array of nodes
29033  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
29034  * you get the JSON object for the node
29035  */
29036     getNodeData : function(node){
29037         if(node instanceof Array){
29038             var data = [];
29039             for(var i = 0, len = node.length; i < len; i++){
29040                 data.push(this.getNodeData(node[i]));
29041             }
29042             return data;
29043         }
29044         return this.jsonData[this.indexOf(node)] || null;
29045     },
29046
29047     beforeRender : function(){
29048         this.snapshot = this.jsonData;
29049         if(this.sortInfo){
29050             this.sort.apply(this, this.sortInfo);
29051         }
29052         this.fireEvent("beforerender", this, this.jsonData);
29053     },
29054
29055     onLoad : function(el, o){
29056         this.fireEvent("load", this, this.jsonData, o);
29057     },
29058
29059     onLoadException : function(el, o){
29060         this.fireEvent("loadexception", this, o);
29061     },
29062
29063 /**
29064  * Filter the data by a specific property.
29065  * @param {String} property A property on your JSON objects
29066  * @param {String/RegExp} value Either string that the property values
29067  * should start with, or a RegExp to test against the property
29068  */
29069     filter : function(property, value){
29070         if(this.jsonData){
29071             var data = [];
29072             var ss = this.snapshot;
29073             if(typeof value == "string"){
29074                 var vlen = value.length;
29075                 if(vlen == 0){
29076                     this.clearFilter();
29077                     return;
29078                 }
29079                 value = value.toLowerCase();
29080                 for(var i = 0, len = ss.length; i < len; i++){
29081                     var o = ss[i];
29082                     if(o[property].substr(0, vlen).toLowerCase() == value){
29083                         data.push(o);
29084                     }
29085                 }
29086             } else if(value.exec){ // regex?
29087                 for(var i = 0, len = ss.length; i < len; i++){
29088                     var o = ss[i];
29089                     if(value.test(o[property])){
29090                         data.push(o);
29091                     }
29092                 }
29093             } else{
29094                 return;
29095             }
29096             this.jsonData = data;
29097             this.refresh();
29098         }
29099     },
29100
29101 /**
29102  * Filter by a function. The passed function will be called with each
29103  * object in the current dataset. If the function returns true the value is kept,
29104  * otherwise it is filtered.
29105  * @param {Function} fn
29106  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
29107  */
29108     filterBy : function(fn, scope){
29109         if(this.jsonData){
29110             var data = [];
29111             var ss = this.snapshot;
29112             for(var i = 0, len = ss.length; i < len; i++){
29113                 var o = ss[i];
29114                 if(fn.call(scope || this, o)){
29115                     data.push(o);
29116                 }
29117             }
29118             this.jsonData = data;
29119             this.refresh();
29120         }
29121     },
29122
29123 /**
29124  * Clears the current filter.
29125  */
29126     clearFilter : function(){
29127         if(this.snapshot && this.jsonData != this.snapshot){
29128             this.jsonData = this.snapshot;
29129             this.refresh();
29130         }
29131     },
29132
29133
29134 /**
29135  * Sorts the data for this view and refreshes it.
29136  * @param {String} property A property on your JSON objects to sort on
29137  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
29138  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
29139  */
29140     sort : function(property, dir, sortType){
29141         this.sortInfo = Array.prototype.slice.call(arguments, 0);
29142         if(this.jsonData){
29143             var p = property;
29144             var dsc = dir && dir.toLowerCase() == "desc";
29145             var f = function(o1, o2){
29146                 var v1 = sortType ? sortType(o1[p]) : o1[p];
29147                 var v2 = sortType ? sortType(o2[p]) : o2[p];
29148                 ;
29149                 if(v1 < v2){
29150                     return dsc ? +1 : -1;
29151                 } else if(v1 > v2){
29152                     return dsc ? -1 : +1;
29153                 } else{
29154                     return 0;
29155                 }
29156             };
29157             this.jsonData.sort(f);
29158             this.refresh();
29159             if(this.jsonData != this.snapshot){
29160                 this.snapshot.sort(f);
29161             }
29162         }
29163     }
29164 });/*
29165  * Based on:
29166  * Ext JS Library 1.1.1
29167  * Copyright(c) 2006-2007, Ext JS, LLC.
29168  *
29169  * Originally Released Under LGPL - original licence link has changed is not relivant.
29170  *
29171  * Fork - LGPL
29172  * <script type="text/javascript">
29173  */
29174  
29175
29176 /**
29177  * @class Roo.ColorPalette
29178  * @extends Roo.Component
29179  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
29180  * Here's an example of typical usage:
29181  * <pre><code>
29182 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
29183 cp.render('my-div');
29184
29185 cp.on('select', function(palette, selColor){
29186     // do something with selColor
29187 });
29188 </code></pre>
29189  * @constructor
29190  * Create a new ColorPalette
29191  * @param {Object} config The config object
29192  */
29193 Roo.ColorPalette = function(config){
29194     Roo.ColorPalette.superclass.constructor.call(this, config);
29195     this.addEvents({
29196         /**
29197              * @event select
29198              * Fires when a color is selected
29199              * @param {ColorPalette} this
29200              * @param {String} color The 6-digit color hex code (without the # symbol)
29201              */
29202         select: true
29203     });
29204
29205     if(this.handler){
29206         this.on("select", this.handler, this.scope, true);
29207     }
29208 };
29209 Roo.extend(Roo.ColorPalette, Roo.Component, {
29210     /**
29211      * @cfg {String} itemCls
29212      * The CSS class to apply to the containing element (defaults to "x-color-palette")
29213      */
29214     itemCls : "x-color-palette",
29215     /**
29216      * @cfg {String} value
29217      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
29218      * the hex codes are case-sensitive.
29219      */
29220     value : null,
29221     clickEvent:'click',
29222     // private
29223     ctype: "Roo.ColorPalette",
29224
29225     /**
29226      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
29227      */
29228     allowReselect : false,
29229
29230     /**
29231      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
29232      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
29233      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
29234      * of colors with the width setting until the box is symmetrical.</p>
29235      * <p>You can override individual colors if needed:</p>
29236      * <pre><code>
29237 var cp = new Roo.ColorPalette();
29238 cp.colors[0] = "FF0000";  // change the first box to red
29239 </code></pre>
29240
29241 Or you can provide a custom array of your own for complete control:
29242 <pre><code>
29243 var cp = new Roo.ColorPalette();
29244 cp.colors = ["000000", "993300", "333300"];
29245 </code></pre>
29246      * @type Array
29247      */
29248     colors : [
29249         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
29250         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
29251         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
29252         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
29253         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
29254     ],
29255
29256     // private
29257     onRender : function(container, position){
29258         var t = new Roo.MasterTemplate(
29259             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
29260         );
29261         var c = this.colors;
29262         for(var i = 0, len = c.length; i < len; i++){
29263             t.add([c[i]]);
29264         }
29265         var el = document.createElement("div");
29266         el.className = this.itemCls;
29267         t.overwrite(el);
29268         container.dom.insertBefore(el, position);
29269         this.el = Roo.get(el);
29270         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
29271         if(this.clickEvent != 'click'){
29272             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
29273         }
29274     },
29275
29276     // private
29277     afterRender : function(){
29278         Roo.ColorPalette.superclass.afterRender.call(this);
29279         if(this.value){
29280             var s = this.value;
29281             this.value = null;
29282             this.select(s);
29283         }
29284     },
29285
29286     // private
29287     handleClick : function(e, t){
29288         e.preventDefault();
29289         if(!this.disabled){
29290             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
29291             this.select(c.toUpperCase());
29292         }
29293     },
29294
29295     /**
29296      * Selects the specified color in the palette (fires the select event)
29297      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
29298      */
29299     select : function(color){
29300         color = color.replace("#", "");
29301         if(color != this.value || this.allowReselect){
29302             var el = this.el;
29303             if(this.value){
29304                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
29305             }
29306             el.child("a.color-"+color).addClass("x-color-palette-sel");
29307             this.value = color;
29308             this.fireEvent("select", this, color);
29309         }
29310     }
29311 });/*
29312  * Based on:
29313  * Ext JS Library 1.1.1
29314  * Copyright(c) 2006-2007, Ext JS, LLC.
29315  *
29316  * Originally Released Under LGPL - original licence link has changed is not relivant.
29317  *
29318  * Fork - LGPL
29319  * <script type="text/javascript">
29320  */
29321  
29322 /**
29323  * @class Roo.DatePicker
29324  * @extends Roo.Component
29325  * Simple date picker class.
29326  * @constructor
29327  * Create a new DatePicker
29328  * @param {Object} config The config object
29329  */
29330 Roo.DatePicker = function(config){
29331     Roo.DatePicker.superclass.constructor.call(this, config);
29332
29333     this.value = config && config.value ?
29334                  config.value.clearTime() : new Date().clearTime();
29335
29336     this.addEvents({
29337         /**
29338              * @event select
29339              * Fires when a date is selected
29340              * @param {DatePicker} this
29341              * @param {Date} date The selected date
29342              */
29343         'select': true,
29344         /**
29345              * @event monthchange
29346              * Fires when the displayed month changes 
29347              * @param {DatePicker} this
29348              * @param {Date} date The selected month
29349              */
29350         'monthchange': true
29351     });
29352
29353     if(this.handler){
29354         this.on("select", this.handler,  this.scope || this);
29355     }
29356     // build the disabledDatesRE
29357     if(!this.disabledDatesRE && this.disabledDates){
29358         var dd = this.disabledDates;
29359         var re = "(?:";
29360         for(var i = 0; i < dd.length; i++){
29361             re += dd[i];
29362             if(i != dd.length-1) {
29363                 re += "|";
29364             }
29365         }
29366         this.disabledDatesRE = new RegExp(re + ")");
29367     }
29368 };
29369
29370 Roo.extend(Roo.DatePicker, Roo.Component, {
29371     /**
29372      * @cfg {String} todayText
29373      * The text to display on the button that selects the current date (defaults to "Today")
29374      */
29375     todayText : "Today",
29376     /**
29377      * @cfg {String} okText
29378      * The text to display on the ok button
29379      */
29380     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
29381     /**
29382      * @cfg {String} cancelText
29383      * The text to display on the cancel button
29384      */
29385     cancelText : "Cancel",
29386     /**
29387      * @cfg {String} todayTip
29388      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
29389      */
29390     todayTip : "{0} (Spacebar)",
29391     /**
29392      * @cfg {Date} minDate
29393      * Minimum allowable date (JavaScript date object, defaults to null)
29394      */
29395     minDate : null,
29396     /**
29397      * @cfg {Date} maxDate
29398      * Maximum allowable date (JavaScript date object, defaults to null)
29399      */
29400     maxDate : null,
29401     /**
29402      * @cfg {String} minText
29403      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
29404      */
29405     minText : "This date is before the minimum date",
29406     /**
29407      * @cfg {String} maxText
29408      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
29409      */
29410     maxText : "This date is after the maximum date",
29411     /**
29412      * @cfg {String} format
29413      * The default date format string which can be overriden for localization support.  The format must be
29414      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
29415      */
29416     format : "m/d/y",
29417     /**
29418      * @cfg {Array} disabledDays
29419      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
29420      */
29421     disabledDays : null,
29422     /**
29423      * @cfg {String} disabledDaysText
29424      * The tooltip to display when the date falls on a disabled day (defaults to "")
29425      */
29426     disabledDaysText : "",
29427     /**
29428      * @cfg {RegExp} disabledDatesRE
29429      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
29430      */
29431     disabledDatesRE : null,
29432     /**
29433      * @cfg {String} disabledDatesText
29434      * The tooltip text to display when the date falls on a disabled date (defaults to "")
29435      */
29436     disabledDatesText : "",
29437     /**
29438      * @cfg {Boolean} constrainToViewport
29439      * True to constrain the date picker to the viewport (defaults to true)
29440      */
29441     constrainToViewport : true,
29442     /**
29443      * @cfg {Array} monthNames
29444      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
29445      */
29446     monthNames : Date.monthNames,
29447     /**
29448      * @cfg {Array} dayNames
29449      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
29450      */
29451     dayNames : Date.dayNames,
29452     /**
29453      * @cfg {String} nextText
29454      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
29455      */
29456     nextText: 'Next Month (Control+Right)',
29457     /**
29458      * @cfg {String} prevText
29459      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
29460      */
29461     prevText: 'Previous Month (Control+Left)',
29462     /**
29463      * @cfg {String} monthYearText
29464      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
29465      */
29466     monthYearText: 'Choose a month (Control+Up/Down to move years)',
29467     /**
29468      * @cfg {Number} startDay
29469      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
29470      */
29471     startDay : 0,
29472     /**
29473      * @cfg {Bool} showClear
29474      * Show a clear button (usefull for date form elements that can be blank.)
29475      */
29476     
29477     showClear: false,
29478     
29479     /**
29480      * Sets the value of the date field
29481      * @param {Date} value The date to set
29482      */
29483     setValue : function(value){
29484         var old = this.value;
29485         
29486         if (typeof(value) == 'string') {
29487          
29488             value = Date.parseDate(value, this.format);
29489         }
29490         if (!value) {
29491             value = new Date();
29492         }
29493         
29494         this.value = value.clearTime(true);
29495         if(this.el){
29496             this.update(this.value);
29497         }
29498     },
29499
29500     /**
29501      * Gets the current selected value of the date field
29502      * @return {Date} The selected date
29503      */
29504     getValue : function(){
29505         return this.value;
29506     },
29507
29508     // private
29509     focus : function(){
29510         if(this.el){
29511             this.update(this.activeDate);
29512         }
29513     },
29514
29515     // privateval
29516     onRender : function(container, position){
29517         
29518         var m = [
29519              '<table cellspacing="0">',
29520                 '<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>',
29521                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
29522         var dn = this.dayNames;
29523         for(var i = 0; i < 7; i++){
29524             var d = this.startDay+i;
29525             if(d > 6){
29526                 d = d-7;
29527             }
29528             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
29529         }
29530         m[m.length] = "</tr></thead><tbody><tr>";
29531         for(var i = 0; i < 42; i++) {
29532             if(i % 7 == 0 && i != 0){
29533                 m[m.length] = "</tr><tr>";
29534             }
29535             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
29536         }
29537         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
29538             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
29539
29540         var el = document.createElement("div");
29541         el.className = "x-date-picker";
29542         el.innerHTML = m.join("");
29543
29544         container.dom.insertBefore(el, position);
29545
29546         this.el = Roo.get(el);
29547         this.eventEl = Roo.get(el.firstChild);
29548
29549         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
29550             handler: this.showPrevMonth,
29551             scope: this,
29552             preventDefault:true,
29553             stopDefault:true
29554         });
29555
29556         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
29557             handler: this.showNextMonth,
29558             scope: this,
29559             preventDefault:true,
29560             stopDefault:true
29561         });
29562
29563         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
29564
29565         this.monthPicker = this.el.down('div.x-date-mp');
29566         this.monthPicker.enableDisplayMode('block');
29567         
29568         var kn = new Roo.KeyNav(this.eventEl, {
29569             "left" : function(e){
29570                 e.ctrlKey ?
29571                     this.showPrevMonth() :
29572                     this.update(this.activeDate.add("d", -1));
29573             },
29574
29575             "right" : function(e){
29576                 e.ctrlKey ?
29577                     this.showNextMonth() :
29578                     this.update(this.activeDate.add("d", 1));
29579             },
29580
29581             "up" : function(e){
29582                 e.ctrlKey ?
29583                     this.showNextYear() :
29584                     this.update(this.activeDate.add("d", -7));
29585             },
29586
29587             "down" : function(e){
29588                 e.ctrlKey ?
29589                     this.showPrevYear() :
29590                     this.update(this.activeDate.add("d", 7));
29591             },
29592
29593             "pageUp" : function(e){
29594                 this.showNextMonth();
29595             },
29596
29597             "pageDown" : function(e){
29598                 this.showPrevMonth();
29599             },
29600
29601             "enter" : function(e){
29602                 e.stopPropagation();
29603                 return true;
29604             },
29605
29606             scope : this
29607         });
29608
29609         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
29610
29611         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
29612
29613         this.el.unselectable();
29614         
29615         this.cells = this.el.select("table.x-date-inner tbody td");
29616         this.textNodes = this.el.query("table.x-date-inner tbody span");
29617
29618         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
29619             text: "&#160;",
29620             tooltip: this.monthYearText
29621         });
29622
29623         this.mbtn.on('click', this.showMonthPicker, this);
29624         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
29625
29626
29627         var today = (new Date()).dateFormat(this.format);
29628         
29629         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
29630         if (this.showClear) {
29631             baseTb.add( new Roo.Toolbar.Fill());
29632         }
29633         baseTb.add({
29634             text: String.format(this.todayText, today),
29635             tooltip: String.format(this.todayTip, today),
29636             handler: this.selectToday,
29637             scope: this
29638         });
29639         
29640         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
29641             
29642         //});
29643         if (this.showClear) {
29644             
29645             baseTb.add( new Roo.Toolbar.Fill());
29646             baseTb.add({
29647                 text: '&#160;',
29648                 cls: 'x-btn-icon x-btn-clear',
29649                 handler: function() {
29650                     //this.value = '';
29651                     this.fireEvent("select", this, '');
29652                 },
29653                 scope: this
29654             });
29655         }
29656         
29657         
29658         if(Roo.isIE){
29659             this.el.repaint();
29660         }
29661         this.update(this.value);
29662     },
29663
29664     createMonthPicker : function(){
29665         if(!this.monthPicker.dom.firstChild){
29666             var buf = ['<table border="0" cellspacing="0">'];
29667             for(var i = 0; i < 6; i++){
29668                 buf.push(
29669                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
29670                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
29671                     i == 0 ?
29672                     '<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>' :
29673                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
29674                 );
29675             }
29676             buf.push(
29677                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
29678                     this.okText,
29679                     '</button><button type="button" class="x-date-mp-cancel">',
29680                     this.cancelText,
29681                     '</button></td></tr>',
29682                 '</table>'
29683             );
29684             this.monthPicker.update(buf.join(''));
29685             this.monthPicker.on('click', this.onMonthClick, this);
29686             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
29687
29688             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
29689             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
29690
29691             this.mpMonths.each(function(m, a, i){
29692                 i += 1;
29693                 if((i%2) == 0){
29694                     m.dom.xmonth = 5 + Math.round(i * .5);
29695                 }else{
29696                     m.dom.xmonth = Math.round((i-1) * .5);
29697                 }
29698             });
29699         }
29700     },
29701
29702     showMonthPicker : function(){
29703         this.createMonthPicker();
29704         var size = this.el.getSize();
29705         this.monthPicker.setSize(size);
29706         this.monthPicker.child('table').setSize(size);
29707
29708         this.mpSelMonth = (this.activeDate || this.value).getMonth();
29709         this.updateMPMonth(this.mpSelMonth);
29710         this.mpSelYear = (this.activeDate || this.value).getFullYear();
29711         this.updateMPYear(this.mpSelYear);
29712
29713         this.monthPicker.slideIn('t', {duration:.2});
29714     },
29715
29716     updateMPYear : function(y){
29717         this.mpyear = y;
29718         var ys = this.mpYears.elements;
29719         for(var i = 1; i <= 10; i++){
29720             var td = ys[i-1], y2;
29721             if((i%2) == 0){
29722                 y2 = y + Math.round(i * .5);
29723                 td.firstChild.innerHTML = y2;
29724                 td.xyear = y2;
29725             }else{
29726                 y2 = y - (5-Math.round(i * .5));
29727                 td.firstChild.innerHTML = y2;
29728                 td.xyear = y2;
29729             }
29730             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
29731         }
29732     },
29733
29734     updateMPMonth : function(sm){
29735         this.mpMonths.each(function(m, a, i){
29736             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
29737         });
29738     },
29739
29740     selectMPMonth: function(m){
29741         
29742     },
29743
29744     onMonthClick : function(e, t){
29745         e.stopEvent();
29746         var el = new Roo.Element(t), pn;
29747         if(el.is('button.x-date-mp-cancel')){
29748             this.hideMonthPicker();
29749         }
29750         else if(el.is('button.x-date-mp-ok')){
29751             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29752             this.hideMonthPicker();
29753         }
29754         else if(pn = el.up('td.x-date-mp-month', 2)){
29755             this.mpMonths.removeClass('x-date-mp-sel');
29756             pn.addClass('x-date-mp-sel');
29757             this.mpSelMonth = pn.dom.xmonth;
29758         }
29759         else if(pn = el.up('td.x-date-mp-year', 2)){
29760             this.mpYears.removeClass('x-date-mp-sel');
29761             pn.addClass('x-date-mp-sel');
29762             this.mpSelYear = pn.dom.xyear;
29763         }
29764         else if(el.is('a.x-date-mp-prev')){
29765             this.updateMPYear(this.mpyear-10);
29766         }
29767         else if(el.is('a.x-date-mp-next')){
29768             this.updateMPYear(this.mpyear+10);
29769         }
29770     },
29771
29772     onMonthDblClick : function(e, t){
29773         e.stopEvent();
29774         var el = new Roo.Element(t), pn;
29775         if(pn = el.up('td.x-date-mp-month', 2)){
29776             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
29777             this.hideMonthPicker();
29778         }
29779         else if(pn = el.up('td.x-date-mp-year', 2)){
29780             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29781             this.hideMonthPicker();
29782         }
29783     },
29784
29785     hideMonthPicker : function(disableAnim){
29786         if(this.monthPicker){
29787             if(disableAnim === true){
29788                 this.monthPicker.hide();
29789             }else{
29790                 this.monthPicker.slideOut('t', {duration:.2});
29791             }
29792         }
29793     },
29794
29795     // private
29796     showPrevMonth : function(e){
29797         this.update(this.activeDate.add("mo", -1));
29798     },
29799
29800     // private
29801     showNextMonth : function(e){
29802         this.update(this.activeDate.add("mo", 1));
29803     },
29804
29805     // private
29806     showPrevYear : function(){
29807         this.update(this.activeDate.add("y", -1));
29808     },
29809
29810     // private
29811     showNextYear : function(){
29812         this.update(this.activeDate.add("y", 1));
29813     },
29814
29815     // private
29816     handleMouseWheel : function(e){
29817         var delta = e.getWheelDelta();
29818         if(delta > 0){
29819             this.showPrevMonth();
29820             e.stopEvent();
29821         } else if(delta < 0){
29822             this.showNextMonth();
29823             e.stopEvent();
29824         }
29825     },
29826
29827     // private
29828     handleDateClick : function(e, t){
29829         e.stopEvent();
29830         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
29831             this.setValue(new Date(t.dateValue));
29832             this.fireEvent("select", this, this.value);
29833         }
29834     },
29835
29836     // private
29837     selectToday : function(){
29838         this.setValue(new Date().clearTime());
29839         this.fireEvent("select", this, this.value);
29840     },
29841
29842     // private
29843     update : function(date)
29844     {
29845         var vd = this.activeDate;
29846         this.activeDate = date;
29847         if(vd && this.el){
29848             var t = date.getTime();
29849             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
29850                 this.cells.removeClass("x-date-selected");
29851                 this.cells.each(function(c){
29852                    if(c.dom.firstChild.dateValue == t){
29853                        c.addClass("x-date-selected");
29854                        setTimeout(function(){
29855                             try{c.dom.firstChild.focus();}catch(e){}
29856                        }, 50);
29857                        return false;
29858                    }
29859                 });
29860                 return;
29861             }
29862         }
29863         
29864         var days = date.getDaysInMonth();
29865         var firstOfMonth = date.getFirstDateOfMonth();
29866         var startingPos = firstOfMonth.getDay()-this.startDay;
29867
29868         if(startingPos <= this.startDay){
29869             startingPos += 7;
29870         }
29871
29872         var pm = date.add("mo", -1);
29873         var prevStart = pm.getDaysInMonth()-startingPos;
29874
29875         var cells = this.cells.elements;
29876         var textEls = this.textNodes;
29877         days += startingPos;
29878
29879         // convert everything to numbers so it's fast
29880         var day = 86400000;
29881         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
29882         var today = new Date().clearTime().getTime();
29883         var sel = date.clearTime().getTime();
29884         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
29885         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
29886         var ddMatch = this.disabledDatesRE;
29887         var ddText = this.disabledDatesText;
29888         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
29889         var ddaysText = this.disabledDaysText;
29890         var format = this.format;
29891
29892         var setCellClass = function(cal, cell){
29893             cell.title = "";
29894             var t = d.getTime();
29895             cell.firstChild.dateValue = t;
29896             if(t == today){
29897                 cell.className += " x-date-today";
29898                 cell.title = cal.todayText;
29899             }
29900             if(t == sel){
29901                 cell.className += " x-date-selected";
29902                 setTimeout(function(){
29903                     try{cell.firstChild.focus();}catch(e){}
29904                 }, 50);
29905             }
29906             // disabling
29907             if(t < min) {
29908                 cell.className = " x-date-disabled";
29909                 cell.title = cal.minText;
29910                 return;
29911             }
29912             if(t > max) {
29913                 cell.className = " x-date-disabled";
29914                 cell.title = cal.maxText;
29915                 return;
29916             }
29917             if(ddays){
29918                 if(ddays.indexOf(d.getDay()) != -1){
29919                     cell.title = ddaysText;
29920                     cell.className = " x-date-disabled";
29921                 }
29922             }
29923             if(ddMatch && format){
29924                 var fvalue = d.dateFormat(format);
29925                 if(ddMatch.test(fvalue)){
29926                     cell.title = ddText.replace("%0", fvalue);
29927                     cell.className = " x-date-disabled";
29928                 }
29929             }
29930         };
29931
29932         var i = 0;
29933         for(; i < startingPos; i++) {
29934             textEls[i].innerHTML = (++prevStart);
29935             d.setDate(d.getDate()+1);
29936             cells[i].className = "x-date-prevday";
29937             setCellClass(this, cells[i]);
29938         }
29939         for(; i < days; i++){
29940             intDay = i - startingPos + 1;
29941             textEls[i].innerHTML = (intDay);
29942             d.setDate(d.getDate()+1);
29943             cells[i].className = "x-date-active";
29944             setCellClass(this, cells[i]);
29945         }
29946         var extraDays = 0;
29947         for(; i < 42; i++) {
29948              textEls[i].innerHTML = (++extraDays);
29949              d.setDate(d.getDate()+1);
29950              cells[i].className = "x-date-nextday";
29951              setCellClass(this, cells[i]);
29952         }
29953
29954         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
29955         this.fireEvent('monthchange', this, date);
29956         
29957         if(!this.internalRender){
29958             var main = this.el.dom.firstChild;
29959             var w = main.offsetWidth;
29960             this.el.setWidth(w + this.el.getBorderWidth("lr"));
29961             Roo.fly(main).setWidth(w);
29962             this.internalRender = true;
29963             // opera does not respect the auto grow header center column
29964             // then, after it gets a width opera refuses to recalculate
29965             // without a second pass
29966             if(Roo.isOpera && !this.secondPass){
29967                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
29968                 this.secondPass = true;
29969                 this.update.defer(10, this, [date]);
29970             }
29971         }
29972         
29973         
29974     }
29975 });Roo.panel = {};
29976 /*
29977 * Licence: LGPL
29978 */
29979
29980 /**
29981  * @class Roo.panel.Cropbox
29982  * @extends Roo.BoxComponent
29983  * Panel Cropbox class
29984  * @cfg {String} emptyText show when image has been loaded
29985  * @cfg {String} rotateNotify show when image too small to rotate
29986  * @cfg {Number} errorTimeout default 3000
29987  * @cfg {Number} minWidth default 300
29988  * @cfg {Number} minHeight default 300
29989  * @cfg {Number} outputMaxWidth default 1200
29990  * @cfg {Number} windowSize default 300
29991  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29992  * @cfg {Boolean} isDocument (true|false) default false
29993  * @cfg {String} url action url
29994  * @cfg {String} paramName default 'imageUpload'
29995  * @cfg {String} method default POST
29996  * @cfg {Boolean} loadMask (true|false) default true
29997  * @cfg {Boolean} loadingText default 'Loading...'
29998  * 
29999  * @constructor
30000  * Create a new Cropbox
30001  * @param {Object} config The config object
30002  */
30003
30004  Roo.panel.Cropbox = function(config){
30005     Roo.panel.Cropbox.superclass.constructor.call(this, config);
30006     
30007     this.addEvents({
30008         /**
30009          * @event beforeselectfile
30010          * Fire before select file
30011          * @param {Roo.panel.Cropbox} this
30012          */
30013         "beforeselectfile" : true,
30014         /**
30015          * @event initial
30016          * Fire after initEvent
30017          * @param {Roo.panel.Cropbox} this
30018          */
30019         "initial" : true,
30020         /**
30021          * @event crop
30022          * Fire after initEvent
30023          * @param {Roo.panel.Cropbox} this
30024          * @param {String} data
30025          */
30026         "crop" : true,
30027         /**
30028          * @event prepare
30029          * Fire when preparing the file data
30030          * @param {Roo.panel.Cropbox} this
30031          * @param {Object} file
30032          */
30033         "prepare" : true,
30034         /**
30035          * @event exception
30036          * Fire when get exception
30037          * @param {Roo.panel.Cropbox} this
30038          * @param {XMLHttpRequest} xhr
30039          */
30040         "exception" : true,
30041         /**
30042          * @event beforeloadcanvas
30043          * Fire before load the canvas
30044          * @param {Roo.panel.Cropbox} this
30045          * @param {String} src
30046          */
30047         "beforeloadcanvas" : true,
30048         /**
30049          * @event trash
30050          * Fire when trash image
30051          * @param {Roo.panel.Cropbox} this
30052          */
30053         "trash" : true,
30054         /**
30055          * @event download
30056          * Fire when download the image
30057          * @param {Roo.panel.Cropbox} this
30058          */
30059         "download" : true,
30060         /**
30061          * @event footerbuttonclick
30062          * Fire when footerbuttonclick
30063          * @param {Roo.panel.Cropbox} this
30064          * @param {String} type
30065          */
30066         "footerbuttonclick" : true,
30067         /**
30068          * @event resize
30069          * Fire when resize
30070          * @param {Roo.panel.Cropbox} this
30071          */
30072         "resize" : true,
30073         /**
30074          * @event rotate
30075          * Fire when rotate the image
30076          * @param {Roo.panel.Cropbox} this
30077          * @param {String} pos
30078          */
30079         "rotate" : true,
30080         /**
30081          * @event inspect
30082          * Fire when inspect the file
30083          * @param {Roo.panel.Cropbox} this
30084          * @param {Object} file
30085          */
30086         "inspect" : true,
30087         /**
30088          * @event upload
30089          * Fire when xhr upload the file
30090          * @param {Roo.panel.Cropbox} this
30091          * @param {Object} data
30092          */
30093         "upload" : true,
30094         /**
30095          * @event arrange
30096          * Fire when arrange the file data
30097          * @param {Roo.panel.Cropbox} this
30098          * @param {Object} formData
30099          */
30100         "arrange" : true,
30101         /**
30102          * @event loadcanvas
30103          * Fire after load the canvas
30104          * @param {Roo.panel.Cropbox}
30105          * @param {Object} imgEl
30106          */
30107         "loadcanvas" : true
30108     });
30109     
30110     this.buttons = this.buttons || Roo.panel.Cropbox.footer.STANDARD;
30111 };
30112
30113 Roo.extend(Roo.panel.Cropbox, Roo.Component,  {
30114     
30115     emptyText : 'Click to upload image',
30116     rotateNotify : 'Image is too small to rotate',
30117     errorTimeout : 3000,
30118     scale : 0,
30119     baseScale : 1,
30120     rotate : 0,
30121     dragable : false,
30122     pinching : false,
30123     mouseX : 0,
30124     mouseY : 0,
30125     cropData : false,
30126     minWidth : 300,
30127     minHeight : 300,
30128     outputMaxWidth : 1200,
30129     windowSize : 300,
30130     file : false,
30131     exif : {},
30132     baseRotate : 1,
30133     cropType : 'image/jpeg',
30134     buttons : false,
30135     canvasLoaded : false,
30136     isDocument : false,
30137     method : 'POST',
30138     paramName : 'imageUpload',
30139     loadMask : true,
30140     loadingText : 'Loading...',
30141     maskEl : false,
30142     
30143     getAutoCreate : function()
30144     {
30145         var cfg = {
30146             tag : 'div',
30147             cls : 'roo-upload-cropbox',
30148             cn : [
30149                 {
30150                     tag : 'input',
30151                     cls : 'roo-upload-cropbox-selector',
30152                     type : 'file'
30153                 },
30154                 {
30155                     tag : 'div',
30156                     cls : 'roo-upload-cropbox-body',
30157                     style : 'cursor:pointer',
30158                     cn : [
30159                         {
30160                             tag : 'div',
30161                             cls : 'roo-upload-cropbox-preview'
30162                         },
30163                         {
30164                             tag : 'div',
30165                             cls : 'roo-upload-cropbox-thumb'
30166                         },
30167                         {
30168                             tag : 'div',
30169                             cls : 'roo-upload-cropbox-empty-notify',
30170                             html : this.emptyText
30171                         },
30172                         {
30173                             tag : 'div',
30174                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30175                             html : this.rotateNotify
30176                         }
30177                     ]
30178                 },
30179                 {
30180                     tag : 'div',
30181                     cls : 'roo-upload-cropbox-footer',
30182                     cn : {
30183                         tag : 'div',
30184                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30185                         cn : []
30186                     }
30187                 }
30188             ]
30189         };
30190         
30191         return cfg;
30192     },
30193     
30194     onRender : function(ct, position)
30195     {
30196         Roo.panel.Cropbox.superclass.onRender.call(this, ct, position);
30197
30198         if(this.el){
30199             if (this.el.attr('xtype')) {
30200                 this.el.attr('xtypex', this.el.attr('xtype'));
30201                 this.el.dom.removeAttribute('xtype');
30202                 
30203                 this.initEvents();
30204             }
30205         }
30206         else {
30207             var cfg = Roo.apply({},  this.getAutoCreate());
30208         
30209             cfg.id = this.id || Roo.id();
30210             
30211             if (this.cls) {
30212                 cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
30213             }
30214             
30215             if (this.style) { // fixme needs to support more complex style data.
30216                 cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
30217             }
30218             
30219             this.el = ct.createChild(cfg, position);
30220             
30221             this.initEvents();
30222         }
30223         
30224         if (this.buttons.length) {
30225             
30226             Roo.each(this.buttons, function(bb) {
30227                 
30228                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30229                 
30230                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30231                 
30232             }, this);
30233         }
30234         
30235         if(this.loadMask){
30236             this.maskEl = this.el;
30237         }
30238     },
30239     
30240     initEvents : function()
30241     {
30242         this.urlAPI = (window.createObjectURL && window) || 
30243                                 (window.URL && URL.revokeObjectURL && URL) || 
30244                                 (window.webkitURL && webkitURL);
30245                         
30246         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30247         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30248         
30249         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30250         this.selectorEl.hide();
30251         
30252         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30253         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30254         
30255         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30256         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30257         this.thumbEl.hide();
30258         
30259         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30260         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30261         
30262         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30263         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30264         this.errorEl.hide();
30265         
30266         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30267         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30268         this.footerEl.hide();
30269         
30270         this.setThumbBoxSize();
30271         
30272         this.bind();
30273         
30274         this.resize();
30275         
30276         this.fireEvent('initial', this);
30277     },
30278
30279     bind : function()
30280     {
30281         var _this = this;
30282         
30283         window.addEventListener("resize", function() { _this.resize(); } );
30284         
30285         this.bodyEl.on('click', this.beforeSelectFile, this);
30286         
30287         if(Roo.isTouch){
30288             this.bodyEl.on('touchstart', this.onTouchStart, this);
30289             this.bodyEl.on('touchmove', this.onTouchMove, this);
30290             this.bodyEl.on('touchend', this.onTouchEnd, this);
30291         }
30292         
30293         if(!Roo.isTouch){
30294             this.bodyEl.on('mousedown', this.onMouseDown, this);
30295             this.bodyEl.on('mousemove', this.onMouseMove, this);
30296             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30297             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30298             Roo.get(document).on('mouseup', this.onMouseUp, this);
30299         }
30300         
30301         this.selectorEl.on('change', this.onFileSelected, this);
30302     },
30303     
30304     reset : function()
30305     {    
30306         this.scale = 0;
30307         this.baseScale = 1;
30308         this.rotate = 0;
30309         this.baseRotate = 1;
30310         this.dragable = false;
30311         this.pinching = false;
30312         this.mouseX = 0;
30313         this.mouseY = 0;
30314         this.cropData = false;
30315         this.notifyEl.dom.innerHTML = this.emptyText;
30316         
30317         // this.selectorEl.dom.value = '';
30318         
30319     },
30320     
30321     resize : function()
30322     {
30323         if(this.fireEvent('resize', this) != false){
30324             this.setThumbBoxPosition();
30325             this.setCanvasPosition();
30326         }
30327     },
30328     
30329     onFooterButtonClick : function(e, el, o, type)
30330     {
30331         switch (type) {
30332             case 'rotate-left' :
30333                 this.onRotateLeft(e);
30334                 break;
30335             case 'rotate-right' :
30336                 this.onRotateRight(e);
30337                 break;
30338             case 'picture' :
30339                 this.beforeSelectFile(e);
30340                 break;
30341             case 'trash' :
30342                 this.trash(e);
30343                 break;
30344             case 'crop' :
30345                 this.crop(e);
30346                 break;
30347             case 'download' :
30348                 this.download(e);
30349                 break;
30350             case 'center' :
30351                 this.center(e);
30352                 break;
30353             default :
30354                 break;
30355         }
30356         
30357         this.fireEvent('footerbuttonclick', this, type);
30358     },
30359     
30360     beforeSelectFile : function(e)
30361     {
30362         e.preventDefault();
30363         
30364         if(this.fireEvent('beforeselectfile', this) != false){
30365             this.selectorEl.dom.click();
30366         }
30367     },
30368     
30369     onFileSelected : function(e)
30370     {
30371         e.preventDefault();
30372         
30373         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30374             return;
30375         }
30376         
30377         var file = this.selectorEl.dom.files[0];
30378         
30379         if(this.fireEvent('inspect', this, file) != false){
30380             this.prepare(file);
30381         }
30382         
30383     },
30384     
30385     trash : function(e)
30386     {
30387         this.fireEvent('trash', this);
30388     },
30389     
30390     download : function(e)
30391     {
30392         this.fireEvent('download', this);
30393     },
30394
30395     center : function(e)
30396     {
30397         this.setCanvasPosition();
30398     },
30399     
30400     loadCanvas : function(src)
30401     {   
30402         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30403             
30404             this.reset();
30405             
30406             this.imageEl = document.createElement('img');
30407             
30408             var _this = this;
30409             
30410             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30411             
30412             this.imageEl.src = src;
30413         }
30414     },
30415     
30416     onLoadCanvas : function()
30417     {   
30418         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30419         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30420
30421         if(this.fireEvent('loadcanvas', this, this.imageEl) != false){
30422         
30423             this.bodyEl.un('click', this.beforeSelectFile, this);
30424             
30425             this.notifyEl.hide();
30426             this.thumbEl.show();
30427             this.footerEl.show();
30428             
30429             this.baseRotateLevel();
30430             
30431             if(this.isDocument){
30432                 this.setThumbBoxSize();
30433             }
30434             
30435             this.setThumbBoxPosition();
30436             
30437             this.baseScaleLevel();
30438             
30439             this.draw();
30440             
30441             this.resize();
30442             
30443             this.canvasLoaded = true;
30444         
30445         }
30446         
30447         if(this.loadMask){
30448             this.maskEl.unmask();
30449         }
30450         
30451     },
30452     
30453     setCanvasPosition : function(center = true)
30454     {   
30455         if(!this.canvasEl){
30456             return;
30457         }
30458
30459         var newCenterLeft = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30460         var newCenterTop = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30461
30462         if(center) {
30463             this.previewEl.setLeft(newCenterLeft);
30464             this.previewEl.setTop(newCenterTop);
30465
30466             return;
30467         }
30468         
30469         var oldScaleLevel = this.baseScale * Math.pow(1.02, this.startScale);
30470         var oldCanvasWidth = Math.floor(this.imageEl.OriginWidth * oldScaleLevel);
30471         var oldCanvasHeight = Math.floor(this.imageEl.OriginHeight * oldScaleLevel);
30472
30473         var oldCenterLeft = Math.ceil((this.bodyEl.getWidth() - oldCanvasWidth) / 2);
30474         var oldCenterTop = Math.ceil((this.bodyEl.getHeight() - oldCanvasHeight) / 2);
30475
30476         var leftDiff = newCenterLeft - oldCenterLeft;
30477         var topDiff = newCenterTop - oldCenterTop;
30478
30479         var newPreviewLeft = this.previewEl.getLeft(true) + leftDiff;
30480         var newPreviewTop = this.previewEl.getTop(true) + topDiff;
30481
30482         this.previewEl.setLeft(newPreviewLeft);
30483         this.previewEl.setTop(newPreviewTop);
30484         
30485     },
30486     
30487     onMouseDown : function(e)
30488     {   
30489         e.stopEvent();
30490         
30491         this.dragable = true;
30492         this.pinching = false;
30493         
30494         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30495             this.dragable = false;
30496             return;
30497         }
30498         
30499         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30500         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30501         
30502     },
30503     
30504     onMouseMove : function(e)
30505     {   
30506         e.stopEvent();
30507         
30508         if(!this.canvasLoaded){
30509             return;
30510         }
30511         
30512         if (!this.dragable){
30513             return;
30514         }
30515
30516         var maxPaddingLeft = this.canvasEl.width / 0.9 * 0.05;
30517         var maxPaddingTop = maxPaddingLeft * this.minHeight / this.minWidth;
30518
30519         if ((this.imageEl.OriginWidth / this.imageEl.OriginHeight <= this.minWidth / this.minHeight)) {
30520             maxPaddingLeft = (this.canvasEl.height * this.minWidth / this.minHeight - this.canvasEl.width) / 2 + maxPaddingLeft;
30521         }
30522
30523         if ((this.imageEl.OriginWidth / this.imageEl.OriginHeight >= this.minWidth / this.minHeight)) {
30524             maxPaddingTop = (this.canvasEl.width * this.minHeight / this.minWidth - this.canvasEl.height) / 2 + maxPaddingTop;
30525         }
30526         
30527         var minX = Math.ceil(this.thumbEl.getLeft(true) + this.thumbEl.getWidth() - this.canvasEl.width - maxPaddingLeft);
30528         var minY = Math.ceil(this.thumbEl.getTop(true) + this.thumbEl.getHeight() - this.canvasEl.height - maxPaddingTop);
30529         
30530         var maxX = Math.ceil(this.thumbEl.getLeft(true) + maxPaddingLeft);
30531         var maxY = Math.ceil(this.thumbEl.getTop(true) +  maxPaddingTop);
30532
30533         if(minX > maxX) {
30534             var tempX = minX;
30535             minX = maxX;
30536             maxX = tempX;
30537         }
30538
30539         if(minY > maxY) {
30540             var tempY = minY;
30541             minY = maxY;
30542             maxY = tempY;
30543         }
30544
30545         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30546         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30547         
30548         x = x - this.mouseX;
30549         y = y - this.mouseY;
30550
30551         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30552         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30553         
30554         bgX = (bgX < minX) ? minX : ((bgX > maxX) ? maxX : bgX);
30555         bgY = (bgY < minY) ? minY : ((bgY > maxY) ? maxY : bgY);
30556         
30557         this.previewEl.setLeft(bgX);
30558         this.previewEl.setTop(bgY);
30559         
30560         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30561         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30562     },
30563     
30564     onMouseUp : function(e)
30565     {   
30566         e.stopEvent();
30567         
30568         this.dragable = false;
30569     },
30570     
30571     onMouseWheel : function(e)
30572     {   
30573         e.stopEvent();
30574         
30575         this.startScale = this.scale;
30576         this.scale = (e.getWheelDelta() > 0) ? (this.scale + 1) : (this.scale - 1);
30577         
30578         if(!this.zoomable()){
30579             this.scale = this.startScale;
30580             return;
30581         }
30582
30583         
30584         this.draw();
30585         
30586         return;
30587     },
30588     
30589     zoomable : function()
30590     {
30591         var minScale = this.thumbEl.getWidth() / this.minWidth;
30592         
30593         if(this.minWidth < this.minHeight){
30594             minScale = this.thumbEl.getHeight() / this.minHeight;
30595         }
30596         
30597         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30598         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30599  
30600         var maxWidth = this.imageEl.OriginWidth;
30601         var maxHeight = this.imageEl.OriginHeight;
30602
30603
30604         var newCanvasWidth = Math.floor(this.imageEl.OriginWidth * this.getScaleLevel());
30605         var newCanvasHeight = Math.floor(this.imageEl.OriginHeight * this.getScaleLevel());
30606
30607         var oldCenterLeft = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30608         var oldCenterTop = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30609
30610         var newCenterLeft = Math.ceil((this.bodyEl.getWidth() - newCanvasWidth) / 2);
30611         var newCenterTop = Math.ceil((this.bodyEl.getHeight() - newCanvasHeight) / 2);
30612
30613         var leftDiff = newCenterLeft - oldCenterLeft;
30614         var topDiff = newCenterTop - oldCenterTop;
30615
30616         var newPreviewLeft = this.previewEl.getLeft(true) + leftDiff;
30617         var newPreviewTop = this.previewEl.getTop(true) + topDiff;
30618
30619         var paddingLeft = newPreviewLeft - this.thumbEl.getLeft(true);
30620         var paddingTop = newPreviewTop - this.thumbEl.getTop(true);
30621
30622         var paddingRight = this.thumbEl.getLeft(true) + this.thumbEl.getWidth() - newCanvasWidth - newPreviewLeft;
30623         var paddingBottom = this.thumbEl.getTop(true) + this.thumbEl.getHeight() - newCanvasHeight - newPreviewTop;
30624
30625         var maxPaddingLeft = newCanvasWidth / 0.9 * 0.05;
30626         var maxPaddingTop = maxPaddingLeft * this.minHeight / this.minWidth;
30627
30628         if ((this.imageEl.OriginWidth / this.imageEl.OriginHeight <= this.minWidth / this.minHeight)) {
30629             maxPaddingLeft = (newCanvasHeight * this.minWidth / this.minHeight - newCanvasWidth) / 2 + maxPaddingLeft;
30630         }
30631
30632         if ((this.imageEl.OriginWidth / this.imageEl.OriginHeight >= this.minWidth / this.minHeight)) {
30633             maxPaddingTop = (newCanvasWidth * this.minHeight / this.minWidth - newCanvasHeight) / 2 + maxPaddingTop;
30634         }
30635         
30636         if(
30637                 this.isDocument &&
30638                 (this.rotate == 0 || this.rotate == 180) && 
30639                 (
30640                     width > this.imageEl.OriginWidth || 
30641                     height > this.imageEl.OriginHeight ||
30642                     (width < this.minWidth && height < this.minHeight)
30643                 )
30644         ){
30645             return false;
30646         }
30647         
30648         if(
30649                 this.isDocument &&
30650                 (this.rotate == 90 || this.rotate == 270) && 
30651                 (
30652                     width > this.imageEl.OriginWidth || 
30653                     height > this.imageEl.OriginHeight ||
30654                     (width < this.minHeight && height < this.minWidth)
30655                 )
30656         ){
30657             return false;
30658         }
30659         
30660         if(
30661                 !this.isDocument &&
30662                 (this.rotate == 0 || this.rotate == 180) && 
30663                 (
30664                     // for zoom out
30665                     paddingLeft > maxPaddingLeft ||
30666                     paddingRight > maxPaddingLeft ||
30667                     paddingTop > maxPaddingTop ||
30668                     paddingBottom > maxPaddingTop ||
30669                     // for zoom in
30670                     width > maxWidth ||
30671                     height > maxHeight
30672                 )
30673         ){
30674             return false;
30675         }
30676         
30677         if(
30678                 !this.isDocument &&
30679                 (this.rotate == 90 || this.rotate == 270) && 
30680                 (
30681                     width < this.minHeight || 
30682                     width > this.imageEl.OriginWidth || 
30683                     height < this.minWidth || 
30684                     height > this.imageEl.OriginHeight
30685                 )
30686         ){
30687             return false;
30688         }
30689         
30690         return true;
30691         
30692     },
30693     
30694     onRotateLeft : function(e)
30695     {   
30696         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30697             
30698             var minScale = this.thumbEl.getWidth() / this.minWidth;
30699             
30700             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30701             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30702             
30703             this.startScale = this.scale;
30704             
30705             while (this.getScaleLevel() < minScale){
30706             
30707                 this.scale = this.scale + 1;
30708                 
30709                 if(!this.zoomable()){
30710                     break;
30711                 }
30712                 
30713                 if(
30714                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30715                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30716                 ){
30717                     continue;
30718                 }
30719                 
30720                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30721
30722                 this.draw();
30723                 
30724                 return;
30725             }
30726             
30727             this.scale = this.startScale;
30728             
30729             this.onRotateFail();
30730             
30731             return false;
30732         }
30733         
30734         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30735
30736         if(this.isDocument){
30737             this.setThumbBoxSize();
30738             this.setThumbBoxPosition();
30739             this.setCanvasPosition();
30740         }
30741         
30742         this.draw();
30743         
30744         this.fireEvent('rotate', this, 'left');
30745         
30746     },
30747     
30748     onRotateRight : function(e)
30749     {
30750         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30751             
30752             var minScale = this.thumbEl.getWidth() / this.minWidth;
30753         
30754             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30755             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30756             
30757             this.startScale = this.scale;
30758             
30759             while (this.getScaleLevel() < minScale){
30760             
30761                 this.scale = this.scale + 1;
30762                 
30763                 if(!this.zoomable()){
30764                     break;
30765                 }
30766                 
30767                 if(
30768                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30769                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30770                 ){
30771                     continue;
30772                 }
30773                 
30774                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30775
30776                 this.draw();
30777                 
30778                 return;
30779             }
30780             
30781             this.scale = this.startScale;
30782             
30783             this.onRotateFail();
30784             
30785             return false;
30786         }
30787         
30788         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30789
30790         if(this.isDocument){
30791             this.setThumbBoxSize();
30792             this.setThumbBoxPosition();
30793             this.setCanvasPosition();
30794         }
30795         
30796         this.draw();
30797         
30798         this.fireEvent('rotate', this, 'right');
30799     },
30800     
30801     onRotateFail : function()
30802     {
30803         this.errorEl.show(true);
30804         
30805         var _this = this;
30806         
30807         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30808     },
30809     
30810     draw : function()
30811     {
30812         this.previewEl.dom.innerHTML = '';
30813         
30814         var canvasEl = document.createElement("canvas");
30815         
30816         var contextEl = canvasEl.getContext("2d");
30817         
30818         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30819         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30820         var center = this.imageEl.OriginWidth / 2;
30821         
30822         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30823             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30824             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30825             center = this.imageEl.OriginHeight / 2;
30826         }
30827         
30828         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30829         
30830         contextEl.translate(center, center);
30831         contextEl.rotate(this.rotate * Math.PI / 180);
30832
30833         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30834         
30835         this.canvasEl = document.createElement("canvas");
30836         
30837         this.contextEl = this.canvasEl.getContext("2d");
30838         
30839         switch (this.rotate) {
30840             case 0 :
30841                 
30842                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30843                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30844                 
30845                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30846                 
30847                 break;
30848             case 90 : 
30849                 
30850                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30851                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30852                 
30853                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30854                     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);
30855                     break;
30856                 }
30857                 
30858                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30859                 
30860                 break;
30861             case 180 :
30862                 
30863                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30864                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30865                 
30866                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30867                     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);
30868                     break;
30869                 }
30870                 
30871                 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);
30872                 
30873                 break;
30874             case 270 :
30875                 
30876                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30877                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30878         
30879                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30880                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30881                     break;
30882                 }
30883                 
30884                 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);
30885                 
30886                 break;
30887             default : 
30888                 break;
30889         }
30890         
30891         this.previewEl.appendChild(this.canvasEl);
30892         
30893         this.setCanvasPosition(false);
30894     },
30895     
30896     crop : function()
30897     {
30898         if(!this.canvasLoaded){
30899             return;
30900         }
30901         
30902         var imageCanvas = document.createElement("canvas");
30903         
30904         var imageContext = imageCanvas.getContext("2d");
30905         
30906         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30907         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30908         
30909         var center = imageCanvas.width / 2;
30910         
30911         imageContext.translate(center, center);
30912         
30913         imageContext.rotate(this.rotate * Math.PI / 180);
30914         
30915         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30916         
30917         var canvas = document.createElement("canvas");
30918         
30919         var context = canvas.getContext("2d");
30920
30921         canvas.width = this.thumbEl.getWidth() / this.getScaleLevel();
30922         
30923         canvas.height = this.thumbEl.getHeight() / this.getScaleLevel();
30924
30925         switch (this.rotate) {
30926             case 0 :
30927                 
30928                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30929                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30930                 
30931                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30932                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30933                 
30934                 var sx = this.thumbEl.getLeft(true) - this.previewEl.getLeft(true);
30935                 var sy = this.thumbEl.getTop(true) - this.previewEl.getTop(true);
30936
30937                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30938                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30939
30940                 if(canvas.width > this.outputMaxWidth) {
30941                     var scale = this.outputMaxWidth / canvas.width;
30942                     canvas.width = canvas.width * scale;
30943                     canvas.height = canvas.height * scale;
30944                     context.scale(scale, scale);
30945                 }
30946
30947                 context.fillStyle = 'white';
30948                 context.fillRect(0, 0, this.thumbEl.getWidth() / this.getScaleLevel(), this.thumbEl.getHeight() / this.getScaleLevel());
30949
30950
30951                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30952                 
30953                 break;
30954             case 90 : 
30955                 
30956                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30957                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30958                 
30959                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30960                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30961                 
30962                 var targetWidth = this.minWidth - 2 * x;
30963                 var targetHeight = this.minHeight - 2 * y;
30964                 
30965                 var scale = 1;
30966                 
30967                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30968                     scale = targetWidth / width;
30969                 }
30970                 
30971                 if(x > 0 && y == 0){
30972                     scale = targetHeight / height;
30973                 }
30974                 
30975                 if(x > 0 && y > 0){
30976                     scale = targetWidth / width;
30977                     
30978                     if(width < height){
30979                         scale = targetHeight / height;
30980                     }
30981                 }
30982                 
30983                 context.scale(scale, scale);
30984                 
30985                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30986                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30987
30988                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30989                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30990                 
30991                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30992                 
30993                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30994                 
30995                 break;
30996             case 180 :
30997                 
30998                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30999                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31000                 
31001                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31002                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31003                 
31004                 var targetWidth = this.minWidth - 2 * x;
31005                 var targetHeight = this.minHeight - 2 * y;
31006                 
31007                 var scale = 1;
31008                 
31009                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31010                     scale = targetWidth / width;
31011                 }
31012                 
31013                 if(x > 0 && y == 0){
31014                     scale = targetHeight / height;
31015                 }
31016                 
31017                 if(x > 0 && y > 0){
31018                     scale = targetWidth / width;
31019                     
31020                     if(width < height){
31021                         scale = targetHeight / height;
31022                     }
31023                 }
31024                 
31025                 context.scale(scale, scale);
31026                 
31027                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31028                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31029
31030                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31031                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31032
31033                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31034                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31035                 
31036                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31037                 
31038                 break;
31039             case 270 :
31040                 
31041                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31042                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31043                 
31044                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31045                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31046                 
31047                 var targetWidth = this.minWidth - 2 * x;
31048                 var targetHeight = this.minHeight - 2 * y;
31049                 
31050                 var scale = 1;
31051                 
31052                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31053                     scale = targetWidth / width;
31054                 }
31055                 
31056                 if(x > 0 && y == 0){
31057                     scale = targetHeight / height;
31058                 }
31059                 
31060                 if(x > 0 && y > 0){
31061                     scale = targetWidth / width;
31062                     
31063                     if(width < height){
31064                         scale = targetHeight / height;
31065                     }
31066                 }
31067                 
31068                 context.scale(scale, scale);
31069                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31070                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31071
31072                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31073                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31074                 
31075                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31076                 
31077                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31078                 
31079                 break;
31080             default : 
31081                 break;
31082         }
31083         
31084         this.cropData = canvas.toDataURL(this.cropType);
31085         
31086         if(this.fireEvent('crop', this, this.cropData) !== false){
31087             this.process(this.file, this.cropData);
31088         }
31089         
31090         return;
31091         
31092     },
31093     
31094     setThumbBoxSize : function()
31095     {
31096         var width, height;
31097         
31098         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31099             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31100             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31101             
31102             this.minWidth = width;
31103             this.minHeight = height;
31104             
31105             if(this.rotate == 90 || this.rotate == 270){
31106                 this.minWidth = height;
31107                 this.minHeight = width;
31108             }
31109         }
31110         
31111         height = this.windowSize;
31112         width = Math.ceil(this.minWidth * height / this.minHeight);
31113         
31114         if(this.minWidth > this.minHeight){
31115             width = this.windowSize;
31116             height = Math.ceil(this.minHeight * width / this.minWidth);
31117         }
31118         
31119         this.thumbEl.setStyle({
31120             width : width + 'px',
31121             height : height + 'px'
31122         });
31123
31124         return;
31125             
31126     },
31127     
31128     setThumbBoxPosition : function()
31129     {
31130         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31131         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31132         
31133         this.thumbEl.setLeft(x);
31134         this.thumbEl.setTop(y);
31135         
31136     },
31137     
31138     baseRotateLevel : function()
31139     {
31140         this.baseRotate = 1;
31141         
31142         if(
31143                 typeof(this.exif) != 'undefined' &&
31144                 typeof(this.exif[Roo.panel.Cropbox['tags']['Orientation']]) != 'undefined' &&
31145                 [1, 3, 6, 8].indexOf(this.exif[Roo.panel.Cropbox['tags']['Orientation']]) != -1
31146         ){
31147             this.baseRotate = this.exif[Roo.panel.Cropbox['tags']['Orientation']];
31148         }
31149         
31150         this.rotate = Roo.panel.Cropbox['Orientation'][this.baseRotate];
31151         
31152     },
31153     
31154     baseScaleLevel : function()
31155     {
31156         var width, height;
31157         
31158         if(this.isDocument){
31159             
31160             if(this.baseRotate == 6 || this.baseRotate == 8){
31161             
31162                 height = this.thumbEl.getHeight();
31163                 this.baseScale = height / this.imageEl.OriginWidth;
31164
31165                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31166                     width = this.thumbEl.getWidth();
31167                     this.baseScale = width / this.imageEl.OriginHeight;
31168                 }
31169
31170                 return;
31171             }
31172
31173             height = this.thumbEl.getHeight();
31174             this.baseScale = height / this.imageEl.OriginHeight;
31175
31176             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31177                 width = this.thumbEl.getWidth();
31178                 this.baseScale = width / this.imageEl.OriginWidth;
31179             }
31180
31181             return;
31182         }
31183         
31184         if(this.baseRotate == 6 || this.baseRotate == 8){
31185             
31186             width = this.thumbEl.getHeight();
31187             this.baseScale = width / this.imageEl.OriginHeight;
31188             
31189             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31190                 height = this.thumbEl.getWidth();
31191                 this.baseScale = height / this.imageEl.OriginHeight;
31192             }
31193             
31194             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31195                 height = this.thumbEl.getWidth();
31196                 this.baseScale = height / this.imageEl.OriginHeight;
31197                 
31198                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31199                     width = this.thumbEl.getHeight();
31200                     this.baseScale = width / this.imageEl.OriginWidth;
31201                 }
31202             }
31203             
31204             return;
31205         }
31206         
31207         width = this.thumbEl.getWidth();
31208         this.baseScale = width / this.imageEl.OriginWidth;
31209         
31210         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31211             height = this.thumbEl.getHeight();
31212             this.baseScale = height / this.imageEl.OriginHeight;
31213         }
31214         
31215         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31216             
31217             height = this.thumbEl.getHeight();
31218             this.baseScale = height / this.imageEl.OriginHeight;
31219             
31220             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31221                 width = this.thumbEl.getWidth();
31222                 this.baseScale = width / this.imageEl.OriginWidth;
31223             }
31224             
31225         }
31226
31227         if(this.imageEl.OriginWidth < this.minWidth || this.imageEl.OriginHeight < this.minHeight) {
31228             this.baseScale = width / this.minWidth;
31229         }
31230
31231         return;
31232     },
31233     
31234     getScaleLevel : function()
31235     {
31236         return this.baseScale * Math.pow(1.02, this.scale);
31237     },
31238     
31239     onTouchStart : function(e)
31240     {
31241         if(!this.canvasLoaded){
31242             this.beforeSelectFile(e);
31243             return;
31244         }
31245         
31246         var touches = e.browserEvent.touches;
31247         
31248         if(!touches){
31249             return;
31250         }
31251         
31252         if(touches.length == 1){
31253             this.onMouseDown(e);
31254             return;
31255         }
31256         
31257         if(touches.length != 2){
31258             return;
31259         }
31260         
31261         var coords = [];
31262         
31263         for(var i = 0, finger; finger = touches[i]; i++){
31264             coords.push(finger.pageX, finger.pageY);
31265         }
31266         
31267         var x = Math.pow(coords[0] - coords[2], 2);
31268         var y = Math.pow(coords[1] - coords[3], 2);
31269         
31270         this.startDistance = Math.sqrt(x + y);
31271         
31272         this.startScale = this.scale;
31273         
31274         this.pinching = true;
31275         this.dragable = false;
31276         
31277     },
31278     
31279     onTouchMove : function(e)
31280     {
31281         if(!this.pinching && !this.dragable){
31282             return;
31283         }
31284         
31285         var touches = e.browserEvent.touches;
31286         
31287         if(!touches){
31288             return;
31289         }
31290         
31291         if(this.dragable){
31292             this.onMouseMove(e);
31293             return;
31294         }
31295         
31296         var coords = [];
31297         
31298         for(var i = 0, finger; finger = touches[i]; i++){
31299             coords.push(finger.pageX, finger.pageY);
31300         }
31301         
31302         var x = Math.pow(coords[0] - coords[2], 2);
31303         var y = Math.pow(coords[1] - coords[3], 2);
31304         
31305         this.endDistance = Math.sqrt(x + y);
31306         
31307         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31308         
31309         if(!this.zoomable()){
31310             this.scale = this.startScale;
31311             return;
31312         }
31313         
31314         this.draw();
31315         
31316     },
31317     
31318     onTouchEnd : function(e)
31319     {
31320         this.pinching = false;
31321         this.dragable = false;
31322         
31323     },
31324     
31325     process : function(file, crop)
31326     {
31327         if(this.loadMask){
31328             this.maskEl.mask(this.loadingText);
31329         }
31330         
31331         this.xhr = new XMLHttpRequest();
31332         
31333         file.xhr = this.xhr;
31334
31335         this.xhr.open(this.method, this.url, true);
31336         
31337         var headers = {
31338             "Accept": "application/json",
31339             "Cache-Control": "no-cache",
31340             "X-Requested-With": "XMLHttpRequest"
31341         };
31342         
31343         for (var headerName in headers) {
31344             var headerValue = headers[headerName];
31345             if (headerValue) {
31346                 this.xhr.setRequestHeader(headerName, headerValue);
31347             }
31348         }
31349         
31350         var _this = this;
31351         
31352         this.xhr.onload = function()
31353         {
31354             _this.xhrOnLoad(_this.xhr);
31355         }
31356         
31357         this.xhr.onerror = function()
31358         {
31359             _this.xhrOnError(_this.xhr);
31360         }
31361         
31362         var formData = new FormData();
31363
31364         formData.append('returnHTML', 'NO');
31365
31366         if(crop){
31367             formData.append('crop', crop);
31368             var blobBin = atob(crop.split(',')[1]);
31369             var array = [];
31370             for(var i = 0; i < blobBin.length; i++) {
31371                 array.push(blobBin.charCodeAt(i));
31372             }
31373             var croppedFile =new Blob([new Uint8Array(array)], {type: this.cropType});
31374             formData.append(this.paramName, croppedFile, file.name);
31375         }
31376         
31377         if(typeof(file.filename) != 'undefined'){
31378             formData.append('filename', file.filename);
31379         }
31380         
31381         if(typeof(file.mimetype) != 'undefined'){
31382             formData.append('mimetype', file.mimetype);
31383         }
31384
31385         if(this.fireEvent('arrange', this, formData) != false){
31386             this.xhr.send(formData);
31387         };
31388     },
31389     
31390     xhrOnLoad : function(xhr)
31391     {
31392         if(this.loadMask){
31393             this.maskEl.unmask();
31394         }
31395         
31396         if (xhr.readyState !== 4) {
31397             this.fireEvent('exception', this, xhr);
31398             return;
31399         }
31400
31401         var response = Roo.decode(xhr.responseText);
31402         
31403         if(!response.success){
31404             this.fireEvent('exception', this, xhr);
31405             return;
31406         }
31407         
31408         var response = Roo.decode(xhr.responseText);
31409         
31410         this.fireEvent('upload', this, response);
31411         
31412     },
31413     
31414     xhrOnError : function()
31415     {
31416         if(this.loadMask){
31417             this.maskEl.unmask();
31418         }
31419         
31420         Roo.log('xhr on error');
31421         
31422         var response = Roo.decode(xhr.responseText);
31423           
31424         Roo.log(response);
31425         
31426     },
31427     
31428     prepare : function(file)
31429     {   
31430         if(this.loadMask){
31431             this.maskEl.mask(this.loadingText);
31432         }
31433         
31434         this.file = false;
31435         this.exif = {};
31436         
31437         if(typeof(file) === 'string'){
31438             this.loadCanvas(file);
31439             return;
31440         }
31441         
31442         if(!file || !this.urlAPI){
31443             return;
31444         }
31445         
31446         this.file = file;
31447         if(typeof(file.type) != 'undefined' && file.type.length != 0) {
31448             this.cropType = file.type;
31449         }
31450         
31451         var _this = this;
31452         
31453         if(this.fireEvent('prepare', this, this.file) != false){
31454             
31455             var reader = new FileReader();
31456             
31457             reader.onload = function (e) {
31458                 if (e.target.error) {
31459                     Roo.log(e.target.error);
31460                     return;
31461                 }
31462                 
31463                 var buffer = e.target.result,
31464                     dataView = new DataView(buffer),
31465                     offset = 2,
31466                     maxOffset = dataView.byteLength - 4,
31467                     markerBytes,
31468                     markerLength;
31469                 
31470                 if (dataView.getUint16(0) === 0xffd8) {
31471                     while (offset < maxOffset) {
31472                         markerBytes = dataView.getUint16(offset);
31473                         
31474                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31475                             markerLength = dataView.getUint16(offset + 2) + 2;
31476                             if (offset + markerLength > dataView.byteLength) {
31477                                 Roo.log('Invalid meta data: Invalid segment size.');
31478                                 break;
31479                             }
31480                             
31481                             if(markerBytes == 0xffe1){
31482                                 _this.parseExifData(
31483                                     dataView,
31484                                     offset,
31485                                     markerLength
31486                                 );
31487                             }
31488                             
31489                             offset += markerLength;
31490                             
31491                             continue;
31492                         }
31493                         
31494                         break;
31495                     }
31496                     
31497                 }
31498                 
31499                 var url = _this.urlAPI.createObjectURL(_this.file);
31500                 
31501                 _this.loadCanvas(url);
31502                 
31503                 return;
31504             }
31505             
31506             reader.readAsArrayBuffer(this.file);
31507             
31508         }
31509         
31510     },
31511     
31512     parseExifData : function(dataView, offset, length)
31513     {
31514         var tiffOffset = offset + 10,
31515             littleEndian,
31516             dirOffset;
31517     
31518         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31519             // No Exif data, might be XMP data instead
31520             return;
31521         }
31522         
31523         // Check for the ASCII code for "Exif" (0x45786966):
31524         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31525             // No Exif data, might be XMP data instead
31526             return;
31527         }
31528         if (tiffOffset + 8 > dataView.byteLength) {
31529             Roo.log('Invalid Exif data: Invalid segment size.');
31530             return;
31531         }
31532         // Check for the two null bytes:
31533         if (dataView.getUint16(offset + 8) !== 0x0000) {
31534             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31535             return;
31536         }
31537         // Check the byte alignment:
31538         switch (dataView.getUint16(tiffOffset)) {
31539         case 0x4949:
31540             littleEndian = true;
31541             break;
31542         case 0x4D4D:
31543             littleEndian = false;
31544             break;
31545         default:
31546             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31547             return;
31548         }
31549         // Check for the TIFF tag marker (0x002A):
31550         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31551             Roo.log('Invalid Exif data: Missing TIFF marker.');
31552             return;
31553         }
31554         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31555         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31556         
31557         this.parseExifTags(
31558             dataView,
31559             tiffOffset,
31560             tiffOffset + dirOffset,
31561             littleEndian
31562         );
31563     },
31564     
31565     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31566     {
31567         var tagsNumber,
31568             dirEndOffset,
31569             i;
31570         if (dirOffset + 6 > dataView.byteLength) {
31571             Roo.log('Invalid Exif data: Invalid directory offset.');
31572             return;
31573         }
31574         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31575         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31576         if (dirEndOffset + 4 > dataView.byteLength) {
31577             Roo.log('Invalid Exif data: Invalid directory size.');
31578             return;
31579         }
31580         for (i = 0; i < tagsNumber; i += 1) {
31581             this.parseExifTag(
31582                 dataView,
31583                 tiffOffset,
31584                 dirOffset + 2 + 12 * i, // tag offset
31585                 littleEndian
31586             );
31587         }
31588         // Return the offset to the next directory:
31589         return dataView.getUint32(dirEndOffset, littleEndian);
31590     },
31591     
31592     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31593     {
31594         var tag = dataView.getUint16(offset, littleEndian);
31595         
31596         this.exif[tag] = this.getExifValue(
31597             dataView,
31598             tiffOffset,
31599             offset,
31600             dataView.getUint16(offset + 2, littleEndian), // tag type
31601             dataView.getUint32(offset + 4, littleEndian), // tag length
31602             littleEndian
31603         );
31604     },
31605     
31606     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31607     {
31608         var tagType = Roo.panel.Cropbox.exifTagTypes[type],
31609             tagSize,
31610             dataOffset,
31611             values,
31612             i,
31613             str,
31614             c;
31615     
31616         if (!tagType) {
31617             Roo.log('Invalid Exif data: Invalid tag type.');
31618             return;
31619         }
31620         
31621         tagSize = tagType.size * length;
31622         // Determine if the value is contained in the dataOffset bytes,
31623         // or if the value at the dataOffset is a pointer to the actual data:
31624         dataOffset = tagSize > 4 ?
31625                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31626         if (dataOffset + tagSize > dataView.byteLength) {
31627             Roo.log('Invalid Exif data: Invalid data offset.');
31628             return;
31629         }
31630         if (length === 1) {
31631             return tagType.getValue(dataView, dataOffset, littleEndian);
31632         }
31633         values = [];
31634         for (i = 0; i < length; i += 1) {
31635             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31636         }
31637         
31638         if (tagType.ascii) {
31639             str = '';
31640             // Concatenate the chars:
31641             for (i = 0; i < values.length; i += 1) {
31642                 c = values[i];
31643                 // Ignore the terminating NULL byte(s):
31644                 if (c === '\u0000') {
31645                     break;
31646                 }
31647                 str += c;
31648             }
31649             return str;
31650         }
31651         return values;
31652     }
31653     
31654 });
31655
31656 Roo.apply(Roo.panel.Cropbox, {
31657     tags : {
31658         'Orientation': 0x0112
31659     },
31660     
31661     Orientation: {
31662             1: 0, //'top-left',
31663 //            2: 'top-right',
31664             3: 180, //'bottom-right',
31665 //            4: 'bottom-left',
31666 //            5: 'left-top',
31667             6: 90, //'right-top',
31668 //            7: 'right-bottom',
31669             8: 270 //'left-bottom'
31670     },
31671     
31672     exifTagTypes : {
31673         // byte, 8-bit unsigned int:
31674         1: {
31675             getValue: function (dataView, dataOffset) {
31676                 return dataView.getUint8(dataOffset);
31677             },
31678             size: 1
31679         },
31680         // ascii, 8-bit byte:
31681         2: {
31682             getValue: function (dataView, dataOffset) {
31683                 return String.fromCharCode(dataView.getUint8(dataOffset));
31684             },
31685             size: 1,
31686             ascii: true
31687         },
31688         // short, 16 bit int:
31689         3: {
31690             getValue: function (dataView, dataOffset, littleEndian) {
31691                 return dataView.getUint16(dataOffset, littleEndian);
31692             },
31693             size: 2
31694         },
31695         // long, 32 bit int:
31696         4: {
31697             getValue: function (dataView, dataOffset, littleEndian) {
31698                 return dataView.getUint32(dataOffset, littleEndian);
31699             },
31700             size: 4
31701         },
31702         // rational = two long values, first is numerator, second is denominator:
31703         5: {
31704             getValue: function (dataView, dataOffset, littleEndian) {
31705                 return dataView.getUint32(dataOffset, littleEndian) /
31706                     dataView.getUint32(dataOffset + 4, littleEndian);
31707             },
31708             size: 8
31709         },
31710         // slong, 32 bit signed int:
31711         9: {
31712             getValue: function (dataView, dataOffset, littleEndian) {
31713                 return dataView.getInt32(dataOffset, littleEndian);
31714             },
31715             size: 4
31716         },
31717         // srational, two slongs, first is numerator, second is denominator:
31718         10: {
31719             getValue: function (dataView, dataOffset, littleEndian) {
31720                 return dataView.getInt32(dataOffset, littleEndian) /
31721                     dataView.getInt32(dataOffset + 4, littleEndian);
31722             },
31723             size: 8
31724         }
31725     },
31726     
31727     footer : {
31728         STANDARD : [
31729             {
31730                 tag : 'div',
31731                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31732                 action : 'rotate-left',
31733                 cn : [
31734                     {
31735                         tag : 'button',
31736                         cls : 'btn btn-default',
31737                         html : '<i class="fa fa-undo"></i>'
31738                     }
31739                 ]
31740             },
31741             {
31742                 tag : 'div',
31743                 cls : 'btn-group roo-upload-cropbox-picture',
31744                 action : 'picture',
31745                 cn : [
31746                     {
31747                         tag : 'button',
31748                         cls : 'btn btn-default',
31749                         html : '<i class="fa fa-picture-o"></i>'
31750                     }
31751                 ]
31752             },
31753             {
31754                 tag : 'div',
31755                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31756                 action : 'rotate-right',
31757                 cn : [
31758                     {
31759                         tag : 'button',
31760                         cls : 'btn btn-default',
31761                         html : '<i class="fa fa-repeat"></i>'
31762                     }
31763                 ]
31764             }
31765         ],
31766         DOCUMENT : [
31767             {
31768                 tag : 'div',
31769                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31770                 action : 'rotate-left',
31771                 cn : [
31772                     {
31773                         tag : 'button',
31774                         cls : 'btn btn-default',
31775                         html : '<i class="fa fa-undo"></i>'
31776                     }
31777                 ]
31778             },
31779             {
31780                 tag : 'div',
31781                 cls : 'btn-group roo-upload-cropbox-download',
31782                 action : 'download',
31783                 cn : [
31784                     {
31785                         tag : 'button',
31786                         cls : 'btn btn-default',
31787                         html : '<i class="fa fa-download"></i>'
31788                     }
31789                 ]
31790             },
31791             {
31792                 tag : 'div',
31793                 cls : 'btn-group roo-upload-cropbox-crop',
31794                 action : 'crop',
31795                 cn : [
31796                     {
31797                         tag : 'button',
31798                         cls : 'btn btn-default',
31799                         html : '<i class="fa fa-crop"></i>'
31800                     }
31801                 ]
31802             },
31803             {
31804                 tag : 'div',
31805                 cls : 'btn-group roo-upload-cropbox-trash',
31806                 action : 'trash',
31807                 cn : [
31808                     {
31809                         tag : 'button',
31810                         cls : 'btn btn-default',
31811                         html : '<i class="fa fa-trash"></i>'
31812                     }
31813                 ]
31814             },
31815             {
31816                 tag : 'div',
31817                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31818                 action : 'rotate-right',
31819                 cn : [
31820                     {
31821                         tag : 'button',
31822                         cls : 'btn btn-default',
31823                         html : '<i class="fa fa-repeat"></i>'
31824                     }
31825                 ]
31826             }
31827         ],
31828         ROTATOR : [
31829             {
31830                 tag : 'div',
31831                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31832                 action : 'rotate-left',
31833                 cn : [
31834                     {
31835                         tag : 'button',
31836                         cls : 'btn btn-default',
31837                         html : '<i class="fa fa-undo"></i>'
31838                     }
31839                 ]
31840             },
31841             {
31842                 tag : 'div',
31843                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31844                 action : 'rotate-right',
31845                 cn : [
31846                     {
31847                         tag : 'button',
31848                         cls : 'btn btn-default',
31849                         html : '<i class="fa fa-repeat"></i>'
31850                     }
31851                 ]
31852             }
31853         ],
31854         CENTER : [
31855             {
31856                 tag : 'div',
31857                 cls : 'btn-group roo-upload-cropbox-center',
31858                 action : 'center',
31859                 cn : [
31860                     {
31861                         tag : 'button',
31862                         cls : 'btn btn-default',
31863                         html : 'CENTER'
31864                     }
31865                 ]
31866             }
31867         ]
31868     }
31869 });
31870         /*
31871  * Based on:
31872  * Ext JS Library 1.1.1
31873  * Copyright(c) 2006-2007, Ext JS, LLC.
31874  *
31875  * Originally Released Under LGPL - original licence link has changed is not relivant.
31876  *
31877  * Fork - LGPL
31878  * <script type="text/javascript">
31879  */
31880 /**
31881  * @class Roo.panel.Tab
31882  * @extends Roo.util.Observable
31883  * A lightweight tab container.
31884  * <br><br>
31885  * Usage:
31886  * <pre><code>
31887 // basic tabs 1, built from existing content
31888 var tabs = new Roo.panel.Tab("tabs1");
31889 tabs.addTab("script", "View Script");
31890 tabs.addTab("markup", "View Markup");
31891 tabs.activate("script");
31892
31893 // more advanced tabs, built from javascript
31894 var jtabs = new Roo.panel.Tab("jtabs");
31895 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
31896
31897 // set up the UpdateManager
31898 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
31899 var updater = tab2.getUpdateManager();
31900 updater.setDefaultUrl("ajax1.htm");
31901 tab2.on('activate', updater.refresh, updater, true);
31902
31903 // Use setUrl for Ajax loading
31904 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
31905 tab3.setUrl("ajax2.htm", null, true);
31906
31907 // Disabled tab
31908 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
31909 tab4.disable();
31910
31911 jtabs.activate("jtabs-1");
31912  * </code></pre>
31913  * @constructor
31914  * Create a new TabPanel.
31915  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
31916  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
31917  */
31918 Roo.panel.Tab = function(container, config){
31919     /**
31920     * The container element for this TabPanel.
31921     * @type Roo.Element
31922     */
31923     this.el = Roo.get(container, true);
31924     if(config){
31925         if(typeof config == "boolean"){
31926             this.tabPosition = config ? "bottom" : "top";
31927         }else{
31928             Roo.apply(this, config);
31929         }
31930     }
31931     if(this.tabPosition == "bottom"){
31932         this.bodyEl = Roo.get(this.createBody(this.el.dom));
31933         this.el.addClass("x-tabs-bottom");
31934     }
31935     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
31936     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
31937     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
31938     if(Roo.isIE){
31939         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
31940     }
31941     if(this.tabPosition != "bottom"){
31942         /** The body element that contains {@link Roo.panel.TabItem} bodies. +
31943          * @type Roo.Element
31944          */
31945         this.bodyEl = Roo.get(this.createBody(this.el.dom));
31946         this.el.addClass("x-tabs-top");
31947     }
31948     this.items = [];
31949
31950     this.bodyEl.setStyle("position", "relative");
31951
31952     this.active = null;
31953     this.activateDelegate = this.activate.createDelegate(this);
31954
31955     this.addEvents({
31956         /**
31957          * @event tabchange
31958          * Fires when the active tab changes
31959          * @param {Roo.panel.Tab} this
31960          * @param {Roo.panel.TabItem} activePanel The new active tab
31961          */
31962         "tabchange": true,
31963         /**
31964          * @event beforetabchange
31965          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
31966          * @param {Roo.panel.Tab} this
31967          * @param {Object} e Set cancel to true on this object to cancel the tab change
31968          * @param {Roo.panel.TabItem} tab The tab being changed to
31969          */
31970         "beforetabchange" : true
31971     });
31972
31973     Roo.EventManager.onWindowResize(this.onResize, this);
31974     this.cpad = this.el.getPadding("lr");
31975     this.hiddenCount = 0;
31976
31977
31978     // toolbar on the tabbar support...
31979     if (this.toolbar) {
31980         var tcfg = this.toolbar;
31981         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
31982         this.toolbar = new Roo.Toolbar(tcfg);
31983         if (Roo.isSafari) {
31984             var tbl = tcfg.container.child('table', true);
31985             tbl.setAttribute('width', '100%');
31986         }
31987         
31988     }
31989    
31990
31991
31992     Roo.panel.Tab.superclass.constructor.call(this);
31993 };
31994
31995 Roo.extend(Roo.panel.Tab, Roo.util.Observable, {
31996     /*
31997      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
31998      */
31999     tabPosition : "top",
32000     /*
32001      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
32002      */
32003     currentTabWidth : 0,
32004     /*
32005      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
32006      */
32007     minTabWidth : 40,
32008     /*
32009      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
32010      */
32011     maxTabWidth : 250,
32012     /*
32013      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
32014      */
32015     preferredTabWidth : 175,
32016     /*
32017      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
32018      */
32019     resizeTabs : false,
32020     /*
32021      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
32022      */
32023     monitorResize : true,
32024     /*
32025      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
32026      */
32027     toolbar : false,
32028
32029     /**
32030      * 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.
32031      * @param {String} id The id of the div to use <b>or create</b>
32032      * @param {String} text The text for the tab
32033      * @param {String} content (optional) Content to put in the TabPanelItem body
32034      * @param {Boolean} closable (optional) True to create a close icon on the tab
32035      * @return {Roo.panel.TabItem} The created TabPanelItem
32036      */
32037     addTab : function(id, text, content, closable){
32038         var item = new Roo.panel.TabItem(this, id, text, closable);
32039         this.addTabItem(item);
32040         if(content){
32041             item.setContent(content);
32042         }
32043         return item;
32044     },
32045
32046     /**
32047      * Returns the {@link Roo.panel.TabItem} with the specified id/index
32048      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
32049      * @return {Roo.panel.TabItem}
32050      */
32051     getTab : function(id){
32052         return this.items[id];
32053     },
32054
32055     /**
32056      * Hides the {@link Roo.panel.TabItem} with the specified id/index
32057      * @param {String/Number} id The id or index of the TabPanelItem to hide.
32058      */
32059     hideTab : function(id){
32060         var t = this.items[id];
32061         if(!t.isHidden()){
32062            t.setHidden(true);
32063            this.hiddenCount++;
32064            this.autoSizeTabs();
32065         }
32066     },
32067
32068     /**
32069      * "Unhides" the {@link Roo.panel.TabItem} with the specified id/index.
32070      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
32071      */
32072     unhideTab : function(id){
32073         var t = this.items[id];
32074         if(t.isHidden()){
32075            t.setHidden(false);
32076            this.hiddenCount--;
32077            this.autoSizeTabs();
32078         }
32079     },
32080
32081     /**
32082      * Adds an existing {@link Roo.panel.TabItem}.
32083      * @param {Roo.panel.TabItem} item The TabPanelItem to add
32084      */
32085     addTabItem : function(item){
32086         this.items[item.id] = item;
32087         this.items.push(item);
32088         if(this.resizeTabs){
32089            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
32090            this.autoSizeTabs();
32091         }else{
32092             item.autoSize();
32093         }
32094     },
32095
32096     /**
32097      * Removes a {@link Roo.panel.TabItem}.
32098      * @param {String/Number} id The id or index of the TabPanelItem to remove.
32099      */
32100     removeTab : function(id){
32101         var items = this.items;
32102         var tab = items[id];
32103         if(!tab) { return; }
32104         var index = items.indexOf(tab);
32105         if(this.active == tab && items.length > 1){
32106             var newTab = this.getNextAvailable(index);
32107             if(newTab) {
32108                 newTab.activate();
32109             }
32110         }
32111         this.stripEl.dom.removeChild(tab.pnode.dom);
32112         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
32113             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
32114         }
32115         items.splice(index, 1);
32116         delete this.items[tab.id];
32117         tab.fireEvent("close", tab);
32118         tab.purgeListeners();
32119         this.autoSizeTabs();
32120     },
32121
32122     getNextAvailable : function(start){
32123         var items = this.items;
32124         var index = start;
32125         // look for a next tab that will slide over to
32126         // replace the one being removed
32127         while(index < items.length){
32128             var item = items[++index];
32129             if(item && !item.isHidden()){
32130                 return item;
32131             }
32132         }
32133         // if one isn't found select the previous tab (on the left)
32134         index = start;
32135         while(index >= 0){
32136             var item = items[--index];
32137             if(item && !item.isHidden()){
32138                 return item;
32139             }
32140         }
32141         return null;
32142     },
32143
32144     /**
32145      * Disables a {@link Roo.panel.TabItem}. It cannot be the active tab, if it is this call is ignored.
32146      * @param {String/Number} id The id or index of the TabPanelItem to disable.
32147      */
32148     disableTab : function(id){
32149         var tab = this.items[id];
32150         if(tab && this.active != tab){
32151             tab.disable();
32152         }
32153     },
32154
32155     /**
32156      * Enables a {@link Roo.panel.TabItem} that is disabled.
32157      * @param {String/Number} id The id or index of the TabPanelItem to enable.
32158      */
32159     enableTab : function(id){
32160         var tab = this.items[id];
32161         tab.enable();
32162     },
32163
32164     /**
32165      * Activates a {@link Roo.panel.TabItem}. The currently active one will be deactivated.
32166      * @param {String/Number} id The id or index of the TabPanelItem to activate.
32167      * @return {Roo.panel.TabItem} The TabPanelItem.
32168      */
32169     activate : function(id){
32170         var tab = this.items[id];
32171         if(!tab){
32172             return null;
32173         }
32174         if(tab == this.active || tab.disabled){
32175             return tab;
32176         }
32177         var e = {};
32178         this.fireEvent("beforetabchange", this, e, tab);
32179         if(e.cancel !== true && !tab.disabled){
32180             if(this.active){
32181                 this.active.hide();
32182             }
32183             this.active = this.items[id];
32184             this.active.show();
32185             this.fireEvent("tabchange", this, this.active);
32186         }
32187         return tab;
32188     },
32189
32190     /**
32191      * Gets the active {@link Roo.panel.TabItem}.
32192      * @return {Roo.panel.TabItem} The active TabPanelItem or null if none are active.
32193      */
32194     getActiveTab : function(){
32195         return this.active;
32196     },
32197
32198     /**
32199      * Updates the tab body element to fit the height of the container element
32200      * for overflow scrolling
32201      * @param {Number} targetHeight (optional) Override the starting height from the elements height
32202      */
32203     syncHeight : function(targetHeight){
32204         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32205         var bm = this.bodyEl.getMargins();
32206         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
32207         this.bodyEl.setHeight(newHeight);
32208         return newHeight;
32209     },
32210
32211     onResize : function(){
32212         if(this.monitorResize){
32213             this.autoSizeTabs();
32214         }
32215     },
32216
32217     /**
32218      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
32219      */
32220     beginUpdate : function(){
32221         this.updating = true;
32222     },
32223
32224     /**
32225      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
32226      */
32227     endUpdate : function(){
32228         this.updating = false;
32229         this.autoSizeTabs();
32230     },
32231
32232     /**
32233      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
32234      */
32235     autoSizeTabs : function(){
32236         var count = this.items.length;
32237         var vcount = count - this.hiddenCount;
32238         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
32239             return;
32240         }
32241         var w = Math.max(this.el.getWidth() - this.cpad, 10);
32242         var availWidth = Math.floor(w / vcount);
32243         var b = this.stripBody;
32244         if(b.getWidth() > w){
32245             var tabs = this.items;
32246             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
32247             if(availWidth < this.minTabWidth){
32248                 /*if(!this.sleft){    // incomplete scrolling code
32249                     this.createScrollButtons();
32250                 }
32251                 this.showScroll();
32252                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
32253             }
32254         }else{
32255             if(this.currentTabWidth < this.preferredTabWidth){
32256                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
32257             }
32258         }
32259     },
32260
32261     /**
32262      * Returns the number of tabs in this TabPanel.
32263      * @return {Number}
32264      */
32265      getCount : function(){
32266          return this.items.length;
32267      },
32268
32269     /**
32270      * Resizes all the tabs to the passed width
32271      * @param {Number} The new width
32272      */
32273     setTabWidth : function(width){
32274         this.currentTabWidth = width;
32275         for(var i = 0, len = this.items.length; i < len; i++) {
32276                 if(!this.items[i].isHidden()) {
32277                 this.items[i].setWidth(width);
32278             }
32279         }
32280     },
32281
32282     /**
32283      * Destroys this TabPanel
32284      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
32285      */
32286     destroy : function(removeEl){
32287         Roo.EventManager.removeResizeListener(this.onResize, this);
32288         for(var i = 0, len = this.items.length; i < len; i++){
32289             this.items[i].purgeListeners();
32290         }
32291         if(removeEl === true){
32292             this.el.update("");
32293             this.el.remove();
32294         }
32295     }
32296 });
32297
32298
32299 /** @private */
32300 Roo.panel.Tab.prototype.createStripList = function(strip){
32301     // div wrapper for retard IE
32302     // returns the "tr" element.
32303     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
32304         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
32305         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
32306     return strip.firstChild.firstChild.firstChild.firstChild;
32307 };
32308 /** @private */
32309 Roo.panel.Tab.prototype.createBody = function(container){
32310     var body = document.createElement("div");
32311     Roo.id(body, "tab-body");
32312     Roo.fly(body).addClass("x-tabs-body");
32313     container.appendChild(body);
32314     return body;
32315 };
32316 /** @private */
32317 Roo.panel.Tab.prototype.createItemBody = function(bodyEl, id){
32318     var body = Roo.getDom(id);
32319     if(!body){
32320         body = document.createElement("div");
32321         body.id = id;
32322     }
32323     Roo.fly(body).addClass("x-tabs-item-body");
32324     bodyEl.insertBefore(body, bodyEl.firstChild);
32325     return body;
32326 };
32327 /** @private */
32328 Roo.panel.Tab.prototype.createStripElements = function(stripEl, text, closable){
32329     var td = document.createElement("td");
32330     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
32331     //stripEl.appendChild(td);
32332     if(closable){
32333         td.className = "x-tabs-closable";
32334         if(!this.closeTpl){
32335             this.closeTpl = new Roo.Template(
32336                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
32337                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
32338                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
32339             );
32340         }
32341         var el = this.closeTpl.overwrite(td, {"text": text});
32342         var close = el.getElementsByTagName("div")[0];
32343         var inner = el.getElementsByTagName("em")[0];
32344         return {"el": el, "close": close, "inner": inner};
32345     } else {
32346         if(!this.tabTpl){
32347             this.tabTpl = new Roo.Template(
32348                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
32349                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
32350             );
32351         }
32352         var el = this.tabTpl.overwrite(td, {"text": text});
32353         var inner = el.getElementsByTagName("em")[0];
32354         return {"el": el, "inner": inner};
32355     }
32356 };/**
32357  * @class Roo.panel.TabItem
32358  * @extends Roo.util.Observable
32359  * Represents an individual item (tab plus body) in a TabPanel.
32360  * @param {Roo.panel.Tab} tabPanel The {@link Roo.panel.Tab} this TabPanelItem belongs to
32361  * @param {String} id The id of this TabPanelItem
32362  * @param {String} text The text for the tab of this TabPanelItem
32363  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
32364  */
32365  Roo.panel.TabItem = function(tabPanel, id, text, closable){
32366     /**
32367      * The {@link Roo.panel.Tab} this TabPanelItem belongs to
32368      * @type Roo.panel.Tab
32369      */
32370     this.tabPanel = tabPanel;
32371     /**
32372      * The id for this TabPanelItem
32373      * @type String
32374      */
32375     this.id = id;
32376     /** @private */
32377     this.disabled = false;
32378     /** @private */
32379     this.text = text;
32380     /** @private */
32381     this.loaded = false;
32382     this.closable = closable;
32383
32384     /**
32385      * The body element for this TabPanelItem.
32386      * @type Roo.Element
32387      */
32388     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
32389     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
32390     this.bodyEl.setStyle("display", "block");
32391     this.bodyEl.setStyle("zoom", "1");
32392     this.hideAction();
32393
32394     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
32395     /** @private */
32396     this.el = Roo.get(els.el, true);
32397     this.inner = Roo.get(els.inner, true);
32398     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
32399     this.pnode = Roo.get(els.el.parentNode, true);
32400     this.el.on("mousedown", this.onTabMouseDown, this);
32401     this.el.on("click", this.onTabClick, this);
32402     /** @private */
32403     if(closable){
32404         var c = Roo.get(els.close, true);
32405         c.dom.title = this.closeText;
32406         c.addClassOnOver("close-over");
32407         c.on("click", this.closeClick, this);
32408      }
32409
32410     this.addEvents({
32411          /**
32412          * @event activate
32413          * Fires when this tab becomes the active tab.
32414          * @param {Roo.panel.Tab} tabPanel The parent TabPanel
32415          * @param {Roo.panel.TabItem} this
32416          */
32417         "activate": true,
32418         /**
32419          * @event beforeclose
32420          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
32421          * @param {Roo.panel.TabItem} this
32422          * @param {Object} e Set cancel to true on this object to cancel the close.
32423          */
32424         "beforeclose": true,
32425         /**
32426          * @event close
32427          * Fires when this tab is closed.
32428          * @param {Roo.panel.TabItem} this
32429          */
32430          "close": true,
32431         /**
32432          * @event deactivate
32433          * Fires when this tab is no longer the active tab.
32434          * @param {Roo.panel.Tab} tabPanel The parent TabPanel
32435          * @param {Roo.panel.TabItem} this
32436          */
32437          "deactivate" : true
32438     });
32439     this.hidden = false;
32440
32441     Roo.panel.TabItem.superclass.constructor.call(this);
32442 };
32443
32444 Roo.extend(Roo.panel.TabItem, Roo.util.Observable, {
32445     purgeListeners : function(){
32446        Roo.util.Observable.prototype.purgeListeners.call(this);
32447        this.el.removeAllListeners();
32448     },
32449     /**
32450      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
32451      */
32452     show : function(){
32453         this.pnode.addClass("on");
32454         this.showAction();
32455         if(Roo.isOpera){
32456             this.tabPanel.stripWrap.repaint();
32457         }
32458         this.fireEvent("activate", this.tabPanel, this);
32459     },
32460
32461     /**
32462      * Returns true if this tab is the active tab.
32463      * @return {Boolean}
32464      */
32465     isActive : function(){
32466         return this.tabPanel.getActiveTab() == this;
32467     },
32468
32469     /**
32470      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
32471      */
32472     hide : function(){
32473         this.pnode.removeClass("on");
32474         this.hideAction();
32475         this.fireEvent("deactivate", this.tabPanel, this);
32476     },
32477
32478     hideAction : function(){
32479         this.bodyEl.hide();
32480         this.bodyEl.setStyle("position", "absolute");
32481         this.bodyEl.setLeft("-20000px");
32482         this.bodyEl.setTop("-20000px");
32483     },
32484
32485     showAction : function(){
32486         this.bodyEl.setStyle("position", "relative");
32487         this.bodyEl.setTop("");
32488         this.bodyEl.setLeft("");
32489         this.bodyEl.show();
32490     },
32491
32492     /**
32493      * Set the tooltip for the tab.
32494      * @param {String} tooltip The tab's tooltip
32495      */
32496     setTooltip : function(text){
32497         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
32498             this.textEl.dom.qtip = text;
32499             this.textEl.dom.removeAttribute('title');
32500         }else{
32501             this.textEl.dom.title = text;
32502         }
32503     },
32504
32505     onTabClick : function(e){
32506         e.preventDefault();
32507         this.tabPanel.activate(this.id);
32508     },
32509
32510     onTabMouseDown : function(e){
32511         e.preventDefault();
32512         this.tabPanel.activate(this.id);
32513     },
32514
32515     getWidth : function(){
32516         return this.inner.getWidth();
32517     },
32518
32519     setWidth : function(width){
32520         var iwidth = width - this.pnode.getPadding("lr");
32521         this.inner.setWidth(iwidth);
32522         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
32523         this.pnode.setWidth(width);
32524     },
32525
32526     /**
32527      * Show or hide the tab
32528      * @param {Boolean} hidden True to hide or false to show.
32529      */
32530     setHidden : function(hidden){
32531         this.hidden = hidden;
32532         this.pnode.setStyle("display", hidden ? "none" : "");
32533     },
32534
32535     /**
32536      * Returns true if this tab is "hidden"
32537      * @return {Boolean}
32538      */
32539     isHidden : function(){
32540         return this.hidden;
32541     },
32542
32543     /**
32544      * Returns the text for this tab
32545      * @return {String}
32546      */
32547     getText : function(){
32548         return this.text;
32549     },
32550
32551     autoSize : function(){
32552         //this.el.beginMeasure();
32553         this.textEl.setWidth(1);
32554         /*
32555          *  #2804 [new] Tabs in Roojs
32556          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
32557          */
32558         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
32559         //this.el.endMeasure();
32560     },
32561
32562     /**
32563      * Sets the text for the tab (Note: this also sets the tooltip text)
32564      * @param {String} text The tab's text and tooltip
32565      */
32566     setText : function(text){
32567         this.text = text;
32568         this.textEl.update(text);
32569         this.setTooltip(text);
32570         if(!this.tabPanel.resizeTabs){
32571             this.autoSize();
32572         }
32573     },
32574     /**
32575      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
32576      */
32577     activate : function(){
32578         this.tabPanel.activate(this.id);
32579     },
32580
32581     /**
32582      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
32583      */
32584     disable : function(){
32585         if(this.tabPanel.active != this){
32586             this.disabled = true;
32587             this.pnode.addClass("disabled");
32588         }
32589     },
32590
32591     /**
32592      * Enables this TabPanelItem if it was previously disabled.
32593      */
32594     enable : function(){
32595         this.disabled = false;
32596         this.pnode.removeClass("disabled");
32597     },
32598
32599     /**
32600      * Sets the content for this TabPanelItem.
32601      * @param {String} content The content
32602      * @param {Boolean} loadScripts true to look for and load scripts
32603      */
32604     setContent : function(content, loadScripts){
32605         this.bodyEl.update(content, loadScripts);
32606     },
32607
32608     /**
32609      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
32610      * @return {Roo.UpdateManager} The UpdateManager
32611      */
32612     getUpdateManager : function(){
32613         return this.bodyEl.getUpdateManager();
32614     },
32615
32616     /**
32617      * Set a URL to be used to load the content for this TabPanelItem.
32618      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
32619      * @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)
32620      * @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)
32621      * @return {Roo.UpdateManager} The UpdateManager
32622      */
32623     setUrl : function(url, params, loadOnce){
32624         if(this.refreshDelegate){
32625             this.un('activate', this.refreshDelegate);
32626         }
32627         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
32628         this.on("activate", this.refreshDelegate);
32629         return this.bodyEl.getUpdateManager();
32630     },
32631
32632     /** @private */
32633     _handleRefresh : function(url, params, loadOnce){
32634         if(!loadOnce || !this.loaded){
32635             var updater = this.bodyEl.getUpdateManager();
32636             updater.update(url, params, this._setLoaded.createDelegate(this));
32637         }
32638     },
32639
32640     /**
32641      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
32642      *   Will fail silently if the setUrl method has not been called.
32643      *   This does not activate the panel, just updates its content.
32644      */
32645     refresh : function(){
32646         if(this.refreshDelegate){
32647            this.loaded = false;
32648            this.refreshDelegate();
32649         }
32650     },
32651
32652     /** @private */
32653     _setLoaded : function(){
32654         this.loaded = true;
32655     },
32656
32657     /** @private */
32658     closeClick : function(e){
32659         var o = {};
32660         e.stopEvent();
32661         this.fireEvent("beforeclose", this, o);
32662         if(o.cancel !== true){
32663             this.tabPanel.removeTab(this.id);
32664         }
32665     },
32666     /**
32667      * The text displayed in the tooltip for the close icon.
32668      * @type String
32669      */
32670     closeText : "Close this tab"
32671 });
32672
32673 /** @private */
32674 Roo.panel.Tab.prototype.createStrip = function(container){
32675     var strip = document.createElement("div");
32676     strip.className = "x-tabs-wrap";
32677     container.appendChild(strip);
32678     return strip;
32679 };/*
32680  * Based on:
32681  * Ext JS Library 1.1.1
32682  * Copyright(c) 2006-2007, Ext JS, LLC.
32683  *
32684  * Originally Released Under LGPL - original licence link has changed is not relivant.
32685  *
32686  * Fork - LGPL
32687  * <script type="text/javascript">
32688  */
32689
32690 /**
32691  * @class Roo.Button
32692  * @extends Roo.util.Observable
32693  * Simple Button class
32694  * @cfg {String} text The button text
32695  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
32696  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
32697  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
32698  * @cfg {Object} scope The scope of the handler
32699  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
32700  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
32701  * @cfg {Boolean} hidden True to start hidden (defaults to false)
32702  * @cfg {Boolean} disabled True to start disabled (defaults to false)
32703  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
32704  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
32705    applies if enableToggle = true)
32706  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
32707  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
32708   an {@link Roo.util.ClickRepeater} config object (defaults to false).
32709  * @constructor
32710  * Create a new button
32711  * @param {Object} config The config object
32712  */
32713 Roo.Button = function(renderTo, config)
32714 {
32715     if (!config) {
32716         config = renderTo;
32717         renderTo = config.renderTo || false;
32718     }
32719     
32720     Roo.apply(this, config);
32721     this.addEvents({
32722         /**
32723              * @event click
32724              * Fires when this button is clicked
32725              * @param {Button} this
32726              * @param {EventObject} e The click event
32727              */
32728             "click" : true,
32729         /**
32730              * @event toggle
32731              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
32732              * @param {Button} this
32733              * @param {Boolean} pressed
32734              */
32735             "toggle" : true,
32736         /**
32737              * @event mouseover
32738              * Fires when the mouse hovers over the button
32739              * @param {Button} this
32740              * @param {Event} e The event object
32741              */
32742         'mouseover' : true,
32743         /**
32744              * @event mouseout
32745              * Fires when the mouse exits the button
32746              * @param {Button} this
32747              * @param {Event} e The event object
32748              */
32749         'mouseout': true,
32750          /**
32751              * @event render
32752              * Fires when the button is rendered
32753              * @param {Button} this
32754              */
32755         'render': true
32756     });
32757     if(this.menu){
32758         this.menu = Roo.menu.MenuMgr.get(this.menu);
32759     }
32760     // register listeners first!!  - so render can be captured..
32761     Roo.util.Observable.call(this);
32762     if(renderTo){
32763         this.render(renderTo);
32764     }
32765     
32766   
32767 };
32768
32769 Roo.extend(Roo.Button, Roo.util.Observable, {
32770     /**
32771      * 
32772      */
32773     
32774     /**
32775      * Read-only. True if this button is hidden
32776      * @type Boolean
32777      */
32778     hidden : false,
32779     /**
32780      * Read-only. True if this button is disabled
32781      * @type Boolean
32782      */
32783     disabled : false,
32784     /**
32785      * Read-only. True if this button is pressed (only if enableToggle = true)
32786      * @type Boolean
32787      */
32788     pressed : false,
32789
32790     /**
32791      * @cfg {Number} tabIndex 
32792      * The DOM tabIndex for this button (defaults to undefined)
32793      */
32794     tabIndex : undefined,
32795
32796     /**
32797      * @cfg {Boolean} enableToggle
32798      * True to enable pressed/not pressed toggling (defaults to false)
32799      */
32800     enableToggle: false,
32801     /**
32802      * @cfg {Roo.menu.Menu} menu
32803      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
32804      */
32805     menu : undefined,
32806     /**
32807      * @cfg {String} menuAlign
32808      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
32809      */
32810     menuAlign : "tl-bl?",
32811
32812     /**
32813      * @cfg {String} iconCls
32814      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
32815      */
32816     iconCls : undefined,
32817     /**
32818      * @cfg {String} type
32819      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
32820      */
32821     type : 'button',
32822
32823     // private
32824     menuClassTarget: 'tr',
32825
32826     /**
32827      * @cfg {String} clickEvent
32828      * The type of event to map to the button's event handler (defaults to 'click')
32829      */
32830     clickEvent : 'click',
32831
32832     /**
32833      * @cfg {Boolean} handleMouseEvents
32834      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
32835      */
32836     handleMouseEvents : true,
32837
32838     /**
32839      * @cfg {String} tooltipType
32840      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
32841      */
32842     tooltipType : 'qtip',
32843
32844     /**
32845      * @cfg {String} cls
32846      * A CSS class to apply to the button's main element.
32847      */
32848     
32849     /**
32850      * @cfg {Roo.Template} template (Optional)
32851      * An {@link Roo.Template} with which to create the Button's main element. This Template must
32852      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
32853      * require code modifications if required elements (e.g. a button) aren't present.
32854      */
32855
32856     // private
32857     render : function(renderTo){
32858         var btn;
32859         if(this.hideParent){
32860             this.parentEl = Roo.get(renderTo);
32861         }
32862         if(!this.dhconfig){
32863             if(!this.template){
32864                 if(!Roo.Button.buttonTemplate){
32865                     // hideous table template
32866                     Roo.Button.buttonTemplate = new Roo.Template(
32867                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
32868                         '<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>',
32869                         "</tr></tbody></table>");
32870                 }
32871                 this.template = Roo.Button.buttonTemplate;
32872             }
32873             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
32874             var btnEl = btn.child("button:first");
32875             btnEl.on('focus', this.onFocus, this);
32876             btnEl.on('blur', this.onBlur, this);
32877             if(this.cls){
32878                 btn.addClass(this.cls);
32879             }
32880             if(this.icon){
32881                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
32882             }
32883             if(this.iconCls){
32884                 btnEl.addClass(this.iconCls);
32885                 if(!this.cls){
32886                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
32887                 }
32888             }
32889             if(this.tabIndex !== undefined){
32890                 btnEl.dom.tabIndex = this.tabIndex;
32891             }
32892             if(this.tooltip){
32893                 if(typeof this.tooltip == 'object'){
32894                     Roo.QuickTips.tips(Roo.apply({
32895                           target: btnEl.id
32896                     }, this.tooltip));
32897                 } else {
32898                     btnEl.dom[this.tooltipType] = this.tooltip;
32899                 }
32900             }
32901         }else{
32902             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
32903         }
32904         this.el = btn;
32905         if(this.id){
32906             this.el.dom.id = this.el.id = this.id;
32907         }
32908         if(this.menu){
32909             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
32910             this.menu.on("show", this.onMenuShow, this);
32911             this.menu.on("hide", this.onMenuHide, this);
32912         }
32913         btn.addClass("x-btn");
32914         if(Roo.isIE && !Roo.isIE7){
32915             this.autoWidth.defer(1, this);
32916         }else{
32917             this.autoWidth();
32918         }
32919         if(this.handleMouseEvents){
32920             btn.on("mouseover", this.onMouseOver, this);
32921             btn.on("mouseout", this.onMouseOut, this);
32922             btn.on("mousedown", this.onMouseDown, this);
32923         }
32924         btn.on(this.clickEvent, this.onClick, this);
32925         //btn.on("mouseup", this.onMouseUp, this);
32926         if(this.hidden){
32927             this.hide();
32928         }
32929         if(this.disabled){
32930             this.disable();
32931         }
32932         Roo.ButtonToggleMgr.register(this);
32933         if(this.pressed){
32934             this.el.addClass("x-btn-pressed");
32935         }
32936         if(this.repeat){
32937             var repeater = new Roo.util.ClickRepeater(btn,
32938                 typeof this.repeat == "object" ? this.repeat : {}
32939             );
32940             repeater.on("click", this.onClick,  this);
32941         }
32942         
32943         this.fireEvent('render', this);
32944         
32945     },
32946     /**
32947      * Returns the button's underlying element
32948      * @return {Roo.Element} The element
32949      */
32950     getEl : function(){
32951         return this.el;  
32952     },
32953     
32954     /**
32955      * Destroys this Button and removes any listeners.
32956      */
32957     destroy : function(){
32958         Roo.ButtonToggleMgr.unregister(this);
32959         this.el.removeAllListeners();
32960         this.purgeListeners();
32961         this.el.remove();
32962     },
32963
32964     // private
32965     autoWidth : function(){
32966         if(this.el){
32967             this.el.setWidth("auto");
32968             if(Roo.isIE7 && Roo.isStrict){
32969                 var ib = this.el.child('button');
32970                 if(ib && ib.getWidth() > 20){
32971                     ib.clip();
32972                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
32973                 }
32974             }
32975             if(this.minWidth){
32976                 if(this.hidden){
32977                     this.el.beginMeasure();
32978                 }
32979                 if(this.el.getWidth() < this.minWidth){
32980                     this.el.setWidth(this.minWidth);
32981                 }
32982                 if(this.hidden){
32983                     this.el.endMeasure();
32984                 }
32985             }
32986         }
32987     },
32988
32989     /**
32990      * Assigns this button's click handler
32991      * @param {Function} handler The function to call when the button is clicked
32992      * @param {Object} scope (optional) Scope for the function passed in
32993      */
32994     setHandler : function(handler, scope){
32995         this.handler = handler;
32996         this.scope = scope;  
32997     },
32998     
32999     /**
33000      * Sets this button's text
33001      * @param {String} text The button text
33002      */
33003     setText : function(text){
33004         this.text = text;
33005         if(this.el){
33006             this.el.child("td.x-btn-center button.x-btn-text").update(text);
33007         }
33008         this.autoWidth();
33009     },
33010     
33011     /**
33012      * Gets the text for this button
33013      * @return {String} The button text
33014      */
33015     getText : function(){
33016         return this.text;  
33017     },
33018     
33019     /**
33020      * Show this button
33021      */
33022     show: function(){
33023         this.hidden = false;
33024         if(this.el){
33025             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
33026         }
33027     },
33028     
33029     /**
33030      * Hide this button
33031      */
33032     hide: function(){
33033         this.hidden = true;
33034         if(this.el){
33035             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
33036         }
33037     },
33038     
33039     /**
33040      * Convenience function for boolean show/hide
33041      * @param {Boolean} visible True to show, false to hide
33042      */
33043     setVisible: function(visible){
33044         if(visible) {
33045             this.show();
33046         }else{
33047             this.hide();
33048         }
33049     },
33050     /**
33051          * Similar to toggle, but does not trigger event.
33052          * @param {Boolean} state [required] Force a particular state
33053          */
33054         setPressed : function(state)
33055         {
33056             if(state != this.pressed){
33057             if(state){
33058                 this.el.addClass("x-btn-pressed");
33059                 this.pressed = true;
33060             }else{
33061                 this.el.removeClass("x-btn-pressed");
33062                 this.pressed = false;
33063             }
33064         }
33065         },
33066         
33067     /**
33068      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
33069      * @param {Boolean} state (optional) Force a particular state
33070      */
33071     toggle : function(state){
33072         state = state === undefined ? !this.pressed : state;
33073         if(state != this.pressed){
33074             if(state){
33075                 this.el.addClass("x-btn-pressed");
33076                 this.pressed = true;
33077                 this.fireEvent("toggle", this, true);
33078             }else{
33079                 this.el.removeClass("x-btn-pressed");
33080                 this.pressed = false;
33081                 this.fireEvent("toggle", this, false);
33082             }
33083             if(this.toggleHandler){
33084                 this.toggleHandler.call(this.scope || this, this, state);
33085             }
33086         }
33087     },
33088     
33089         
33090         
33091     /**
33092      * Focus the button
33093      */
33094     focus : function(){
33095         this.el.child('button:first').focus();
33096     },
33097     
33098     /**
33099      * Disable this button
33100      */
33101     disable : function(){
33102         if(this.el){
33103             this.el.addClass("x-btn-disabled");
33104         }
33105         this.disabled = true;
33106     },
33107     
33108     /**
33109      * Enable this button
33110      */
33111     enable : function(){
33112         if(this.el){
33113             this.el.removeClass("x-btn-disabled");
33114         }
33115         this.disabled = false;
33116     },
33117
33118     /**
33119      * Convenience function for boolean enable/disable
33120      * @param {Boolean} enabled True to enable, false to disable
33121      */
33122     setDisabled : function(v){
33123         this[v !== true ? "enable" : "disable"]();
33124     },
33125
33126     // private
33127     onClick : function(e)
33128     {
33129         if(e){
33130             e.preventDefault();
33131         }
33132         if(e.button != 0){
33133             return;
33134         }
33135         if(!this.disabled){
33136             if(this.enableToggle){
33137                 this.toggle();
33138             }
33139             if(this.menu && !this.menu.isVisible()){
33140                 this.menu.show(this.el, this.menuAlign);
33141             }
33142             this.fireEvent("click", this, e);
33143             if(this.handler){
33144                 this.el.removeClass("x-btn-over");
33145                 this.handler.call(this.scope || this, this, e);
33146             }
33147         }
33148     },
33149     // private
33150     onMouseOver : function(e){
33151         if(!this.disabled){
33152             this.el.addClass("x-btn-over");
33153             this.fireEvent('mouseover', this, e);
33154         }
33155     },
33156     // private
33157     onMouseOut : function(e){
33158         if(!e.within(this.el,  true)){
33159             this.el.removeClass("x-btn-over");
33160             this.fireEvent('mouseout', this, e);
33161         }
33162     },
33163     // private
33164     onFocus : function(e){
33165         if(!this.disabled){
33166             this.el.addClass("x-btn-focus");
33167         }
33168     },
33169     // private
33170     onBlur : function(e){
33171         this.el.removeClass("x-btn-focus");
33172     },
33173     // private
33174     onMouseDown : function(e){
33175         if(!this.disabled && e.button == 0){
33176             this.el.addClass("x-btn-click");
33177             Roo.get(document).on('mouseup', this.onMouseUp, this);
33178         }
33179     },
33180     // private
33181     onMouseUp : function(e){
33182         if(e.button == 0){
33183             this.el.removeClass("x-btn-click");
33184             Roo.get(document).un('mouseup', this.onMouseUp, this);
33185         }
33186     },
33187     // private
33188     onMenuShow : function(e){
33189         this.el.addClass("x-btn-menu-active");
33190     },
33191     // private
33192     onMenuHide : function(e){
33193         this.el.removeClass("x-btn-menu-active");
33194     }   
33195 });
33196
33197 // Private utility class used by Button
33198 Roo.ButtonToggleMgr = function(){
33199    var groups = {};
33200    
33201    function toggleGroup(btn, state){
33202        if(state){
33203            var g = groups[btn.toggleGroup];
33204            for(var i = 0, l = g.length; i < l; i++){
33205                if(g[i] != btn){
33206                    g[i].toggle(false);
33207                }
33208            }
33209        }
33210    }
33211    
33212    return {
33213        register : function(btn){
33214            if(!btn.toggleGroup){
33215                return;
33216            }
33217            var g = groups[btn.toggleGroup];
33218            if(!g){
33219                g = groups[btn.toggleGroup] = [];
33220            }
33221            g.push(btn);
33222            btn.on("toggle", toggleGroup);
33223        },
33224        
33225        unregister : function(btn){
33226            if(!btn.toggleGroup){
33227                return;
33228            }
33229            var g = groups[btn.toggleGroup];
33230            if(g){
33231                g.remove(btn);
33232                btn.un("toggle", toggleGroup);
33233            }
33234        }
33235    };
33236 }();/*
33237  * Based on:
33238  * Ext JS Library 1.1.1
33239  * Copyright(c) 2006-2007, Ext JS, LLC.
33240  *
33241  * Originally Released Under LGPL - original licence link has changed is not relivant.
33242  *
33243  * Fork - LGPL
33244  * <script type="text/javascript">
33245  */
33246  
33247 /**
33248  * @class Roo.SplitButton
33249  * @extends Roo.Button
33250  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
33251  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
33252  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
33253  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
33254  * @cfg {String} arrowTooltip The title attribute of the arrow
33255  * @constructor
33256  * Create a new menu button
33257  * @param {String/HTMLElement/Element} renderTo The element to append the button to
33258  * @param {Object} config The config object
33259  */
33260 Roo.SplitButton = function(renderTo, config){
33261     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
33262     /**
33263      * @event arrowclick
33264      * Fires when this button's arrow is clicked
33265      * @param {SplitButton} this
33266      * @param {EventObject} e The click event
33267      */
33268     this.addEvents({"arrowclick":true});
33269 };
33270
33271 Roo.extend(Roo.SplitButton, Roo.Button, {
33272     render : function(renderTo){
33273         // this is one sweet looking template!
33274         var tpl = new Roo.Template(
33275             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
33276             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
33277             '<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>',
33278             "</tbody></table></td><td>",
33279             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
33280             '<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>',
33281             "</tbody></table></td></tr></table>"
33282         );
33283         var btn = tpl.append(renderTo, [this.text, this.type], true);
33284         var btnEl = btn.child("button");
33285         if(this.cls){
33286             btn.addClass(this.cls);
33287         }
33288         if(this.icon){
33289             btnEl.setStyle('background-image', 'url(' +this.icon +')');
33290         }
33291         if(this.iconCls){
33292             btnEl.addClass(this.iconCls);
33293             if(!this.cls){
33294                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
33295             }
33296         }
33297         this.el = btn;
33298         if(this.handleMouseEvents){
33299             btn.on("mouseover", this.onMouseOver, this);
33300             btn.on("mouseout", this.onMouseOut, this);
33301             btn.on("mousedown", this.onMouseDown, this);
33302             btn.on("mouseup", this.onMouseUp, this);
33303         }
33304         btn.on(this.clickEvent, this.onClick, this);
33305         if(this.tooltip){
33306             if(typeof this.tooltip == 'object'){
33307                 Roo.QuickTips.tips(Roo.apply({
33308                       target: btnEl.id
33309                 }, this.tooltip));
33310             } else {
33311                 btnEl.dom[this.tooltipType] = this.tooltip;
33312             }
33313         }
33314         if(this.arrowTooltip){
33315             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
33316         }
33317         if(this.hidden){
33318             this.hide();
33319         }
33320         if(this.disabled){
33321             this.disable();
33322         }
33323         if(this.pressed){
33324             this.el.addClass("x-btn-pressed");
33325         }
33326         if(Roo.isIE && !Roo.isIE7){
33327             this.autoWidth.defer(1, this);
33328         }else{
33329             this.autoWidth();
33330         }
33331         if(this.menu){
33332             this.menu.on("show", this.onMenuShow, this);
33333             this.menu.on("hide", this.onMenuHide, this);
33334         }
33335         this.fireEvent('render', this);
33336     },
33337
33338     // private
33339     autoWidth : function(){
33340         if(this.el){
33341             var tbl = this.el.child("table:first");
33342             var tbl2 = this.el.child("table:last");
33343             this.el.setWidth("auto");
33344             tbl.setWidth("auto");
33345             if(Roo.isIE7 && Roo.isStrict){
33346                 var ib = this.el.child('button:first');
33347                 if(ib && ib.getWidth() > 20){
33348                     ib.clip();
33349                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
33350                 }
33351             }
33352             if(this.minWidth){
33353                 if(this.hidden){
33354                     this.el.beginMeasure();
33355                 }
33356                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
33357                     tbl.setWidth(this.minWidth-tbl2.getWidth());
33358                 }
33359                 if(this.hidden){
33360                     this.el.endMeasure();
33361                 }
33362             }
33363             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
33364         } 
33365     },
33366     /**
33367      * Sets this button's click handler
33368      * @param {Function} handler The function to call when the button is clicked
33369      * @param {Object} scope (optional) Scope for the function passed above
33370      */
33371     setHandler : function(handler, scope){
33372         this.handler = handler;
33373         this.scope = scope;  
33374     },
33375     
33376     /**
33377      * Sets this button's arrow click handler
33378      * @param {Function} handler The function to call when the arrow is clicked
33379      * @param {Object} scope (optional) Scope for the function passed above
33380      */
33381     setArrowHandler : function(handler, scope){
33382         this.arrowHandler = handler;
33383         this.scope = scope;  
33384     },
33385     
33386     /**
33387      * Focus the button
33388      */
33389     focus : function(){
33390         if(this.el){
33391             this.el.child("button:first").focus();
33392         }
33393     },
33394
33395     // private
33396     onClick : function(e){
33397         e.preventDefault();
33398         if(!this.disabled){
33399             if(e.getTarget(".x-btn-menu-arrow-wrap")){
33400                 if(this.menu && !this.menu.isVisible()){
33401                     this.menu.show(this.el, this.menuAlign);
33402                 }
33403                 this.fireEvent("arrowclick", this, e);
33404                 if(this.arrowHandler){
33405                     this.arrowHandler.call(this.scope || this, this, e);
33406                 }
33407             }else{
33408                 this.fireEvent("click", this, e);
33409                 if(this.handler){
33410                     this.handler.call(this.scope || this, this, e);
33411                 }
33412             }
33413         }
33414     },
33415     // private
33416     onMouseDown : function(e){
33417         if(!this.disabled){
33418             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
33419         }
33420     },
33421     // private
33422     onMouseUp : function(e){
33423         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
33424     }   
33425 });
33426
33427
33428 // backwards compat
33429 Roo.MenuButton = Roo.SplitButton;/*
33430  * Based on:
33431  * Ext JS Library 1.1.1
33432  * Copyright(c) 2006-2007, Ext JS, LLC.
33433  *
33434  * Originally Released Under LGPL - original licence link has changed is not relivant.
33435  *
33436  * Fork - LGPL
33437  * <script type="text/javascript">
33438  */
33439
33440 /**
33441  * @class Roo.Toolbar
33442  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field 
33443  * Basic Toolbar class.
33444  * @constructor
33445  * Creates a new Toolbar
33446  * @param {Object} container The config object
33447  */ 
33448 Roo.Toolbar = function(container, buttons, config)
33449 {
33450     /// old consturctor format still supported..
33451     if(container instanceof Array){ // omit the container for later rendering
33452         buttons = container;
33453         config = buttons;
33454         container = null;
33455     }
33456     if (typeof(container) == 'object' && container.xtype) {
33457         config = container;
33458         container = config.container;
33459         buttons = config.buttons || []; // not really - use items!!
33460     }
33461     var xitems = [];
33462     if (config && config.items) {
33463         xitems = config.items;
33464         delete config.items;
33465     }
33466     Roo.apply(this, config);
33467     this.buttons = buttons;
33468     
33469     if(container){
33470         this.render(container);
33471     }
33472     this.xitems = xitems;
33473     Roo.each(xitems, function(b) {
33474         this.add(b);
33475     }, this);
33476     
33477 };
33478
33479 Roo.Toolbar.prototype = {
33480     /**
33481      * @cfg {Array} items
33482      * array of button configs or elements to add (will be converted to a MixedCollection)
33483      */
33484     items: false,
33485     /**
33486      * @cfg {String/HTMLElement/Element} container
33487      * The id or element that will contain the toolbar
33488      */
33489     // private
33490     render : function(ct){
33491         this.el = Roo.get(ct);
33492         if(this.cls){
33493             this.el.addClass(this.cls);
33494         }
33495         // using a table allows for vertical alignment
33496         // 100% width is needed by Safari...
33497         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
33498         this.tr = this.el.child("tr", true);
33499         var autoId = 0;
33500         this.items = new Roo.util.MixedCollection(false, function(o){
33501             return o.id || ("item" + (++autoId));
33502         });
33503         if(this.buttons){
33504             this.add.apply(this, this.buttons);
33505             delete this.buttons;
33506         }
33507     },
33508
33509     /**
33510      * Adds element(s) to the toolbar -- this function takes a variable number of 
33511      * arguments of mixed type and adds them to the toolbar.
33512      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
33513      * <ul>
33514      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
33515      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
33516      * <li>Field: Any form field (equivalent to {@link #addField})</li>
33517      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
33518      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
33519      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
33520      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
33521      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
33522      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
33523      * </ul>
33524      * @param {Mixed} arg2
33525      * @param {Mixed} etc.
33526      */
33527     add : function(){
33528         var a = arguments, l = a.length;
33529         for(var i = 0; i < l; i++){
33530             this._add(a[i]);
33531         }
33532     },
33533     // private..
33534     _add : function(el) {
33535         
33536         if (el.xtype) {
33537             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
33538         }
33539         
33540         if (el.applyTo){ // some kind of form field
33541             return this.addField(el);
33542         } 
33543         if (el.render){ // some kind of Toolbar.Item
33544             return this.addItem(el);
33545         }
33546         if (typeof el == "string"){ // string
33547             if(el == "separator" || el == "-"){
33548                 return this.addSeparator();
33549             }
33550             if (el == " "){
33551                 return this.addSpacer();
33552             }
33553             if(el == "->"){
33554                 return this.addFill();
33555             }
33556             return this.addText(el);
33557             
33558         }
33559         if(el.tagName){ // element
33560             return this.addElement(el);
33561         }
33562         if(typeof el == "object"){ // must be button config?
33563             return this.addButton(el);
33564         }
33565         // and now what?!?!
33566         return false;
33567         
33568     },
33569     
33570     /**
33571      * Add an Xtype element
33572      * @param {Object} xtype Xtype Object
33573      * @return {Object} created Object
33574      */
33575     addxtype : function(e){
33576         return this.add(e);  
33577     },
33578     
33579     /**
33580      * Returns the Element for this toolbar.
33581      * @return {Roo.Element}
33582      */
33583     getEl : function(){
33584         return this.el;  
33585     },
33586     
33587     /**
33588      * Adds a separator
33589      * @return {Roo.Toolbar.Item} The separator item
33590      */
33591     addSeparator : function(){
33592         return this.addItem(new Roo.Toolbar.Separator());
33593     },
33594
33595     /**
33596      * Adds a spacer element
33597      * @return {Roo.Toolbar.Spacer} The spacer item
33598      */
33599     addSpacer : function(){
33600         return this.addItem(new Roo.Toolbar.Spacer());
33601     },
33602
33603     /**
33604      * Adds a fill element that forces subsequent additions to the right side of the toolbar
33605      * @return {Roo.Toolbar.Fill} The fill item
33606      */
33607     addFill : function(){
33608         return this.addItem(new Roo.Toolbar.Fill());
33609     },
33610
33611     /**
33612      * Adds any standard HTML element to the toolbar
33613      * @param {String/HTMLElement/Element} el The element or id of the element to add
33614      * @return {Roo.Toolbar.Item} The element's item
33615      */
33616     addElement : function(el){
33617         return this.addItem(new Roo.Toolbar.Item(el));
33618     },
33619     /**
33620      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
33621      * @type Roo.util.MixedCollection  
33622      */
33623     items : false,
33624      
33625     /**
33626      * Adds any Toolbar.Item or subclass
33627      * @param {Roo.Toolbar.Item} item
33628      * @return {Roo.Toolbar.Item} The item
33629      */
33630     addItem : function(item){
33631         var td = this.nextBlock();
33632         item.render(td);
33633         this.items.add(item);
33634         return item;
33635     },
33636     
33637     /**
33638      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
33639      * @param {Object/Array} config A button config or array of configs
33640      * @return {Roo.Toolbar.Button/Array}
33641      */
33642     addButton : function(config){
33643         if(config instanceof Array){
33644             var buttons = [];
33645             for(var i = 0, len = config.length; i < len; i++) {
33646                 buttons.push(this.addButton(config[i]));
33647             }
33648             return buttons;
33649         }
33650         var b = config;
33651         if(!(config instanceof Roo.Toolbar.Button)){
33652             b = config.split ?
33653                 new Roo.Toolbar.SplitButton(config) :
33654                 new Roo.Toolbar.Button(config);
33655         }
33656         var td = this.nextBlock();
33657         b.render(td);
33658         this.items.add(b);
33659         return b;
33660     },
33661     
33662     /**
33663      * Adds text to the toolbar
33664      * @param {String} text The text to add
33665      * @return {Roo.Toolbar.Item} The element's item
33666      */
33667     addText : function(text){
33668         return this.addItem(new Roo.Toolbar.TextItem(text));
33669     },
33670     
33671     /**
33672      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
33673      * @param {Number} index The index where the item is to be inserted
33674      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
33675      * @return {Roo.Toolbar.Button/Item}
33676      */
33677     insertButton : function(index, item){
33678         if(item instanceof Array){
33679             var buttons = [];
33680             for(var i = 0, len = item.length; i < len; i++) {
33681                buttons.push(this.insertButton(index + i, item[i]));
33682             }
33683             return buttons;
33684         }
33685         if (!(item instanceof Roo.Toolbar.Button)){
33686            item = new Roo.Toolbar.Button(item);
33687         }
33688         var td = document.createElement("td");
33689         this.tr.insertBefore(td, this.tr.childNodes[index]);
33690         item.render(td);
33691         this.items.insert(index, item);
33692         return item;
33693     },
33694     
33695     /**
33696      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
33697      * @param {Object} config
33698      * @return {Roo.Toolbar.Item} The element's item
33699      */
33700     addDom : function(config, returnEl){
33701         var td = this.nextBlock();
33702         Roo.DomHelper.overwrite(td, config);
33703         var ti = new Roo.Toolbar.Item(td.firstChild);
33704         ti.render(td);
33705         this.items.add(ti);
33706         return ti;
33707     },
33708
33709     /**
33710      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
33711      * @type Roo.util.MixedCollection  
33712      */
33713     fields : false,
33714     
33715     /**
33716      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
33717      * Note: the field should not have been rendered yet. For a field that has already been
33718      * rendered, use {@link #addElement}.
33719      * @param {Roo.form.Field} field
33720      * @return {Roo.ToolbarItem}
33721      */
33722      
33723       
33724     addField : function(field) {
33725         if (!this.fields) {
33726             var autoId = 0;
33727             this.fields = new Roo.util.MixedCollection(false, function(o){
33728                 return o.id || ("item" + (++autoId));
33729             });
33730
33731         }
33732         
33733         var td = this.nextBlock();
33734         field.render(td);
33735         var ti = new Roo.Toolbar.Item(td.firstChild);
33736         ti.render(td);
33737         this.items.add(ti);
33738         this.fields.add(field);
33739         return ti;
33740     },
33741     /**
33742      * Hide the toolbar
33743      * @method hide
33744      */
33745      
33746       
33747     hide : function()
33748     {
33749         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
33750         this.el.child('div').hide();
33751     },
33752     /**
33753      * Show the toolbar
33754      * @method show
33755      */
33756     show : function()
33757     {
33758         this.el.child('div').show();
33759     },
33760       
33761     // private
33762     nextBlock : function(){
33763         var td = document.createElement("td");
33764         this.tr.appendChild(td);
33765         return td;
33766     },
33767
33768     // private
33769     destroy : function(){
33770         if(this.items){ // rendered?
33771             Roo.destroy.apply(Roo, this.items.items);
33772         }
33773         if(this.fields){ // rendered?
33774             Roo.destroy.apply(Roo, this.fields.items);
33775         }
33776         Roo.Element.uncache(this.el, this.tr);
33777     }
33778 };
33779
33780 /**
33781  * @class Roo.Toolbar.Item
33782  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
33783  * @constructor
33784  * Creates a new Item
33785  * @param {HTMLElement} el 
33786  */
33787 Roo.Toolbar.Item = function(el){
33788     var cfg = {};
33789     if (typeof (el.xtype) != 'undefined') {
33790         cfg = el;
33791         el = cfg.el;
33792     }
33793     
33794     this.el = Roo.getDom(el);
33795     this.id = Roo.id(this.el);
33796     this.hidden = false;
33797     
33798     this.addEvents({
33799          /**
33800              * @event render
33801              * Fires when the button is rendered
33802              * @param {Button} this
33803              */
33804         'render': true
33805     });
33806     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
33807 };
33808 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
33809 //Roo.Toolbar.Item.prototype = {
33810     
33811     /**
33812      * Get this item's HTML Element
33813      * @return {HTMLElement}
33814      */
33815     getEl : function(){
33816        return this.el;  
33817     },
33818
33819     // private
33820     render : function(td){
33821         
33822          this.td = td;
33823         td.appendChild(this.el);
33824         
33825         this.fireEvent('render', this);
33826     },
33827     
33828     /**
33829      * Removes and destroys this item.
33830      */
33831     destroy : function(){
33832         this.td.parentNode.removeChild(this.td);
33833     },
33834     
33835     /**
33836      * Shows this item.
33837      */
33838     show: function(){
33839         this.hidden = false;
33840         this.td.style.display = "";
33841     },
33842     
33843     /**
33844      * Hides this item.
33845      */
33846     hide: function(){
33847         this.hidden = true;
33848         this.td.style.display = "none";
33849     },
33850     
33851     /**
33852      * Convenience function for boolean show/hide.
33853      * @param {Boolean} visible true to show/false to hide
33854      */
33855     setVisible: function(visible){
33856         if(visible) {
33857             this.show();
33858         }else{
33859             this.hide();
33860         }
33861     },
33862     
33863     /**
33864      * Try to focus this item.
33865      */
33866     focus : function(){
33867         Roo.fly(this.el).focus();
33868     },
33869     
33870     /**
33871      * Disables this item.
33872      */
33873     disable : function(){
33874         Roo.fly(this.td).addClass("x-item-disabled");
33875         this.disabled = true;
33876         this.el.disabled = true;
33877     },
33878     
33879     /**
33880      * Enables this item.
33881      */
33882     enable : function(){
33883         Roo.fly(this.td).removeClass("x-item-disabled");
33884         this.disabled = false;
33885         this.el.disabled = false;
33886     }
33887 });
33888
33889
33890 /**
33891  * @class Roo.Toolbar.Separator
33892  * @extends Roo.Toolbar.Item
33893  * A simple toolbar separator class
33894  * @constructor
33895  * Creates a new Separator
33896  */
33897 Roo.Toolbar.Separator = function(cfg){
33898     
33899     var s = document.createElement("span");
33900     s.className = "ytb-sep";
33901     if (cfg) {
33902         cfg.el = s;
33903     }
33904     
33905     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
33906 };
33907 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
33908     enable:Roo.emptyFn,
33909     disable:Roo.emptyFn,
33910     focus:Roo.emptyFn
33911 });
33912
33913 /**
33914  * @class Roo.Toolbar.Spacer
33915  * @extends Roo.Toolbar.Item
33916  * A simple element that adds extra horizontal space to a toolbar.
33917  * @constructor
33918  * Creates a new Spacer
33919  */
33920 Roo.Toolbar.Spacer = function(cfg){
33921     var s = document.createElement("div");
33922     s.className = "ytb-spacer";
33923     if (cfg) {
33924         cfg.el = s;
33925     }
33926     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
33927 };
33928 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
33929     enable:Roo.emptyFn,
33930     disable:Roo.emptyFn,
33931     focus:Roo.emptyFn
33932 });
33933
33934 /**
33935  * @class Roo.Toolbar.Fill
33936  * @extends Roo.Toolbar.Spacer
33937  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
33938  * @constructor
33939  * Creates a new Spacer
33940  */
33941 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
33942     // private
33943     render : function(td){
33944         td.style.width = '100%';
33945         Roo.Toolbar.Fill.superclass.render.call(this, td);
33946     }
33947 });
33948
33949 /**
33950  * @class Roo.Toolbar.TextItem
33951  * @extends Roo.Toolbar.Item
33952  * A simple class that renders text directly into a toolbar.
33953  * @constructor
33954  * Creates a new TextItem
33955  * @cfg {string} text 
33956  */
33957 Roo.Toolbar.TextItem = function(cfg){
33958     var  text = cfg || "";
33959     if (typeof(cfg) == 'object') {
33960         text = cfg.text || "";
33961     }  else {
33962         cfg = null;
33963     }
33964     var s = document.createElement("span");
33965     s.className = "ytb-text";
33966     s.innerHTML = text;
33967     if (cfg) {
33968         cfg.el  = s;
33969     }
33970     
33971     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
33972 };
33973 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
33974     
33975      
33976     enable:Roo.emptyFn,
33977     disable:Roo.emptyFn,
33978     focus:Roo.emptyFn,
33979      /**
33980      * Shows this button
33981      */
33982     show: function(){
33983         this.hidden = false;
33984         this.el.style.display = "";
33985     },
33986     
33987     /**
33988      * Hides this button
33989      */
33990     hide: function(){
33991         this.hidden = true;
33992         this.el.style.display = "none";
33993     }
33994     
33995 });
33996
33997 /**
33998  * @class Roo.Toolbar.Button
33999  * @extends Roo.Button
34000  * A button that renders into a toolbar.
34001  * @constructor
34002  * Creates a new Button
34003  * @param {Object} config A standard {@link Roo.Button} config object
34004  */
34005 Roo.Toolbar.Button = function(config){
34006     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
34007 };
34008 Roo.extend(Roo.Toolbar.Button, Roo.Button,
34009 {
34010     
34011     
34012     render : function(td){
34013         this.td = td;
34014         Roo.Toolbar.Button.superclass.render.call(this, td);
34015     },
34016     
34017     /**
34018      * Removes and destroys this button
34019      */
34020     destroy : function(){
34021         Roo.Toolbar.Button.superclass.destroy.call(this);
34022         this.td.parentNode.removeChild(this.td);
34023     },
34024     
34025     /**
34026      * Shows this button
34027      */
34028     show: function(){
34029         this.hidden = false;
34030         this.td.style.display = "";
34031     },
34032     
34033     /**
34034      * Hides this button
34035      */
34036     hide: function(){
34037         this.hidden = true;
34038         this.td.style.display = "none";
34039     },
34040
34041     /**
34042      * Disables this item
34043      */
34044     disable : function(){
34045         Roo.fly(this.td).addClass("x-item-disabled");
34046         this.disabled = true;
34047     },
34048
34049     /**
34050      * Enables this item
34051      */
34052     enable : function(){
34053         Roo.fly(this.td).removeClass("x-item-disabled");
34054         this.disabled = false;
34055     }
34056 });
34057 // backwards compat
34058 Roo.ToolbarButton = Roo.Toolbar.Button;
34059
34060 /**
34061  * @class Roo.Toolbar.SplitButton
34062  * @extends Roo.SplitButton
34063  * A menu button that renders into a toolbar.
34064  * @constructor
34065  * Creates a new SplitButton
34066  * @param {Object} config A standard {@link Roo.SplitButton} config object
34067  */
34068 Roo.Toolbar.SplitButton = function(config){
34069     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
34070 };
34071 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
34072     render : function(td){
34073         this.td = td;
34074         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
34075     },
34076     
34077     /**
34078      * Removes and destroys this button
34079      */
34080     destroy : function(){
34081         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
34082         this.td.parentNode.removeChild(this.td);
34083     },
34084     
34085     /**
34086      * Shows this button
34087      */
34088     show: function(){
34089         this.hidden = false;
34090         this.td.style.display = "";
34091     },
34092     
34093     /**
34094      * Hides this button
34095      */
34096     hide: function(){
34097         this.hidden = true;
34098         this.td.style.display = "none";
34099     }
34100 });
34101
34102 // backwards compat
34103 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
34104  * Based on:
34105  * Ext JS Library 1.1.1
34106  * Copyright(c) 2006-2007, Ext JS, LLC.
34107  *
34108  * Originally Released Under LGPL - original licence link has changed is not relivant.
34109  *
34110  * Fork - LGPL
34111  * <script type="text/javascript">
34112  */
34113  
34114 /**
34115  * @class Roo.PagingToolbar
34116  * @extends Roo.Toolbar
34117  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field
34118  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
34119  * @constructor
34120  * Create a new PagingToolbar
34121  * @param {Object} config The config object
34122  */
34123 Roo.PagingToolbar = function(el, ds, config)
34124 {
34125     // old args format still supported... - xtype is prefered..
34126     if (typeof(el) == 'object' && el.xtype) {
34127         // created from xtype...
34128         config = el;
34129         ds = el.dataSource;
34130         el = config.container;
34131     }
34132     var items = [];
34133     if (config.items) {
34134         items = config.items;
34135         config.items = [];
34136     }
34137     
34138     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
34139     this.ds = ds;
34140     this.cursor = 0;
34141     this.renderButtons(this.el);
34142     this.bind(ds);
34143     
34144     // supprot items array.
34145    
34146     Roo.each(items, function(e) {
34147         this.add(Roo.factory(e));
34148     },this);
34149     
34150 };
34151
34152 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
34153    
34154     /**
34155      * @cfg {String/HTMLElement/Element} container
34156      * container The id or element that will contain the toolbar
34157      */
34158     /**
34159      * @cfg {Boolean} displayInfo
34160      * True to display the displayMsg (defaults to false)
34161      */
34162     
34163     
34164     /**
34165      * @cfg {Number} pageSize
34166      * The number of records to display per page (defaults to 20)
34167      */
34168     pageSize: 20,
34169     /**
34170      * @cfg {String} displayMsg
34171      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
34172      */
34173     displayMsg : 'Displaying {0} - {1} of {2}',
34174     /**
34175      * @cfg {String} emptyMsg
34176      * The message to display when no records are found (defaults to "No data to display")
34177      */
34178     emptyMsg : 'No data to display',
34179     /**
34180      * Customizable piece of the default paging text (defaults to "Page")
34181      * @type String
34182      */
34183     beforePageText : "Page",
34184     /**
34185      * Customizable piece of the default paging text (defaults to "of %0")
34186      * @type String
34187      */
34188     afterPageText : "of {0}",
34189     /**
34190      * Customizable piece of the default paging text (defaults to "First Page")
34191      * @type String
34192      */
34193     firstText : "First Page",
34194     /**
34195      * Customizable piece of the default paging text (defaults to "Previous Page")
34196      * @type String
34197      */
34198     prevText : "Previous Page",
34199     /**
34200      * Customizable piece of the default paging text (defaults to "Next Page")
34201      * @type String
34202      */
34203     nextText : "Next Page",
34204     /**
34205      * Customizable piece of the default paging text (defaults to "Last Page")
34206      * @type String
34207      */
34208     lastText : "Last Page",
34209     /**
34210      * Customizable piece of the default paging text (defaults to "Refresh")
34211      * @type String
34212      */
34213     refreshText : "Refresh",
34214
34215     // private
34216     renderButtons : function(el){
34217         Roo.PagingToolbar.superclass.render.call(this, el);
34218         this.first = this.addButton({
34219             tooltip: this.firstText,
34220             cls: "x-btn-icon x-grid-page-first",
34221             disabled: true,
34222             handler: this.onClick.createDelegate(this, ["first"])
34223         });
34224         this.prev = this.addButton({
34225             tooltip: this.prevText,
34226             cls: "x-btn-icon x-grid-page-prev",
34227             disabled: true,
34228             handler: this.onClick.createDelegate(this, ["prev"])
34229         });
34230         //this.addSeparator();
34231         this.add(this.beforePageText);
34232         this.field = Roo.get(this.addDom({
34233            tag: "input",
34234            type: "text",
34235            size: "3",
34236            value: "1",
34237            cls: "x-grid-page-number"
34238         }).el);
34239         this.field.on("keydown", this.onPagingKeydown, this);
34240         this.field.on("focus", function(){this.dom.select();});
34241         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
34242         this.field.setHeight(18);
34243         //this.addSeparator();
34244         this.next = this.addButton({
34245             tooltip: this.nextText,
34246             cls: "x-btn-icon x-grid-page-next",
34247             disabled: true,
34248             handler: this.onClick.createDelegate(this, ["next"])
34249         });
34250         this.last = this.addButton({
34251             tooltip: this.lastText,
34252             cls: "x-btn-icon x-grid-page-last",
34253             disabled: true,
34254             handler: this.onClick.createDelegate(this, ["last"])
34255         });
34256         //this.addSeparator();
34257         this.loading = this.addButton({
34258             tooltip: this.refreshText,
34259             cls: "x-btn-icon x-grid-loading",
34260             handler: this.onClick.createDelegate(this, ["refresh"])
34261         });
34262
34263         if(this.displayInfo){
34264             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
34265         }
34266     },
34267
34268     // private
34269     updateInfo : function(){
34270         if(this.displayEl){
34271             var count = this.ds.getCount();
34272             var msg = count == 0 ?
34273                 this.emptyMsg :
34274                 String.format(
34275                     this.displayMsg,
34276                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
34277                 );
34278             this.displayEl.update(msg);
34279         }
34280     },
34281
34282     // private
34283     onLoad : function(ds, r, o){
34284        this.cursor = o.params ? o.params.start : 0;
34285        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
34286
34287        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
34288        this.field.dom.value = ap;
34289        this.first.setDisabled(ap == 1);
34290        this.prev.setDisabled(ap == 1);
34291        this.next.setDisabled(ap == ps);
34292        this.last.setDisabled(ap == ps);
34293        this.loading.enable();
34294        this.updateInfo();
34295     },
34296
34297     // private
34298     getPageData : function(){
34299         var total = this.ds.getTotalCount();
34300         return {
34301             total : total,
34302             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
34303             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
34304         };
34305     },
34306
34307     // private
34308     onLoadError : function(){
34309         this.loading.enable();
34310     },
34311
34312     // private
34313     onPagingKeydown : function(e){
34314         var k = e.getKey();
34315         var d = this.getPageData();
34316         if(k == e.RETURN){
34317             var v = this.field.dom.value, pageNum;
34318             if(!v || isNaN(pageNum = parseInt(v, 10))){
34319                 this.field.dom.value = d.activePage;
34320                 return;
34321             }
34322             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
34323             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
34324             e.stopEvent();
34325         }
34326         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))
34327         {
34328           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
34329           this.field.dom.value = pageNum;
34330           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
34331           e.stopEvent();
34332         }
34333         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
34334         {
34335           var v = this.field.dom.value, pageNum; 
34336           var increment = (e.shiftKey) ? 10 : 1;
34337           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
34338             increment *= -1;
34339           }
34340           if(!v || isNaN(pageNum = parseInt(v, 10))) {
34341             this.field.dom.value = d.activePage;
34342             return;
34343           }
34344           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
34345           {
34346             this.field.dom.value = parseInt(v, 10) + increment;
34347             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
34348             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
34349           }
34350           e.stopEvent();
34351         }
34352     },
34353
34354     // private
34355     beforeLoad : function(){
34356         if(this.loading){
34357             this.loading.disable();
34358         }
34359     },
34360     /**
34361      * event that occurs when you click on the navigation buttons - can be used to trigger load of a grid.
34362      * @param {String} which (first|prev|next|last|refresh)  which button to press.
34363      *
34364      */
34365     // private
34366     onClick : function(which){
34367         var ds = this.ds;
34368         switch(which){
34369             case "first":
34370                 ds.load({params:{start: 0, limit: this.pageSize}});
34371             break;
34372             case "prev":
34373                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
34374             break;
34375             case "next":
34376                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
34377             break;
34378             case "last":
34379                 var total = ds.getTotalCount();
34380                 var extra = total % this.pageSize;
34381                 var lastStart = extra ? (total - extra) : total-this.pageSize;
34382                 ds.load({params:{start: lastStart, limit: this.pageSize}});
34383             break;
34384             case "refresh":
34385                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
34386             break;
34387         }
34388     },
34389
34390     /**
34391      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
34392      * @param {Roo.data.Store} store The data store to unbind
34393      */
34394     unbind : function(ds){
34395         ds.un("beforeload", this.beforeLoad, this);
34396         ds.un("load", this.onLoad, this);
34397         ds.un("loadexception", this.onLoadError, this);
34398         ds.un("remove", this.updateInfo, this);
34399         ds.un("add", this.updateInfo, this);
34400         this.ds = undefined;
34401     },
34402
34403     /**
34404      * Binds the paging toolbar to the specified {@link Roo.data.Store}
34405      * @param {Roo.data.Store} store The data store to bind
34406      */
34407     bind : function(ds){
34408         ds.on("beforeload", this.beforeLoad, this);
34409         ds.on("load", this.onLoad, this);
34410         ds.on("loadexception", this.onLoadError, this);
34411         ds.on("remove", this.updateInfo, this);
34412         ds.on("add", this.updateInfo, this);
34413         this.ds = ds;
34414     }
34415 });/*
34416  * Based on:
34417  * Ext JS Library 1.1.1
34418  * Copyright(c) 2006-2007, Ext JS, LLC.
34419  *
34420  * Originally Released Under LGPL - original licence link has changed is not relivant.
34421  *
34422  * Fork - LGPL
34423  * <script type="text/javascript">
34424  */
34425
34426 /**
34427  * @class Roo.Resizable
34428  * @extends Roo.util.Observable
34429  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
34430  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
34431  * 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
34432  * the element will be wrapped for you automatically.</p>
34433  * <p>Here is the list of valid resize handles:</p>
34434  * <pre>
34435 Value   Description
34436 ------  -------------------
34437  'n'     north
34438  's'     south
34439  'e'     east
34440  'w'     west
34441  'nw'    northwest
34442  'sw'    southwest
34443  'se'    southeast
34444  'ne'    northeast
34445  'hd'    horizontal drag
34446  'all'   all
34447 </pre>
34448  * <p>Here's an example showing the creation of a typical Resizable:</p>
34449  * <pre><code>
34450 var resizer = new Roo.Resizable("element-id", {
34451     handles: 'all',
34452     minWidth: 200,
34453     minHeight: 100,
34454     maxWidth: 500,
34455     maxHeight: 400,
34456     pinned: true
34457 });
34458 resizer.on("resize", myHandler);
34459 </code></pre>
34460  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
34461  * resizer.east.setDisplayed(false);</p>
34462  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
34463  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
34464  * resize operation's new size (defaults to [0, 0])
34465  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
34466  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
34467  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
34468  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
34469  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
34470  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
34471  * @cfg {Number} width The width of the element in pixels (defaults to null)
34472  * @cfg {Number} height The height of the element in pixels (defaults to null)
34473  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
34474  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
34475  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
34476  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
34477  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
34478  * in favor of the handles config option (defaults to false)
34479  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
34480  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
34481  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
34482  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
34483  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
34484  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
34485  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
34486  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
34487  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
34488  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
34489  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
34490  * @constructor
34491  * Create a new resizable component
34492  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
34493  * @param {Object} config configuration options
34494   */
34495 Roo.Resizable = function(el, config)
34496 {
34497     this.el = Roo.get(el);
34498
34499     if(config && config.wrap){
34500         config.resizeChild = this.el;
34501         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
34502         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
34503         this.el.setStyle("overflow", "hidden");
34504         this.el.setPositioning(config.resizeChild.getPositioning());
34505         config.resizeChild.clearPositioning();
34506         if(!config.width || !config.height){
34507             var csize = config.resizeChild.getSize();
34508             this.el.setSize(csize.width, csize.height);
34509         }
34510         if(config.pinned && !config.adjustments){
34511             config.adjustments = "auto";
34512         }
34513     }
34514
34515     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
34516     this.proxy.unselectable();
34517     this.proxy.enableDisplayMode('block');
34518
34519     Roo.apply(this, config);
34520
34521     if(this.pinned){
34522         this.disableTrackOver = true;
34523         this.el.addClass("x-resizable-pinned");
34524     }
34525     // if the element isn't positioned, make it relative
34526     var position = this.el.getStyle("position");
34527     if(position != "absolute" && position != "fixed"){
34528         this.el.setStyle("position", "relative");
34529     }
34530     if(!this.handles){ // no handles passed, must be legacy style
34531         this.handles = 's,e,se';
34532         if(this.multiDirectional){
34533             this.handles += ',n,w';
34534         }
34535     }
34536     if(this.handles == "all"){
34537         this.handles = "n s e w ne nw se sw";
34538     }
34539     var hs = this.handles.split(/\s*?[,;]\s*?| /);
34540     var ps = Roo.Resizable.positions;
34541     for(var i = 0, len = hs.length; i < len; i++){
34542         if(hs[i] && ps[hs[i]]){
34543             var pos = ps[hs[i]];
34544             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
34545         }
34546     }
34547     // legacy
34548     this.corner = this.southeast;
34549     
34550     // updateBox = the box can move..
34551     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
34552         this.updateBox = true;
34553     }
34554
34555     this.activeHandle = null;
34556
34557     if(this.resizeChild){
34558         if(typeof this.resizeChild == "boolean"){
34559             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
34560         }else{
34561             this.resizeChild = Roo.get(this.resizeChild, true);
34562         }
34563     }
34564     
34565     if(this.adjustments == "auto"){
34566         var rc = this.resizeChild;
34567         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
34568         if(rc && (hw || hn)){
34569             rc.position("relative");
34570             rc.setLeft(hw ? hw.el.getWidth() : 0);
34571             rc.setTop(hn ? hn.el.getHeight() : 0);
34572         }
34573         this.adjustments = [
34574             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
34575             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
34576         ];
34577     }
34578
34579     if(this.draggable){
34580         this.dd = this.dynamic ?
34581             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
34582         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
34583     }
34584
34585     // public events
34586     this.addEvents({
34587         /**
34588          * @event beforeresize
34589          * Fired before resize is allowed. Set enabled to false to cancel resize.
34590          * @param {Roo.Resizable} this
34591          * @param {Roo.EventObject} e The mousedown event
34592          */
34593         "beforeresize" : true,
34594         /**
34595          * @event resizing
34596          * Fired a resizing.
34597          * @param {Roo.Resizable} this
34598          * @param {Number} x The new x position
34599          * @param {Number} y The new y position
34600          * @param {Number} w The new w width
34601          * @param {Number} h The new h hight
34602          * @param {Roo.EventObject} e The mouseup event
34603          */
34604         "resizing" : true,
34605         /**
34606          * @event resize
34607          * Fired after a resize.
34608          * @param {Roo.Resizable} this
34609          * @param {Number} width The new width
34610          * @param {Number} height The new height
34611          * @param {Roo.EventObject} e The mouseup event
34612          */
34613         "resize" : true
34614     });
34615
34616     if(this.width !== null && this.height !== null){
34617         this.resizeTo(this.width, this.height);
34618     }else{
34619         this.updateChildSize();
34620     }
34621     if(Roo.isIE){
34622         this.el.dom.style.zoom = 1;
34623     }
34624     Roo.Resizable.superclass.constructor.call(this);
34625 };
34626
34627 Roo.extend(Roo.Resizable, Roo.util.Observable, {
34628         resizeChild : false,
34629         adjustments : [0, 0],
34630         minWidth : 5,
34631         minHeight : 5,
34632         maxWidth : 10000,
34633         maxHeight : 10000,
34634         enabled : true,
34635         animate : false,
34636         duration : .35,
34637         dynamic : false,
34638         handles : false,
34639         multiDirectional : false,
34640         disableTrackOver : false,
34641         easing : 'easeOutStrong',
34642         widthIncrement : 0,
34643         heightIncrement : 0,
34644         pinned : false,
34645         width : null,
34646         height : null,
34647         preserveRatio : false,
34648         transparent: false,
34649         minX: 0,
34650         minY: 0,
34651         draggable: false,
34652
34653         /**
34654          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
34655          */
34656         constrainTo: undefined,
34657         /**
34658          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
34659          */
34660         resizeRegion: undefined,
34661
34662
34663     /**
34664      * Perform a manual resize
34665      * @param {Number} width
34666      * @param {Number} height
34667      */
34668     resizeTo : function(width, height){
34669         this.el.setSize(width, height);
34670         this.updateChildSize();
34671         this.fireEvent("resize", this, width, height, null);
34672     },
34673
34674     // private
34675     startSizing : function(e, handle){
34676         this.fireEvent("beforeresize", this, e);
34677         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
34678
34679             if(!this.overlay){
34680                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
34681                 this.overlay.unselectable();
34682                 this.overlay.enableDisplayMode("block");
34683                 this.overlay.on("mousemove", this.onMouseMove, this);
34684                 this.overlay.on("mouseup", this.onMouseUp, this);
34685             }
34686             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
34687
34688             this.resizing = true;
34689             this.startBox = this.el.getBox();
34690             this.startPoint = e.getXY();
34691             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
34692                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
34693
34694             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34695             this.overlay.show();
34696
34697             if(this.constrainTo) {
34698                 var ct = Roo.get(this.constrainTo);
34699                 this.resizeRegion = ct.getRegion().adjust(
34700                     ct.getFrameWidth('t'),
34701                     ct.getFrameWidth('l'),
34702                     -ct.getFrameWidth('b'),
34703                     -ct.getFrameWidth('r')
34704                 );
34705             }
34706
34707             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
34708             this.proxy.show();
34709             this.proxy.setBox(this.startBox);
34710             if(!this.dynamic){
34711                 this.proxy.setStyle('visibility', 'visible');
34712             }
34713         }
34714     },
34715
34716     // private
34717     onMouseDown : function(handle, e){
34718         if(this.enabled){
34719             e.stopEvent();
34720             this.activeHandle = handle;
34721             this.startSizing(e, handle);
34722         }
34723     },
34724
34725     // private
34726     onMouseUp : function(e){
34727         var size = this.resizeElement();
34728         this.resizing = false;
34729         this.handleOut();
34730         this.overlay.hide();
34731         this.proxy.hide();
34732         this.fireEvent("resize", this, size.width, size.height, e);
34733     },
34734
34735     // private
34736     updateChildSize : function(){
34737         
34738         if(this.resizeChild){
34739             var el = this.el;
34740             var child = this.resizeChild;
34741             var adj = this.adjustments;
34742             if(el.dom.offsetWidth){
34743                 var b = el.getSize(true);
34744                 child.setSize(b.width+adj[0], b.height+adj[1]);
34745             }
34746             // Second call here for IE
34747             // The first call enables instant resizing and
34748             // the second call corrects scroll bars if they
34749             // exist
34750             if(Roo.isIE){
34751                 setTimeout(function(){
34752                     if(el.dom.offsetWidth){
34753                         var b = el.getSize(true);
34754                         child.setSize(b.width+adj[0], b.height+adj[1]);
34755                     }
34756                 }, 10);
34757             }
34758         }
34759     },
34760
34761     // private
34762     snap : function(value, inc, min){
34763         if(!inc || !value) {
34764             return value;
34765         }
34766         var newValue = value;
34767         var m = value % inc;
34768         if(m > 0){
34769             if(m > (inc/2)){
34770                 newValue = value + (inc-m);
34771             }else{
34772                 newValue = value - m;
34773             }
34774         }
34775         return Math.max(min, newValue);
34776     },
34777
34778     // private
34779     resizeElement : function(){
34780         var box = this.proxy.getBox();
34781         if(this.updateBox){
34782             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
34783         }else{
34784             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
34785         }
34786         this.updateChildSize();
34787         if(!this.dynamic){
34788             this.proxy.hide();
34789         }
34790         return box;
34791     },
34792
34793     // private
34794     constrain : function(v, diff, m, mx){
34795         if(v - diff < m){
34796             diff = v - m;
34797         }else if(v - diff > mx){
34798             diff = mx - v;
34799         }
34800         return diff;
34801     },
34802
34803     // private
34804     onMouseMove : function(e){
34805         
34806         if(this.enabled){
34807             try{// try catch so if something goes wrong the user doesn't get hung
34808
34809             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
34810                 return;
34811             }
34812
34813             //var curXY = this.startPoint;
34814             var curSize = this.curSize || this.startBox;
34815             var x = this.startBox.x, y = this.startBox.y;
34816             var ox = x, oy = y;
34817             var w = curSize.width, h = curSize.height;
34818             var ow = w, oh = h;
34819             var mw = this.minWidth, mh = this.minHeight;
34820             var mxw = this.maxWidth, mxh = this.maxHeight;
34821             var wi = this.widthIncrement;
34822             var hi = this.heightIncrement;
34823
34824             var eventXY = e.getXY();
34825             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
34826             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
34827
34828             var pos = this.activeHandle.position;
34829
34830             switch(pos){
34831                 case "east":
34832                     w += diffX;
34833                     w = Math.min(Math.max(mw, w), mxw);
34834                     break;
34835              
34836                 case "south":
34837                     h += diffY;
34838                     h = Math.min(Math.max(mh, h), mxh);
34839                     break;
34840                 case "southeast":
34841                     w += diffX;
34842                     h += diffY;
34843                     w = Math.min(Math.max(mw, w), mxw);
34844                     h = Math.min(Math.max(mh, h), mxh);
34845                     break;
34846                 case "north":
34847                     diffY = this.constrain(h, diffY, mh, mxh);
34848                     y += diffY;
34849                     h -= diffY;
34850                     break;
34851                 case "hdrag":
34852                     
34853                     if (wi) {
34854                         var adiffX = Math.abs(diffX);
34855                         var sub = (adiffX % wi); // how much 
34856                         if (sub > (wi/2)) { // far enough to snap
34857                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
34858                         } else {
34859                             // remove difference.. 
34860                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
34861                         }
34862                     }
34863                     x += diffX;
34864                     x = Math.max(this.minX, x);
34865                     break;
34866                 case "west":
34867                     diffX = this.constrain(w, diffX, mw, mxw);
34868                     x += diffX;
34869                     w -= diffX;
34870                     break;
34871                 case "northeast":
34872                     w += diffX;
34873                     w = Math.min(Math.max(mw, w), mxw);
34874                     diffY = this.constrain(h, diffY, mh, mxh);
34875                     y += diffY;
34876                     h -= diffY;
34877                     break;
34878                 case "northwest":
34879                     diffX = this.constrain(w, diffX, mw, mxw);
34880                     diffY = this.constrain(h, diffY, mh, mxh);
34881                     y += diffY;
34882                     h -= diffY;
34883                     x += diffX;
34884                     w -= diffX;
34885                     break;
34886                case "southwest":
34887                     diffX = this.constrain(w, diffX, mw, mxw);
34888                     h += diffY;
34889                     h = Math.min(Math.max(mh, h), mxh);
34890                     x += diffX;
34891                     w -= diffX;
34892                     break;
34893             }
34894
34895             var sw = this.snap(w, wi, mw);
34896             var sh = this.snap(h, hi, mh);
34897             if(sw != w || sh != h){
34898                 switch(pos){
34899                     case "northeast":
34900                         y -= sh - h;
34901                     break;
34902                     case "north":
34903                         y -= sh - h;
34904                         break;
34905                     case "southwest":
34906                         x -= sw - w;
34907                     break;
34908                     case "west":
34909                         x -= sw - w;
34910                         break;
34911                     case "northwest":
34912                         x -= sw - w;
34913                         y -= sh - h;
34914                     break;
34915                 }
34916                 w = sw;
34917                 h = sh;
34918             }
34919
34920             if(this.preserveRatio){
34921                 switch(pos){
34922                     case "southeast":
34923                     case "east":
34924                         h = oh * (w/ow);
34925                         h = Math.min(Math.max(mh, h), mxh);
34926                         w = ow * (h/oh);
34927                        break;
34928                     case "south":
34929                         w = ow * (h/oh);
34930                         w = Math.min(Math.max(mw, w), mxw);
34931                         h = oh * (w/ow);
34932                         break;
34933                     case "northeast":
34934                         w = ow * (h/oh);
34935                         w = Math.min(Math.max(mw, w), mxw);
34936                         h = oh * (w/ow);
34937                     break;
34938                     case "north":
34939                         var tw = w;
34940                         w = ow * (h/oh);
34941                         w = Math.min(Math.max(mw, w), mxw);
34942                         h = oh * (w/ow);
34943                         x += (tw - w) / 2;
34944                         break;
34945                     case "southwest":
34946                         h = oh * (w/ow);
34947                         h = Math.min(Math.max(mh, h), mxh);
34948                         var tw = w;
34949                         w = ow * (h/oh);
34950                         x += tw - w;
34951                         break;
34952                     case "west":
34953                         var th = h;
34954                         h = oh * (w/ow);
34955                         h = Math.min(Math.max(mh, h), mxh);
34956                         y += (th - h) / 2;
34957                         var tw = w;
34958                         w = ow * (h/oh);
34959                         x += tw - w;
34960                        break;
34961                     case "northwest":
34962                         var tw = w;
34963                         var th = h;
34964                         h = oh * (w/ow);
34965                         h = Math.min(Math.max(mh, h), mxh);
34966                         w = ow * (h/oh);
34967                         y += th - h;
34968                         x += tw - w;
34969                        break;
34970
34971                 }
34972             }
34973             if (pos == 'hdrag') {
34974                 w = ow;
34975             }
34976             this.proxy.setBounds(x, y, w, h);
34977             if(this.dynamic){
34978                 this.resizeElement();
34979             }
34980             }catch(e){}
34981         }
34982         this.fireEvent("resizing", this, x, y, w, h, e);
34983     },
34984
34985     // private
34986     handleOver : function(){
34987         if(this.enabled){
34988             this.el.addClass("x-resizable-over");
34989         }
34990     },
34991
34992     // private
34993     handleOut : function(){
34994         if(!this.resizing){
34995             this.el.removeClass("x-resizable-over");
34996         }
34997     },
34998
34999     /**
35000      * Returns the element this component is bound to.
35001      * @return {Roo.Element}
35002      */
35003     getEl : function(){
35004         return this.el;
35005     },
35006
35007     /**
35008      * Returns the resizeChild element (or null).
35009      * @return {Roo.Element}
35010      */
35011     getResizeChild : function(){
35012         return this.resizeChild;
35013     },
35014     groupHandler : function()
35015     {
35016         
35017     },
35018     /**
35019      * Destroys this resizable. If the element was wrapped and
35020      * removeEl is not true then the element remains.
35021      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
35022      */
35023     destroy : function(removeEl){
35024         this.proxy.remove();
35025         if(this.overlay){
35026             this.overlay.removeAllListeners();
35027             this.overlay.remove();
35028         }
35029         var ps = Roo.Resizable.positions;
35030         for(var k in ps){
35031             if(typeof ps[k] != "function" && this[ps[k]]){
35032                 var h = this[ps[k]];
35033                 h.el.removeAllListeners();
35034                 h.el.remove();
35035             }
35036         }
35037         if(removeEl){
35038             this.el.update("");
35039             this.el.remove();
35040         }
35041     }
35042 });
35043
35044 // private
35045 // hash to map config positions to true positions
35046 Roo.Resizable.positions = {
35047     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
35048     hd: "hdrag"
35049 };
35050
35051 // private
35052 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
35053     if(!this.tpl){
35054         // only initialize the template if resizable is used
35055         var tpl = Roo.DomHelper.createTemplate(
35056             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
35057         );
35058         tpl.compile();
35059         Roo.Resizable.Handle.prototype.tpl = tpl;
35060     }
35061     this.position = pos;
35062     this.rz = rz;
35063     // show north drag fro topdra
35064     var handlepos = pos == 'hdrag' ? 'north' : pos;
35065     
35066     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
35067     if (pos == 'hdrag') {
35068         this.el.setStyle('cursor', 'pointer');
35069     }
35070     this.el.unselectable();
35071     if(transparent){
35072         this.el.setOpacity(0);
35073     }
35074     this.el.on("mousedown", this.onMouseDown, this);
35075     if(!disableTrackOver){
35076         this.el.on("mouseover", this.onMouseOver, this);
35077         this.el.on("mouseout", this.onMouseOut, this);
35078     }
35079 };
35080
35081 // private
35082 Roo.Resizable.Handle.prototype = {
35083     afterResize : function(rz){
35084         Roo.log('after?');
35085         // do nothing
35086     },
35087     // private
35088     onMouseDown : function(e){
35089         this.rz.onMouseDown(this, e);
35090     },
35091     // private
35092     onMouseOver : function(e){
35093         this.rz.handleOver(this, e);
35094     },
35095     // private
35096     onMouseOut : function(e){
35097         this.rz.handleOut(this, e);
35098     }
35099 };/*
35100  * Based on:
35101  * Ext JS Library 1.1.1
35102  * Copyright(c) 2006-2007, Ext JS, LLC.
35103  *
35104  * Originally Released Under LGPL - original licence link has changed is not relivant.
35105  *
35106  * Fork - LGPL
35107  * <script type="text/javascript">
35108  */
35109
35110 /**
35111  * @class Roo.Editor
35112  * @extends Roo.Component
35113  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
35114  * @constructor
35115  * Create a new Editor
35116  * @param {Roo.form.Field} field The Field object (or descendant)
35117  * @param {Object} config The config object
35118  */
35119 Roo.Editor = function(field, config){
35120     Roo.Editor.superclass.constructor.call(this, config);
35121     this.field = field;
35122     this.addEvents({
35123         /**
35124              * @event beforestartedit
35125              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
35126              * false from the handler of this event.
35127              * @param {Editor} this
35128              * @param {Roo.Element} boundEl The underlying element bound to this editor
35129              * @param {Mixed} value The field value being set
35130              */
35131         "beforestartedit" : true,
35132         /**
35133              * @event startedit
35134              * Fires when this editor is displayed
35135              * @param {Roo.Element} boundEl The underlying element bound to this editor
35136              * @param {Mixed} value The starting field value
35137              */
35138         "startedit" : true,
35139         /**
35140              * @event beforecomplete
35141              * Fires after a change has been made to the field, but before the change is reflected in the underlying
35142              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
35143              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
35144              * event will not fire since no edit actually occurred.
35145              * @param {Editor} this
35146              * @param {Mixed} value The current field value
35147              * @param {Mixed} startValue The original field value
35148              */
35149         "beforecomplete" : true,
35150         /**
35151              * @event complete
35152              * Fires after editing is complete and any changed value has been written to the underlying field.
35153              * @param {Editor} this
35154              * @param {Mixed} value The current field value
35155              * @param {Mixed} startValue The original field value
35156              */
35157         "complete" : true,
35158         /**
35159          * @event specialkey
35160          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
35161          * {@link Roo.EventObject#getKey} to determine which key was pressed.
35162          * @param {Roo.form.Field} this
35163          * @param {Roo.EventObject} e The event object
35164          */
35165         "specialkey" : true
35166     });
35167 };
35168
35169 Roo.extend(Roo.Editor, Roo.Component, {
35170     /**
35171      * @cfg {Boolean/String} autosize
35172      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
35173      * or "height" to adopt the height only (defaults to false)
35174      */
35175     /**
35176      * @cfg {Boolean} revertInvalid
35177      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
35178      * validation fails (defaults to true)
35179      */
35180     /**
35181      * @cfg {Boolean} ignoreNoChange
35182      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
35183      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
35184      * will never be ignored.
35185      */
35186     /**
35187      * @cfg {Boolean} hideEl
35188      * False to keep the bound element visible while the editor is displayed (defaults to true)
35189      */
35190     /**
35191      * @cfg {Mixed} value
35192      * The data value of the underlying field (defaults to "")
35193      */
35194     value : "",
35195     /**
35196      * @cfg {String} alignment
35197      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
35198      */
35199     alignment: "c-c?",
35200     /**
35201      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
35202      * for bottom-right shadow (defaults to "frame")
35203      */
35204     shadow : "frame",
35205     /**
35206      * @cfg {Boolean} constrain True to constrain the editor to the viewport
35207      */
35208     constrain : false,
35209     /**
35210      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
35211      */
35212     completeOnEnter : false,
35213     /**
35214      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
35215      */
35216     cancelOnEsc : false,
35217     /**
35218      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
35219      */
35220     updateEl : false,
35221
35222     // private
35223     onRender : function(ct, position){
35224         this.el = new Roo.Layer({
35225             shadow: this.shadow,
35226             cls: "x-editor",
35227             parentEl : ct,
35228             shim : this.shim,
35229             shadowOffset:4,
35230             id: this.id,
35231             constrain: this.constrain
35232         });
35233         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
35234         if(this.field.msgTarget != 'title'){
35235             this.field.msgTarget = 'qtip';
35236         }
35237         this.field.render(this.el);
35238         if(Roo.isGecko){
35239             this.field.el.dom.setAttribute('autocomplete', 'off');
35240         }
35241         this.field.on("specialkey", this.onSpecialKey, this);
35242         if(this.swallowKeys){
35243             this.field.el.swallowEvent(['keydown','keypress']);
35244         }
35245         this.field.show();
35246         this.field.on("blur", this.onBlur, this);
35247         if(this.field.grow){
35248             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
35249         }
35250     },
35251
35252     onSpecialKey : function(field, e)
35253     {
35254         //Roo.log('editor onSpecialKey');
35255         if(this.completeOnEnter && e.getKey() == e.ENTER){
35256             e.stopEvent();
35257             this.completeEdit();
35258             return;
35259         }
35260         // do not fire special key otherwise it might hide close the editor...
35261         if(e.getKey() == e.ENTER){    
35262             return;
35263         }
35264         if(this.cancelOnEsc && e.getKey() == e.ESC){
35265             this.cancelEdit();
35266             return;
35267         } 
35268         this.fireEvent('specialkey', field, e);
35269     
35270     },
35271
35272     /**
35273      * Starts the editing process and shows the editor.
35274      * @param {String/HTMLElement/Element} el The element to edit
35275      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
35276       * to the innerHTML of el.
35277      */
35278     startEdit : function(el, value){
35279         if(this.editing){
35280             this.completeEdit();
35281         }
35282         this.boundEl = Roo.get(el);
35283         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
35284         if(!this.rendered){
35285             this.render(this.parentEl || document.body);
35286         }
35287         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
35288             return;
35289         }
35290         this.startValue = v;
35291         this.field.setValue(v);
35292         if(this.autoSize){
35293             var sz = this.boundEl.getSize();
35294             switch(this.autoSize){
35295                 case "width":
35296                 this.setSize(sz.width,  "");
35297                 break;
35298                 case "height":
35299                 this.setSize("",  sz.height);
35300                 break;
35301                 default:
35302                 this.setSize(sz.width,  sz.height);
35303             }
35304         }
35305         this.el.alignTo(this.boundEl, this.alignment);
35306         this.editing = true;
35307         if(Roo.QuickTips){
35308             Roo.QuickTips.disable();
35309         }
35310         this.show();
35311     },
35312
35313     /**
35314      * Sets the height and width of this editor.
35315      * @param {Number} width The new width
35316      * @param {Number} height The new height
35317      */
35318     setSize : function(w, h){
35319         this.field.setSize(w, h);
35320         if(this.el){
35321             this.el.sync();
35322         }
35323     },
35324
35325     /**
35326      * Realigns the editor to the bound field based on the current alignment config value.
35327      */
35328     realign : function(){
35329         this.el.alignTo(this.boundEl, this.alignment);
35330     },
35331
35332     /**
35333      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
35334      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
35335      */
35336     completeEdit : function(remainVisible){
35337         if(!this.editing){
35338             return;
35339         }
35340         var v = this.getValue();
35341         if(this.revertInvalid !== false && !this.field.isValid()){
35342             v = this.startValue;
35343             this.cancelEdit(true);
35344         }
35345         if(String(v) === String(this.startValue) && this.ignoreNoChange){
35346             this.editing = false;
35347             this.hide();
35348             return;
35349         }
35350         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
35351             this.editing = false;
35352             if(this.updateEl && this.boundEl){
35353                 this.boundEl.update(v);
35354             }
35355             if(remainVisible !== true){
35356                 this.hide();
35357             }
35358             this.fireEvent("complete", this, v, this.startValue);
35359         }
35360     },
35361
35362     // private
35363     onShow : function(){
35364         this.el.show();
35365         if(this.hideEl !== false){
35366             this.boundEl.hide();
35367         }
35368         this.field.show();
35369         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
35370             this.fixIEFocus = true;
35371             this.deferredFocus.defer(50, this);
35372         }else{
35373             this.field.focus();
35374         }
35375         this.fireEvent("startedit", this.boundEl, this.startValue);
35376     },
35377
35378     deferredFocus : function(){
35379         if(this.editing){
35380             this.field.focus();
35381         }
35382     },
35383
35384     /**
35385      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
35386      * reverted to the original starting value.
35387      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
35388      * cancel (defaults to false)
35389      */
35390     cancelEdit : function(remainVisible){
35391         if(this.editing){
35392             this.setValue(this.startValue);
35393             if(remainVisible !== true){
35394                 this.hide();
35395             }
35396         }
35397     },
35398
35399     // private
35400     onBlur : function(){
35401         if(this.allowBlur !== true && this.editing){
35402             this.completeEdit();
35403         }
35404     },
35405
35406     // private
35407     onHide : function(){
35408         if(this.editing){
35409             this.completeEdit();
35410             return;
35411         }
35412         this.field.blur();
35413         if(this.field.collapse){
35414             this.field.collapse();
35415         }
35416         this.el.hide();
35417         if(this.hideEl !== false){
35418             this.boundEl.show();
35419         }
35420         if(Roo.QuickTips){
35421             Roo.QuickTips.enable();
35422         }
35423     },
35424
35425     /**
35426      * Sets the data value of the editor
35427      * @param {Mixed} value Any valid value supported by the underlying field
35428      */
35429     setValue : function(v){
35430         this.field.setValue(v);
35431     },
35432
35433     /**
35434      * Gets the data value of the editor
35435      * @return {Mixed} The data value
35436      */
35437     getValue : function(){
35438         return this.field.getValue();
35439     }
35440 });/*
35441  * Based on:
35442  * Ext JS Library 1.1.1
35443  * Copyright(c) 2006-2007, Ext JS, LLC.
35444  *
35445  * Originally Released Under LGPL - original licence link has changed is not relivant.
35446  *
35447  * Fork - LGPL
35448  * <script type="text/javascript">
35449  */
35450  
35451 /**
35452  * @class Roo.BasicDialog
35453  * @extends Roo.util.Observable
35454  * @parent none builder
35455  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
35456  * <pre><code>
35457 var dlg = new Roo.BasicDialog("my-dlg", {
35458     height: 200,
35459     width: 300,
35460     minHeight: 100,
35461     minWidth: 150,
35462     modal: true,
35463     proxyDrag: true,
35464     shadow: true
35465 });
35466 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
35467 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
35468 dlg.addButton('Cancel', dlg.hide, dlg);
35469 dlg.show();
35470 </code></pre>
35471   <b>A Dialog should always be a direct child of the body element.</b>
35472  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
35473  * @cfg {String} title Default text to display in the title bar (defaults to null)
35474  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
35475  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
35476  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
35477  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
35478  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
35479  * (defaults to null with no animation)
35480  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
35481  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
35482  * property for valid values (defaults to 'all')
35483  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
35484  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
35485  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
35486  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
35487  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
35488  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
35489  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
35490  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
35491  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
35492  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
35493  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
35494  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
35495  * draggable = true (defaults to false)
35496  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
35497  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
35498  * shadow (defaults to false)
35499  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
35500  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
35501  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
35502  * @cfg {Array} buttons Array of buttons
35503  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
35504  * @constructor
35505  * Create a new BasicDialog.
35506  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
35507  * @param {Object} config Configuration options
35508  */
35509 Roo.BasicDialog = function(el, config){
35510     this.el = Roo.get(el);
35511     var dh = Roo.DomHelper;
35512     if(!this.el && config && config.autoCreate){
35513         if(typeof config.autoCreate == "object"){
35514             if(!config.autoCreate.id){
35515                 config.autoCreate.id = el;
35516             }
35517             this.el = dh.append(document.body,
35518                         config.autoCreate, true);
35519         }else{
35520             this.el = dh.append(document.body,
35521                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
35522         }
35523     }
35524     el = this.el;
35525     el.setDisplayed(true);
35526     el.hide = this.hideAction;
35527     this.id = el.id;
35528     el.addClass("x-dlg");
35529
35530     Roo.apply(this, config);
35531
35532     this.proxy = el.createProxy("x-dlg-proxy");
35533     this.proxy.hide = this.hideAction;
35534     this.proxy.setOpacity(.5);
35535     this.proxy.hide();
35536
35537     if(config.width){
35538         el.setWidth(config.width);
35539     }
35540     if(config.height){
35541         el.setHeight(config.height);
35542     }
35543     this.size = el.getSize();
35544     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
35545         this.xy = [config.x,config.y];
35546     }else{
35547         this.xy = el.getCenterXY(true);
35548     }
35549     /** The header element @type Roo.Element */
35550     this.header = el.child("> .x-dlg-hd");
35551     /** The body element @type Roo.Element */
35552     this.body = el.child("> .x-dlg-bd");
35553     /** The footer element @type Roo.Element */
35554     this.footer = el.child("> .x-dlg-ft");
35555
35556     if(!this.header){
35557         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
35558     }
35559     if(!this.body){
35560         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
35561     }
35562
35563     this.header.unselectable();
35564     if(this.title){
35565         this.header.update(this.title);
35566     }
35567     // this element allows the dialog to be focused for keyboard event
35568     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
35569     this.focusEl.swallowEvent("click", true);
35570
35571     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
35572
35573     // wrap the body and footer for special rendering
35574     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
35575     if(this.footer){
35576         this.bwrap.dom.appendChild(this.footer.dom);
35577     }
35578
35579     this.bg = this.el.createChild({
35580         tag: "div", cls:"x-dlg-bg",
35581         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
35582     });
35583     this.centerBg = this.bg.child("div.x-dlg-bg-center");
35584
35585
35586     if(this.autoScroll !== false && !this.autoTabs){
35587         this.body.setStyle("overflow", "auto");
35588     }
35589
35590     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
35591
35592     if(this.closable !== false){
35593         this.el.addClass("x-dlg-closable");
35594         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
35595         this.close.on("click", this.closeClick, this);
35596         this.close.addClassOnOver("x-dlg-close-over");
35597     }
35598     if(this.collapsible !== false){
35599         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
35600         this.collapseBtn.on("click", this.collapseClick, this);
35601         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
35602         this.header.on("dblclick", this.collapseClick, this);
35603     }
35604     if(this.resizable !== false){
35605         this.el.addClass("x-dlg-resizable");
35606         this.resizer = new Roo.Resizable(el, {
35607             minWidth: this.minWidth || 80,
35608             minHeight:this.minHeight || 80,
35609             handles: this.resizeHandles || "all",
35610             pinned: true
35611         });
35612         this.resizer.on("beforeresize", this.beforeResize, this);
35613         this.resizer.on("resize", this.onResize, this);
35614     }
35615     if(this.draggable !== false){
35616         el.addClass("x-dlg-draggable");
35617         if (!this.proxyDrag) {
35618             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
35619         }
35620         else {
35621             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
35622         }
35623         dd.setHandleElId(this.header.id);
35624         dd.endDrag = this.endMove.createDelegate(this);
35625         dd.startDrag = this.startMove.createDelegate(this);
35626         dd.onDrag = this.onDrag.createDelegate(this);
35627         dd.scroll = false;
35628         this.dd = dd;
35629     }
35630     if(this.modal){
35631         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
35632         this.mask.enableDisplayMode("block");
35633         this.mask.hide();
35634         this.el.addClass("x-dlg-modal");
35635     }
35636     if(this.shadow){
35637         this.shadow = new Roo.Shadow({
35638             mode : typeof this.shadow == "string" ? this.shadow : "sides",
35639             offset : this.shadowOffset
35640         });
35641     }else{
35642         this.shadowOffset = 0;
35643     }
35644     if(Roo.useShims && this.shim !== false){
35645         this.shim = this.el.createShim();
35646         this.shim.hide = this.hideAction;
35647         this.shim.hide();
35648     }else{
35649         this.shim = false;
35650     }
35651     if(this.autoTabs){
35652         this.initTabs();
35653     }
35654     if (this.buttons) { 
35655         var bts= this.buttons;
35656         this.buttons = [];
35657         Roo.each(bts, function(b) {
35658             this.addButton(b);
35659         }, this);
35660     }
35661     
35662     
35663     this.addEvents({
35664         /**
35665          * @event keydown
35666          * Fires when a key is pressed
35667          * @param {Roo.BasicDialog} this
35668          * @param {Roo.EventObject} e
35669          */
35670         "keydown" : true,
35671         /**
35672          * @event move
35673          * Fires when this dialog is moved by the user.
35674          * @param {Roo.BasicDialog} this
35675          * @param {Number} x The new page X
35676          * @param {Number} y The new page Y
35677          */
35678         "move" : true,
35679         /**
35680          * @event resize
35681          * Fires when this dialog is resized by the user.
35682          * @param {Roo.BasicDialog} this
35683          * @param {Number} width The new width
35684          * @param {Number} height The new height
35685          */
35686         "resize" : true,
35687         /**
35688          * @event beforehide
35689          * Fires before this dialog is hidden.
35690          * @param {Roo.BasicDialog} this
35691          */
35692         "beforehide" : true,
35693         /**
35694          * @event hide
35695          * Fires when this dialog is hidden.
35696          * @param {Roo.BasicDialog} this
35697          */
35698         "hide" : true,
35699         /**
35700          * @event beforeshow
35701          * Fires before this dialog is shown.
35702          * @param {Roo.BasicDialog} this
35703          */
35704         "beforeshow" : true,
35705         /**
35706          * @event show
35707          * Fires when this dialog is shown.
35708          * @param {Roo.BasicDialog} this
35709          */
35710         "show" : true
35711     });
35712     el.on("keydown", this.onKeyDown, this);
35713     el.on("mousedown", this.toFront, this);
35714     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
35715     this.el.hide();
35716     Roo.DialogManager.register(this);
35717     Roo.BasicDialog.superclass.constructor.call(this);
35718 };
35719
35720 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
35721     shadowOffset: Roo.isIE ? 6 : 5,
35722     minHeight: 80,
35723     minWidth: 200,
35724     minButtonWidth: 75,
35725     defaultButton: null,
35726     buttonAlign: "right",
35727     tabTag: 'div',
35728     firstShow: true,
35729
35730     /**
35731      * Sets the dialog title text
35732      * @param {String} text The title text to display
35733      * @return {Roo.BasicDialog} this
35734      */
35735     setTitle : function(text){
35736         this.header.update(text);
35737         return this;
35738     },
35739
35740     // private
35741     closeClick : function(){
35742         this.hide();
35743     },
35744
35745     // private
35746     collapseClick : function(){
35747         this[this.collapsed ? "expand" : "collapse"]();
35748     },
35749
35750     /**
35751      * Collapses the dialog to its minimized state (only the title bar is visible).
35752      * Equivalent to the user clicking the collapse dialog button.
35753      */
35754     collapse : function(){
35755         if(!this.collapsed){
35756             this.collapsed = true;
35757             this.el.addClass("x-dlg-collapsed");
35758             this.restoreHeight = this.el.getHeight();
35759             this.resizeTo(this.el.getWidth(), this.header.getHeight());
35760         }
35761     },
35762
35763     /**
35764      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
35765      * clicking the expand dialog button.
35766      */
35767     expand : function(){
35768         if(this.collapsed){
35769             this.collapsed = false;
35770             this.el.removeClass("x-dlg-collapsed");
35771             this.resizeTo(this.el.getWidth(), this.restoreHeight);
35772         }
35773     },
35774
35775     /**
35776      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
35777      * @return {Roo.panel.Tab} The tabs component
35778      */
35779     initTabs : function(){
35780         var tabs = this.getTabs();
35781         while(tabs.getTab(0)){
35782             tabs.removeTab(0);
35783         }
35784         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
35785             var dom = el.dom;
35786             tabs.addTab(Roo.id(dom), dom.title);
35787             dom.title = "";
35788         });
35789         tabs.activate(0);
35790         return tabs;
35791     },
35792
35793     // private
35794     beforeResize : function(){
35795         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
35796     },
35797
35798     // private
35799     onResize : function(){
35800         this.refreshSize();
35801         this.syncBodyHeight();
35802         this.adjustAssets();
35803         this.focus();
35804         this.fireEvent("resize", this, this.size.width, this.size.height);
35805     },
35806
35807     // private
35808     onKeyDown : function(e){
35809         if(this.isVisible()){
35810             this.fireEvent("keydown", this, e);
35811         }
35812     },
35813
35814     /**
35815      * Resizes the dialog.
35816      * @param {Number} width
35817      * @param {Number} height
35818      * @return {Roo.BasicDialog} this
35819      */
35820     resizeTo : function(width, height){
35821         this.el.setSize(width, height);
35822         this.size = {width: width, height: height};
35823         this.syncBodyHeight();
35824         if(this.fixedcenter){
35825             this.center();
35826         }
35827         if(this.isVisible()){
35828             this.constrainXY();
35829             this.adjustAssets();
35830         }
35831         this.fireEvent("resize", this, width, height);
35832         return this;
35833     },
35834
35835
35836     /**
35837      * Resizes the dialog to fit the specified content size.
35838      * @param {Number} width
35839      * @param {Number} height
35840      * @return {Roo.BasicDialog} this
35841      */
35842     setContentSize : function(w, h){
35843         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
35844         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
35845         //if(!this.el.isBorderBox()){
35846             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
35847             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
35848         //}
35849         if(this.tabs){
35850             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
35851             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
35852         }
35853         this.resizeTo(w, h);
35854         return this;
35855     },
35856
35857     /**
35858      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
35859      * executed in response to a particular key being pressed while the dialog is active.
35860      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
35861      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
35862      * @param {Function} fn The function to call
35863      * @param {Object} scope (optional) The scope of the function
35864      * @return {Roo.BasicDialog} this
35865      */
35866     addKeyListener : function(key, fn, scope){
35867         var keyCode, shift, ctrl, alt;
35868         if(typeof key == "object" && !(key instanceof Array)){
35869             keyCode = key["key"];
35870             shift = key["shift"];
35871             ctrl = key["ctrl"];
35872             alt = key["alt"];
35873         }else{
35874             keyCode = key;
35875         }
35876         var handler = function(dlg, e){
35877             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
35878                 var k = e.getKey();
35879                 if(keyCode instanceof Array){
35880                     for(var i = 0, len = keyCode.length; i < len; i++){
35881                         if(keyCode[i] == k){
35882                           fn.call(scope || window, dlg, k, e);
35883                           return;
35884                         }
35885                     }
35886                 }else{
35887                     if(k == keyCode){
35888                         fn.call(scope || window, dlg, k, e);
35889                     }
35890                 }
35891             }
35892         };
35893         this.on("keydown", handler);
35894         return this;
35895     },
35896
35897     /**
35898      * Returns the panel.Tab component (creates it if it doesn't exist).
35899      * Note: If you wish to simply check for the existence of tabs without creating them,
35900      * check for a null 'tabs' property.
35901      * @return {Roo.panel.Tab} The tabs component
35902      */
35903     getTabs : function(){
35904         if(!this.tabs){
35905             this.el.addClass("x-dlg-auto-tabs");
35906             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
35907             this.tabs = new Roo.panel.Tab(this.body.dom, this.tabPosition == "bottom");
35908         }
35909         return this.tabs;
35910     },
35911
35912     /**
35913      * Adds a button to the footer section of the dialog.
35914      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
35915      * object or a valid Roo.DomHelper element config
35916      * @param {Function} handler The function called when the button is clicked
35917      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
35918      * @return {Roo.Button} The new button
35919      */
35920     addButton : function(config, handler, scope){
35921         var dh = Roo.DomHelper;
35922         if(!this.footer){
35923             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
35924         }
35925         if(!this.btnContainer){
35926             var tb = this.footer.createChild({
35927
35928                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
35929                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
35930             }, null, true);
35931             this.btnContainer = tb.firstChild.firstChild.firstChild;
35932         }
35933         var bconfig = {
35934             handler: handler,
35935             scope: scope,
35936             minWidth: this.minButtonWidth,
35937             hideParent:true
35938         };
35939         if(typeof config == "string"){
35940             bconfig.text = config;
35941         }else{
35942             if(config.tag){
35943                 bconfig.dhconfig = config;
35944             }else{
35945                 Roo.apply(bconfig, config);
35946             }
35947         }
35948         var fc = false;
35949         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
35950             bconfig.position = Math.max(0, bconfig.position);
35951             fc = this.btnContainer.childNodes[bconfig.position];
35952         }
35953          
35954         var btn = new Roo.Button(
35955             fc ? 
35956                 this.btnContainer.insertBefore(document.createElement("td"),fc)
35957                 : this.btnContainer.appendChild(document.createElement("td")),
35958             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
35959             bconfig
35960         );
35961         this.syncBodyHeight();
35962         if(!this.buttons){
35963             /**
35964              * Array of all the buttons that have been added to this dialog via addButton
35965              * @type Array
35966              */
35967             this.buttons = [];
35968         }
35969         this.buttons.push(btn);
35970         return btn;
35971     },
35972
35973     /**
35974      * Sets the default button to be focused when the dialog is displayed.
35975      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
35976      * @return {Roo.BasicDialog} this
35977      */
35978     setDefaultButton : function(btn){
35979         this.defaultButton = btn;
35980         return this;
35981     },
35982
35983     // private
35984     getHeaderFooterHeight : function(safe){
35985         var height = 0;
35986         if(this.header){
35987            height += this.header.getHeight();
35988         }
35989         if(this.footer){
35990            var fm = this.footer.getMargins();
35991             height += (this.footer.getHeight()+fm.top+fm.bottom);
35992         }
35993         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
35994         height += this.centerBg.getPadding("tb");
35995         return height;
35996     },
35997
35998     // private
35999     syncBodyHeight : function()
36000     {
36001         var bd = this.body, // the text
36002             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
36003             bw = this.bwrap;
36004         var height = this.size.height - this.getHeaderFooterHeight(false);
36005         bd.setHeight(height-bd.getMargins("tb"));
36006         var hh = this.header.getHeight();
36007         var h = this.size.height-hh;
36008         cb.setHeight(h);
36009         
36010         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
36011         bw.setHeight(h-cb.getPadding("tb"));
36012         
36013         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
36014         bd.setWidth(bw.getWidth(true));
36015         if(this.tabs){
36016             this.tabs.syncHeight();
36017             if(Roo.isIE){
36018                 this.tabs.el.repaint();
36019             }
36020         }
36021     },
36022
36023     /**
36024      * Restores the previous state of the dialog if Roo.state is configured.
36025      * @return {Roo.BasicDialog} this
36026      */
36027     restoreState : function(){
36028         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
36029         if(box && box.width){
36030             this.xy = [box.x, box.y];
36031             this.resizeTo(box.width, box.height);
36032         }
36033         return this;
36034     },
36035
36036     // private
36037     beforeShow : function(){
36038         this.expand();
36039         if(this.fixedcenter){
36040             this.xy = this.el.getCenterXY(true);
36041         }
36042         if(this.modal){
36043             Roo.get(document.body).addClass("x-body-masked");
36044             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36045             this.mask.show();
36046         }
36047         this.constrainXY();
36048     },
36049
36050     // private
36051     animShow : function(){
36052         var b = Roo.get(this.animateTarget).getBox();
36053         this.proxy.setSize(b.width, b.height);
36054         this.proxy.setLocation(b.x, b.y);
36055         this.proxy.show();
36056         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
36057                     true, .35, this.showEl.createDelegate(this));
36058     },
36059
36060     /**
36061      * Shows the dialog.
36062      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
36063      * @return {Roo.BasicDialog} this
36064      */
36065     show : function(animateTarget){
36066         if (this.fireEvent("beforeshow", this) === false){
36067             return;
36068         }
36069         if(this.syncHeightBeforeShow){
36070             this.syncBodyHeight();
36071         }else if(this.firstShow){
36072             this.firstShow = false;
36073             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
36074         }
36075         this.animateTarget = animateTarget || this.animateTarget;
36076         if(!this.el.isVisible()){
36077             this.beforeShow();
36078             if(this.animateTarget && Roo.get(this.animateTarget)){
36079                 this.animShow();
36080             }else{
36081                 this.showEl();
36082             }
36083         }
36084         return this;
36085     },
36086
36087     // private
36088     showEl : function(){
36089         this.proxy.hide();
36090         this.el.setXY(this.xy);
36091         this.el.show();
36092         this.adjustAssets(true);
36093         this.toFront();
36094         this.focus();
36095         // IE peekaboo bug - fix found by Dave Fenwick
36096         if(Roo.isIE){
36097             this.el.repaint();
36098         }
36099         this.fireEvent("show", this);
36100     },
36101
36102     /**
36103      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
36104      * dialog itself will receive focus.
36105      */
36106     focus : function(){
36107         if(this.defaultButton){
36108             this.defaultButton.focus();
36109         }else{
36110             this.focusEl.focus();
36111         }
36112     },
36113
36114     // private
36115     constrainXY : function(){
36116         if(this.constraintoviewport !== false){
36117             if(!this.viewSize){
36118                 if(this.container){
36119                     var s = this.container.getSize();
36120                     this.viewSize = [s.width, s.height];
36121                 }else{
36122                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
36123                 }
36124             }
36125             var s = Roo.get(this.container||document).getScroll();
36126
36127             var x = this.xy[0], y = this.xy[1];
36128             var w = this.size.width, h = this.size.height;
36129             var vw = this.viewSize[0], vh = this.viewSize[1];
36130             // only move it if it needs it
36131             var moved = false;
36132             // first validate right/bottom
36133             if(x + w > vw+s.left){
36134                 x = vw - w;
36135                 moved = true;
36136             }
36137             if(y + h > vh+s.top){
36138                 y = vh - h;
36139                 moved = true;
36140             }
36141             // then make sure top/left isn't negative
36142             if(x < s.left){
36143                 x = s.left;
36144                 moved = true;
36145             }
36146             if(y < s.top){
36147                 y = s.top;
36148                 moved = true;
36149             }
36150             if(moved){
36151                 // cache xy
36152                 this.xy = [x, y];
36153                 if(this.isVisible()){
36154                     this.el.setLocation(x, y);
36155                     this.adjustAssets();
36156                 }
36157             }
36158         }
36159     },
36160
36161     // private
36162     onDrag : function(){
36163         if(!this.proxyDrag){
36164             this.xy = this.el.getXY();
36165             this.adjustAssets();
36166         }
36167     },
36168
36169     // private
36170     adjustAssets : function(doShow){
36171         var x = this.xy[0], y = this.xy[1];
36172         var w = this.size.width, h = this.size.height;
36173         if(doShow === true){
36174             if(this.shadow){
36175                 this.shadow.show(this.el);
36176             }
36177             if(this.shim){
36178                 this.shim.show();
36179             }
36180         }
36181         if(this.shadow && this.shadow.isVisible()){
36182             this.shadow.show(this.el);
36183         }
36184         if(this.shim && this.shim.isVisible()){
36185             this.shim.setBounds(x, y, w, h);
36186         }
36187     },
36188
36189     // private
36190     adjustViewport : function(w, h){
36191         if(!w || !h){
36192             w = Roo.lib.Dom.getViewWidth();
36193             h = Roo.lib.Dom.getViewHeight();
36194         }
36195         // cache the size
36196         this.viewSize = [w, h];
36197         if(this.modal && this.mask.isVisible()){
36198             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
36199             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36200         }
36201         if(this.isVisible()){
36202             this.constrainXY();
36203         }
36204     },
36205
36206     /**
36207      * Destroys this dialog and all its supporting elements (including any tabs, shim,
36208      * shadow, proxy, mask, etc.)  Also removes all event listeners.
36209      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
36210      */
36211     destroy : function(removeEl){
36212         if(this.isVisible()){
36213             this.animateTarget = null;
36214             this.hide();
36215         }
36216         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
36217         if(this.tabs){
36218             this.tabs.destroy(removeEl);
36219         }
36220         Roo.destroy(
36221              this.shim,
36222              this.proxy,
36223              this.resizer,
36224              this.close,
36225              this.mask
36226         );
36227         if(this.dd){
36228             this.dd.unreg();
36229         }
36230         if(this.buttons){
36231            for(var i = 0, len = this.buttons.length; i < len; i++){
36232                this.buttons[i].destroy();
36233            }
36234         }
36235         this.el.removeAllListeners();
36236         if(removeEl === true){
36237             this.el.update("");
36238             this.el.remove();
36239         }
36240         Roo.DialogManager.unregister(this);
36241     },
36242
36243     // private
36244     startMove : function(){
36245         if(this.proxyDrag){
36246             this.proxy.show();
36247         }
36248         if(this.constraintoviewport !== false){
36249             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
36250         }
36251     },
36252
36253     // private
36254     endMove : function(){
36255         if(!this.proxyDrag){
36256             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
36257         }else{
36258             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
36259             this.proxy.hide();
36260         }
36261         this.refreshSize();
36262         this.adjustAssets();
36263         this.focus();
36264         this.fireEvent("move", this, this.xy[0], this.xy[1]);
36265     },
36266
36267     /**
36268      * Brings this dialog to the front of any other visible dialogs
36269      * @return {Roo.BasicDialog} this
36270      */
36271     toFront : function(){
36272         Roo.DialogManager.bringToFront(this);
36273         return this;
36274     },
36275
36276     /**
36277      * Sends this dialog to the back (under) of any other visible dialogs
36278      * @return {Roo.BasicDialog} this
36279      */
36280     toBack : function(){
36281         Roo.DialogManager.sendToBack(this);
36282         return this;
36283     },
36284
36285     /**
36286      * Centers this dialog in the viewport
36287      * @return {Roo.BasicDialog} this
36288      */
36289     center : function(){
36290         var xy = this.el.getCenterXY(true);
36291         this.moveTo(xy[0], xy[1]);
36292         return this;
36293     },
36294
36295     /**
36296      * Moves the dialog's top-left corner to the specified point
36297      * @param {Number} x
36298      * @param {Number} y
36299      * @return {Roo.BasicDialog} this
36300      */
36301     moveTo : function(x, y){
36302         this.xy = [x,y];
36303         if(this.isVisible()){
36304             this.el.setXY(this.xy);
36305             this.adjustAssets();
36306         }
36307         return this;
36308     },
36309
36310     /**
36311      * Aligns the dialog to the specified element
36312      * @param {String/HTMLElement/Roo.Element} element The element to align to.
36313      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
36314      * @param {Array} offsets (optional) Offset the positioning by [x, y]
36315      * @return {Roo.BasicDialog} this
36316      */
36317     alignTo : function(element, position, offsets){
36318         this.xy = this.el.getAlignToXY(element, position, offsets);
36319         if(this.isVisible()){
36320             this.el.setXY(this.xy);
36321             this.adjustAssets();
36322         }
36323         return this;
36324     },
36325
36326     /**
36327      * Anchors an element to another element and realigns it when the window is resized.
36328      * @param {String/HTMLElement/Roo.Element} element The element to align to.
36329      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
36330      * @param {Array} offsets (optional) Offset the positioning by [x, y]
36331      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
36332      * is a number, it is used as the buffer delay (defaults to 50ms).
36333      * @return {Roo.BasicDialog} this
36334      */
36335     anchorTo : function(el, alignment, offsets, monitorScroll){
36336         var action = function(){
36337             this.alignTo(el, alignment, offsets);
36338         };
36339         Roo.EventManager.onWindowResize(action, this);
36340         var tm = typeof monitorScroll;
36341         if(tm != 'undefined'){
36342             Roo.EventManager.on(window, 'scroll', action, this,
36343                 {buffer: tm == 'number' ? monitorScroll : 50});
36344         }
36345         action.call(this);
36346         return this;
36347     },
36348
36349     /**
36350      * Returns true if the dialog is visible
36351      * @return {Boolean}
36352      */
36353     isVisible : function(){
36354         return this.el.isVisible();
36355     },
36356
36357     // private
36358     animHide : function(callback){
36359         var b = Roo.get(this.animateTarget).getBox();
36360         this.proxy.show();
36361         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
36362         this.el.hide();
36363         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
36364                     this.hideEl.createDelegate(this, [callback]));
36365     },
36366
36367     /**
36368      * Hides the dialog.
36369      * @param {Function} callback (optional) Function to call when the dialog is hidden
36370      * @return {Roo.BasicDialog} this
36371      */
36372     hide : function(callback){
36373         if (this.fireEvent("beforehide", this) === false){
36374             return;
36375         }
36376         if(this.shadow){
36377             this.shadow.hide();
36378         }
36379         if(this.shim) {
36380           this.shim.hide();
36381         }
36382         // sometimes animateTarget seems to get set.. causing problems...
36383         // this just double checks..
36384         if(this.animateTarget && Roo.get(this.animateTarget)) {
36385            this.animHide(callback);
36386         }else{
36387             this.el.hide();
36388             this.hideEl(callback);
36389         }
36390         return this;
36391     },
36392
36393     // private
36394     hideEl : function(callback){
36395         this.proxy.hide();
36396         if(this.modal){
36397             this.mask.hide();
36398             Roo.get(document.body).removeClass("x-body-masked");
36399         }
36400         this.fireEvent("hide", this);
36401         if(typeof callback == "function"){
36402             callback();
36403         }
36404     },
36405
36406     // private
36407     hideAction : function(){
36408         this.setLeft("-10000px");
36409         this.setTop("-10000px");
36410         this.setStyle("visibility", "hidden");
36411     },
36412
36413     // private
36414     refreshSize : function(){
36415         this.size = this.el.getSize();
36416         this.xy = this.el.getXY();
36417         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
36418     },
36419
36420     // private
36421     // z-index is managed by the DialogManager and may be overwritten at any time
36422     setZIndex : function(index){
36423         if(this.modal){
36424             this.mask.setStyle("z-index", index);
36425         }
36426         if(this.shim){
36427             this.shim.setStyle("z-index", ++index);
36428         }
36429         if(this.shadow){
36430             this.shadow.setZIndex(++index);
36431         }
36432         this.el.setStyle("z-index", ++index);
36433         if(this.proxy){
36434             this.proxy.setStyle("z-index", ++index);
36435         }
36436         if(this.resizer){
36437             this.resizer.proxy.setStyle("z-index", ++index);
36438         }
36439
36440         this.lastZIndex = index;
36441     },
36442
36443     /**
36444      * Returns the element for this dialog
36445      * @return {Roo.Element} The underlying dialog Element
36446      */
36447     getEl : function(){
36448         return this.el;
36449     }
36450 });
36451
36452 /**
36453  * @class Roo.DialogManager
36454  * Provides global access to BasicDialogs that have been created and
36455  * support for z-indexing (layering) multiple open dialogs.
36456  */
36457 Roo.DialogManager = function(){
36458     var list = {};
36459     var accessList = [];
36460     var front = null;
36461
36462     // private
36463     var sortDialogs = function(d1, d2){
36464         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
36465     };
36466
36467     // private
36468     var orderDialogs = function(){
36469         accessList.sort(sortDialogs);
36470         var seed = Roo.DialogManager.zseed;
36471         for(var i = 0, len = accessList.length; i < len; i++){
36472             var dlg = accessList[i];
36473             if(dlg){
36474                 dlg.setZIndex(seed + (i*10));
36475             }
36476         }
36477     };
36478
36479     return {
36480         /**
36481          * The starting z-index for BasicDialogs (defaults to 9000)
36482          * @type Number The z-index value
36483          */
36484         zseed : 9000,
36485
36486         // private
36487         register : function(dlg){
36488             list[dlg.id] = dlg;
36489             accessList.push(dlg);
36490         },
36491
36492         // private
36493         unregister : function(dlg){
36494             delete list[dlg.id];
36495             var i=0;
36496             var len=0;
36497             if(!accessList.indexOf){
36498                 for(  i = 0, len = accessList.length; i < len; i++){
36499                     if(accessList[i] == dlg){
36500                         accessList.splice(i, 1);
36501                         return;
36502                     }
36503                 }
36504             }else{
36505                  i = accessList.indexOf(dlg);
36506                 if(i != -1){
36507                     accessList.splice(i, 1);
36508                 }
36509             }
36510         },
36511
36512         /**
36513          * Gets a registered dialog by id
36514          * @param {String/Object} id The id of the dialog or a dialog
36515          * @return {Roo.BasicDialog} this
36516          */
36517         get : function(id){
36518             return typeof id == "object" ? id : list[id];
36519         },
36520
36521         /**
36522          * Brings the specified dialog to the front
36523          * @param {String/Object} dlg The id of the dialog or a dialog
36524          * @return {Roo.BasicDialog} this
36525          */
36526         bringToFront : function(dlg){
36527             dlg = this.get(dlg);
36528             if(dlg != front){
36529                 front = dlg;
36530                 dlg._lastAccess = new Date().getTime();
36531                 orderDialogs();
36532             }
36533             return dlg;
36534         },
36535
36536         /**
36537          * Sends the specified dialog to the back
36538          * @param {String/Object} dlg The id of the dialog or a dialog
36539          * @return {Roo.BasicDialog} this
36540          */
36541         sendToBack : function(dlg){
36542             dlg = this.get(dlg);
36543             dlg._lastAccess = -(new Date().getTime());
36544             orderDialogs();
36545             return dlg;
36546         },
36547
36548         /**
36549          * Hides all dialogs
36550          */
36551         hideAll : function(){
36552             for(var id in list){
36553                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
36554                     list[id].hide();
36555                 }
36556             }
36557         }
36558     };
36559 }();
36560
36561 /**
36562  * @class Roo.LayoutDialog
36563  * @extends Roo.BasicDialog
36564  * @children Roo.panel.Content
36565  * @parent builder none
36566  * Dialog which provides adjustments for working with a layout in a Dialog.
36567  * Add your necessary layout config options to the dialog's config.<br>
36568  * Example usage (including a nested layout):
36569  * <pre><code>
36570 if(!dialog){
36571     dialog = new Roo.LayoutDialog("download-dlg", {
36572         modal: true,
36573         width:600,
36574         height:450,
36575         shadow:true,
36576         minWidth:500,
36577         minHeight:350,
36578         autoTabs:true,
36579         proxyDrag:true,
36580         // layout config merges with the dialog config
36581         center:{
36582             tabPosition: "top",
36583             alwaysShowTabs: true
36584         }
36585     });
36586     dialog.addKeyListener(27, dialog.hide, dialog);
36587     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
36588     dialog.addButton("Build It!", this.getDownload, this);
36589
36590     // we can even add nested layouts
36591     var innerLayout = new Roo.layout.Border("dl-inner", {
36592         east: {
36593             initialSize: 200,
36594             autoScroll:true,
36595             split:true
36596         },
36597         center: {
36598             autoScroll:true
36599         }
36600     });
36601     innerLayout.beginUpdate();
36602     innerLayout.add("east", new Roo.panel.Content("dl-details"));
36603     innerLayout.add("center", new Roo.panel.Content("selection-panel"));
36604     innerLayout.endUpdate(true);
36605
36606     var layout = dialog.getLayout();
36607     layout.beginUpdate();
36608     layout.add("center", new Roo.panel.Content("standard-panel",
36609                         {title: "Download the Source", fitToFrame:true}));
36610     layout.add("center", new Roo.panel.NestedLayout(innerLayout,
36611                {title: "Build your own roo.js"}));
36612     layout.getRegion("center").showPanel(sp);
36613     layout.endUpdate();
36614 }
36615 </code></pre>
36616     * @constructor
36617     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
36618     * @param {Object} config configuration options
36619   */
36620 Roo.LayoutDialog = function(el, cfg){
36621     
36622     var config=  cfg;
36623     if (typeof(cfg) == 'undefined') {
36624         config = Roo.apply({}, el);
36625         // not sure why we use documentElement here.. - it should always be body.
36626         // IE7 borks horribly if we use documentElement.
36627         // webkit also does not like documentElement - it creates a body element...
36628         el = Roo.get( document.body || document.documentElement ).createChild();
36629         //config.autoCreate = true;
36630     }
36631     
36632     
36633     config.autoTabs = false;
36634     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
36635     this.body.setStyle({overflow:"hidden", position:"relative"});
36636     this.layout = new Roo.layout.Border(this.body.dom, config);
36637     this.layout.monitorWindowResize = false;
36638     this.el.addClass("x-dlg-auto-layout");
36639     // fix case when center region overwrites center function
36640     this.center = Roo.BasicDialog.prototype.center;
36641     this.on("show", this.layout.layout, this.layout, true);
36642     if (config.items) {
36643         var xitems = config.items;
36644         delete config.items;
36645         Roo.each(xitems, this.addxtype, this);
36646     }
36647     
36648     
36649 };
36650 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
36651     
36652     
36653     /**
36654      * @cfg {Roo.layout.Region} east  
36655      */
36656     /**
36657      * @cfg {Roo.layout.Region} west
36658      */
36659     /**
36660      * @cfg {Roo.layout.Region} south
36661      */
36662     /**
36663      * @cfg {Roo.layout.Region} north
36664      */
36665     /**
36666      * @cfg {Roo.layout.Region} center
36667      */
36668     /**
36669      * @cfg {Roo.Button} buttons[]  Bottom buttons..
36670      */
36671     
36672     
36673     /**
36674      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
36675      * @deprecated
36676      */
36677     endUpdate : function(){
36678         this.layout.endUpdate();
36679     },
36680
36681     /**
36682      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
36683      *  @deprecated
36684      */
36685     beginUpdate : function(){
36686         this.layout.beginUpdate();
36687     },
36688
36689     /**
36690      * Get the BorderLayout for this dialog
36691      * @return {Roo.layout.Border}
36692      */
36693     getLayout : function(){
36694         return this.layout;
36695     },
36696
36697     showEl : function(){
36698         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
36699         if(Roo.isIE7){
36700             this.layout.layout();
36701         }
36702     },
36703
36704     // private
36705     // Use the syncHeightBeforeShow config option to control this automatically
36706     syncBodyHeight : function(){
36707         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
36708         if(this.layout){this.layout.layout();}
36709     },
36710     
36711       /**
36712      * Add an xtype element (actually adds to the layout.)
36713      * @return {Object} xdata xtype object data.
36714      */
36715     
36716     addxtype : function(c) {
36717         return this.layout.addxtype(c);
36718     }
36719 });/*
36720  * Based on:
36721  * Ext JS Library 1.1.1
36722  * Copyright(c) 2006-2007, Ext JS, LLC.
36723  *
36724  * Originally Released Under LGPL - original licence link has changed is not relivant.
36725  *
36726  * Fork - LGPL
36727  * <script type="text/javascript">
36728  */
36729  
36730 /**
36731  * @class Roo.MessageBox
36732  * @static
36733  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
36734  * Example usage:
36735  *<pre><code>
36736 // Basic alert:
36737 Roo.Msg.alert('Status', 'Changes saved successfully.');
36738
36739 // Prompt for user data:
36740 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
36741     if (btn == 'ok'){
36742         // process text value...
36743     }
36744 });
36745
36746 // Show a dialog using config options:
36747 Roo.Msg.show({
36748    title:'Save Changes?',
36749    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
36750    buttons: Roo.Msg.YESNOCANCEL,
36751    fn: processResult,
36752    animEl: 'elId'
36753 });
36754 </code></pre>
36755  * @static
36756  */
36757 Roo.MessageBox = function(){
36758     var dlg, opt, mask, waitTimer;
36759     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
36760     var buttons, activeTextEl, bwidth;
36761
36762     // private
36763     var handleButton = function(button){
36764         dlg.hide();
36765         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
36766     };
36767
36768     // private
36769     var handleHide = function(){
36770         if(opt && opt.cls){
36771             dlg.el.removeClass(opt.cls);
36772         }
36773         if(waitTimer){
36774             Roo.TaskMgr.stop(waitTimer);
36775             waitTimer = null;
36776         }
36777     };
36778
36779     // private
36780     var updateButtons = function(b){
36781         var width = 0;
36782         if(!b){
36783             buttons["ok"].hide();
36784             buttons["cancel"].hide();
36785             buttons["yes"].hide();
36786             buttons["no"].hide();
36787             dlg.footer.dom.style.display = 'none';
36788             return width;
36789         }
36790         dlg.footer.dom.style.display = '';
36791         for(var k in buttons){
36792             if(typeof buttons[k] != "function"){
36793                 if(b[k]){
36794                     buttons[k].show();
36795                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
36796                     width += buttons[k].el.getWidth()+15;
36797                 }else{
36798                     buttons[k].hide();
36799                 }
36800             }
36801         }
36802         return width;
36803     };
36804
36805     // private
36806     var handleEsc = function(d, k, e){
36807         if(opt && opt.closable !== false){
36808             dlg.hide();
36809         }
36810         if(e){
36811             e.stopEvent();
36812         }
36813     };
36814
36815     return {
36816         /**
36817          * Returns a reference to the underlying {@link Roo.BasicDialog} element
36818          * @return {Roo.BasicDialog} The BasicDialog element
36819          */
36820         getDialog : function(){
36821            if(!dlg){
36822                 dlg = new Roo.BasicDialog("x-msg-box", {
36823                     autoCreate : true,
36824                     shadow: true,
36825                     draggable: true,
36826                     resizable:false,
36827                     constraintoviewport:false,
36828                     fixedcenter:true,
36829                     collapsible : false,
36830                     shim:true,
36831                     modal: true,
36832                     width:400, height:100,
36833                     buttonAlign:"center",
36834                     closeClick : function(){
36835                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
36836                             handleButton("no");
36837                         }else{
36838                             handleButton("cancel");
36839                         }
36840                     }
36841                 });
36842               
36843                 dlg.on("hide", handleHide);
36844                 mask = dlg.mask;
36845                 dlg.addKeyListener(27, handleEsc);
36846                 buttons = {};
36847                 var bt = this.buttonText;
36848                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
36849                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
36850                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
36851                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
36852                 bodyEl = dlg.body.createChild({
36853
36854                     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>'
36855                 });
36856                 msgEl = bodyEl.dom.firstChild;
36857                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
36858                 textboxEl.enableDisplayMode();
36859                 textboxEl.addKeyListener([10,13], function(){
36860                     if(dlg.isVisible() && opt && opt.buttons){
36861                         if(opt.buttons.ok){
36862                             handleButton("ok");
36863                         }else if(opt.buttons.yes){
36864                             handleButton("yes");
36865                         }
36866                     }
36867                 });
36868                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
36869                 textareaEl.enableDisplayMode();
36870                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
36871                 progressEl.enableDisplayMode();
36872                 var pf = progressEl.dom.firstChild;
36873                 if (pf) {
36874                     pp = Roo.get(pf.firstChild);
36875                     pp.setHeight(pf.offsetHeight);
36876                 }
36877                 
36878             }
36879             return dlg;
36880         },
36881
36882         /**
36883          * Updates the message box body text
36884          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
36885          * the XHTML-compliant non-breaking space character '&amp;#160;')
36886          * @return {Roo.MessageBox} This message box
36887          */
36888         updateText : function(text){
36889             if(!dlg.isVisible() && !opt.width){
36890                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
36891             }
36892             msgEl.innerHTML = text || '&#160;';
36893       
36894             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
36895             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
36896             var w = Math.max(
36897                     Math.min(opt.width || cw , this.maxWidth), 
36898                     Math.max(opt.minWidth || this.minWidth, bwidth)
36899             );
36900             if(opt.prompt){
36901                 activeTextEl.setWidth(w);
36902             }
36903             if(dlg.isVisible()){
36904                 dlg.fixedcenter = false;
36905             }
36906             // to big, make it scroll. = But as usual stupid IE does not support
36907             // !important..
36908             
36909             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
36910                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
36911                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
36912             } else {
36913                 bodyEl.dom.style.height = '';
36914                 bodyEl.dom.style.overflowY = '';
36915             }
36916             if (cw > w) {
36917                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
36918             } else {
36919                 bodyEl.dom.style.overflowX = '';
36920             }
36921             
36922             dlg.setContentSize(w, bodyEl.getHeight());
36923             if(dlg.isVisible()){
36924                 dlg.fixedcenter = true;
36925             }
36926             return this;
36927         },
36928
36929         /**
36930          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
36931          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
36932          * @param {Number} value Any number between 0 and 1 (e.g., .5)
36933          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
36934          * @return {Roo.MessageBox} This message box
36935          */
36936         updateProgress : function(value, text){
36937             if(text){
36938                 this.updateText(text);
36939             }
36940             if (pp) { // weird bug on my firefox - for some reason this is not defined
36941                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
36942             }
36943             return this;
36944         },        
36945
36946         /**
36947          * Returns true if the message box is currently displayed
36948          * @return {Boolean} True if the message box is visible, else false
36949          */
36950         isVisible : function(){
36951             return dlg && dlg.isVisible();  
36952         },
36953
36954         /**
36955          * Hides the message box if it is displayed
36956          */
36957         hide : function(){
36958             if(this.isVisible()){
36959                 dlg.hide();
36960             }  
36961         },
36962
36963         /**
36964          * Displays a new message box, or reinitializes an existing message box, based on the config options
36965          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
36966          * The following config object properties are supported:
36967          * <pre>
36968 Property    Type             Description
36969 ----------  ---------------  ------------------------------------------------------------------------------------
36970 animEl            String/Element   An id or Element from which the message box should animate as it opens and
36971                                    closes (defaults to undefined)
36972 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
36973                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
36974 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
36975                                    progress and wait dialogs will ignore this property and always hide the
36976                                    close button as they can only be closed programmatically.
36977 cls               String           A custom CSS class to apply to the message box element
36978 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
36979                                    displayed (defaults to 75)
36980 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
36981                                    function will be btn (the name of the button that was clicked, if applicable,
36982                                    e.g. "ok"), and text (the value of the active text field, if applicable).
36983                                    Progress and wait dialogs will ignore this option since they do not respond to
36984                                    user actions and can only be closed programmatically, so any required function
36985                                    should be called by the same code after it closes the dialog.
36986 icon              String           A CSS class that provides a background image to be used as an icon for
36987                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
36988 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
36989 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
36990 modal             Boolean          False to allow user interaction with the page while the message box is
36991                                    displayed (defaults to true)
36992 msg               String           A string that will replace the existing message box body text (defaults
36993                                    to the XHTML-compliant non-breaking space character '&#160;')
36994 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
36995 progress          Boolean          True to display a progress bar (defaults to false)
36996 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
36997 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
36998 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
36999 title             String           The title text
37000 value             String           The string value to set into the active textbox element if displayed
37001 wait              Boolean          True to display a progress bar (defaults to false)
37002 width             Number           The width of the dialog in pixels
37003 </pre>
37004          *
37005          * Example usage:
37006          * <pre><code>
37007 Roo.Msg.show({
37008    title: 'Address',
37009    msg: 'Please enter your address:',
37010    width: 300,
37011    buttons: Roo.MessageBox.OKCANCEL,
37012    multiline: true,
37013    fn: saveAddress,
37014    animEl: 'addAddressBtn'
37015 });
37016 </code></pre>
37017          * @param {Object} config Configuration options
37018          * @return {Roo.MessageBox} This message box
37019          */
37020         show : function(options)
37021         {
37022             
37023             // this causes nightmares if you show one dialog after another
37024             // especially on callbacks..
37025              
37026             if(this.isVisible()){
37027                 
37028                 this.hide();
37029                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
37030                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
37031                 Roo.log("New Dialog Message:" +  options.msg )
37032                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
37033                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
37034                 
37035             }
37036             var d = this.getDialog();
37037             opt = options;
37038             d.setTitle(opt.title || "&#160;");
37039             d.close.setDisplayed(opt.closable !== false);
37040             activeTextEl = textboxEl;
37041             opt.prompt = opt.prompt || (opt.multiline ? true : false);
37042             if(opt.prompt){
37043                 if(opt.multiline){
37044                     textboxEl.hide();
37045                     textareaEl.show();
37046                     textareaEl.setHeight(typeof opt.multiline == "number" ?
37047                         opt.multiline : this.defaultTextHeight);
37048                     activeTextEl = textareaEl;
37049                 }else{
37050                     textboxEl.show();
37051                     textareaEl.hide();
37052                 }
37053             }else{
37054                 textboxEl.hide();
37055                 textareaEl.hide();
37056             }
37057             progressEl.setDisplayed(opt.progress === true);
37058             this.updateProgress(0);
37059             activeTextEl.dom.value = opt.value || "";
37060             if(opt.prompt){
37061                 dlg.setDefaultButton(activeTextEl);
37062             }else{
37063                 var bs = opt.buttons;
37064                 var db = null;
37065                 if(bs && bs.ok){
37066                     db = buttons["ok"];
37067                 }else if(bs && bs.yes){
37068                     db = buttons["yes"];
37069                 }
37070                 dlg.setDefaultButton(db);
37071             }
37072             bwidth = updateButtons(opt.buttons);
37073             this.updateText(opt.msg);
37074             if(opt.cls){
37075                 d.el.addClass(opt.cls);
37076             }
37077             d.proxyDrag = opt.proxyDrag === true;
37078             d.modal = opt.modal !== false;
37079             d.mask = opt.modal !== false ? mask : false;
37080             if(!d.isVisible()){
37081                 // force it to the end of the z-index stack so it gets a cursor in FF
37082                 document.body.appendChild(dlg.el.dom);
37083                 d.animateTarget = null;
37084                 d.show(options.animEl);
37085             }
37086             dlg.toFront();
37087             return this;
37088         },
37089
37090         /**
37091          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
37092          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
37093          * and closing the message box when the process is complete.
37094          * @param {String} title The title bar text
37095          * @param {String} msg The message box body text
37096          * @return {Roo.MessageBox} This message box
37097          */
37098         progress : function(title, msg){
37099             this.show({
37100                 title : title,
37101                 msg : msg,
37102                 buttons: false,
37103                 progress:true,
37104                 closable:false,
37105                 minWidth: this.minProgressWidth,
37106                 modal : true
37107             });
37108             return this;
37109         },
37110
37111         /**
37112          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
37113          * If a callback function is passed it will be called after the user clicks the button, and the
37114          * id of the button that was clicked will be passed as the only parameter to the callback
37115          * (could also be the top-right close button).
37116          * @param {String} title The title bar text
37117          * @param {String} msg The message box body text
37118          * @param {Function} fn (optional) The callback function invoked after the message box is closed
37119          * @param {Object} scope (optional) The scope of the callback function
37120          * @return {Roo.MessageBox} This message box
37121          */
37122         alert : function(title, msg, fn, scope){
37123             this.show({
37124                 title : title,
37125                 msg : msg,
37126                 buttons: this.OK,
37127                 fn: fn,
37128                 scope : scope,
37129                 modal : true
37130             });
37131             return this;
37132         },
37133
37134         /**
37135          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
37136          * interaction while waiting for a long-running process to complete that does not have defined intervals.
37137          * You are responsible for closing the message box when the process is complete.
37138          * @param {String} msg The message box body text
37139          * @param {String} title (optional) The title bar text
37140          * @return {Roo.MessageBox} This message box
37141          */
37142         wait : function(msg, title){
37143             this.show({
37144                 title : title,
37145                 msg : msg,
37146                 buttons: false,
37147                 closable:false,
37148                 progress:true,
37149                 modal:true,
37150                 width:300,
37151                 wait:true
37152             });
37153             waitTimer = Roo.TaskMgr.start({
37154                 run: function(i){
37155                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
37156                 },
37157                 interval: 1000
37158             });
37159             return this;
37160         },
37161
37162         /**
37163          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
37164          * If a callback function is passed it will be called after the user clicks either button, and the id of the
37165          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
37166          * @param {String} title The title bar text
37167          * @param {String} msg The message box body text
37168          * @param {Function} fn (optional) The callback function invoked after the message box is closed
37169          * @param {Object} scope (optional) The scope of the callback function
37170          * @return {Roo.MessageBox} This message box
37171          */
37172         confirm : function(title, msg, fn, scope){
37173             this.show({
37174                 title : title,
37175                 msg : msg,
37176                 buttons: this.YESNO,
37177                 fn: fn,
37178                 scope : scope,
37179                 modal : true
37180             });
37181             return this;
37182         },
37183
37184         /**
37185          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
37186          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
37187          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
37188          * (could also be the top-right close button) and the text that was entered will be passed as the two
37189          * parameters to the callback.
37190          * @param {String} title The title bar text
37191          * @param {String} msg The message box body text
37192          * @param {Function} fn (optional) The callback function invoked after the message box is closed
37193          * @param {Object} scope (optional) The scope of the callback function
37194          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
37195          * property, or the height in pixels to create the textbox (defaults to false / single-line)
37196          * @return {Roo.MessageBox} This message box
37197          */
37198         prompt : function(title, msg, fn, scope, multiline){
37199             this.show({
37200                 title : title,
37201                 msg : msg,
37202                 buttons: this.OKCANCEL,
37203                 fn: fn,
37204                 minWidth:250,
37205                 scope : scope,
37206                 prompt:true,
37207                 multiline: multiline,
37208                 modal : true
37209             });
37210             return this;
37211         },
37212
37213         /**
37214          * Button config that displays a single OK button
37215          * @type Object
37216          */
37217         OK : {ok:true},
37218         /**
37219          * Button config that displays Yes and No buttons
37220          * @type Object
37221          */
37222         YESNO : {yes:true, no:true},
37223         /**
37224          * Button config that displays OK and Cancel buttons
37225          * @type Object
37226          */
37227         OKCANCEL : {ok:true, cancel:true},
37228         /**
37229          * Button config that displays Yes, No and Cancel buttons
37230          * @type Object
37231          */
37232         YESNOCANCEL : {yes:true, no:true, cancel:true},
37233
37234         /**
37235          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
37236          * @type Number
37237          */
37238         defaultTextHeight : 75,
37239         /**
37240          * The maximum width in pixels of the message box (defaults to 600)
37241          * @type Number
37242          */
37243         maxWidth : 600,
37244         /**
37245          * The minimum width in pixels of the message box (defaults to 100)
37246          * @type Number
37247          */
37248         minWidth : 100,
37249         /**
37250          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
37251          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
37252          * @type Number
37253          */
37254         minProgressWidth : 250,
37255         /**
37256          * An object containing the default button text strings that can be overriden for localized language support.
37257          * Supported properties are: ok, cancel, yes and no.
37258          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
37259          * @type Object
37260          */
37261         buttonText : {
37262             ok : "OK",
37263             cancel : "Cancel",
37264             yes : "Yes",
37265             no : "No"
37266         }
37267     };
37268 }();
37269
37270 /**
37271  * Shorthand for {@link Roo.MessageBox}
37272  */
37273 Roo.Msg = Roo.MessageBox;/*
37274  * Based on:
37275  * Ext JS Library 1.1.1
37276  * Copyright(c) 2006-2007, Ext JS, LLC.
37277  *
37278  * Originally Released Under LGPL - original licence link has changed is not relivant.
37279  *
37280  * Fork - LGPL
37281  * <script type="text/javascript">
37282  */
37283 /**
37284  * @class Roo.QuickTips
37285  * Provides attractive and customizable tooltips for any element.
37286  * @static
37287  */
37288 Roo.QuickTips = function(){
37289     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
37290     var ce, bd, xy, dd;
37291     var visible = false, disabled = true, inited = false;
37292     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
37293     
37294     var onOver = function(e){
37295         if(disabled){
37296             return;
37297         }
37298         var t = e.getTarget();
37299         if(!t || t.nodeType !== 1 || t == document || t == document.body){
37300             return;
37301         }
37302         if(ce && t == ce.el){
37303             clearTimeout(hideProc);
37304             return;
37305         }
37306         if(t && tagEls[t.id]){
37307             tagEls[t.id].el = t;
37308             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
37309             return;
37310         }
37311         var ttp, et = Roo.fly(t);
37312         var ns = cfg.namespace;
37313         if(tm.interceptTitles && t.title){
37314             ttp = t.title;
37315             t.qtip = ttp;
37316             t.removeAttribute("title");
37317             e.preventDefault();
37318         }else{
37319             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
37320         }
37321         if(ttp){
37322             showProc = show.defer(tm.showDelay, tm, [{
37323                 el: t, 
37324                 text: ttp.replace(/\\n/g,'<br/>'),
37325                 width: et.getAttributeNS(ns, cfg.width),
37326                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
37327                 title: et.getAttributeNS(ns, cfg.title),
37328                     cls: et.getAttributeNS(ns, cfg.cls)
37329             }]);
37330         }
37331     };
37332     
37333     var onOut = function(e){
37334         clearTimeout(showProc);
37335         var t = e.getTarget();
37336         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
37337             hideProc = setTimeout(hide, tm.hideDelay);
37338         }
37339     };
37340     
37341     var onMove = function(e){
37342         if(disabled){
37343             return;
37344         }
37345         xy = e.getXY();
37346         xy[1] += 18;
37347         if(tm.trackMouse && ce){
37348             el.setXY(xy);
37349         }
37350     };
37351     
37352     var onDown = function(e){
37353         clearTimeout(showProc);
37354         clearTimeout(hideProc);
37355         if(!e.within(el)){
37356             if(tm.hideOnClick){
37357                 hide();
37358                 tm.disable();
37359                 tm.enable.defer(100, tm);
37360             }
37361         }
37362     };
37363     
37364     var getPad = function(){
37365         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
37366     };
37367
37368     var show = function(o){
37369         if(disabled){
37370             return;
37371         }
37372         clearTimeout(dismissProc);
37373         ce = o;
37374         if(removeCls){ // in case manually hidden
37375             el.removeClass(removeCls);
37376             removeCls = null;
37377         }
37378         if(ce.cls){
37379             el.addClass(ce.cls);
37380             removeCls = ce.cls;
37381         }
37382         if(ce.title){
37383             tipTitle.update(ce.title);
37384             tipTitle.show();
37385         }else{
37386             tipTitle.update('');
37387             tipTitle.hide();
37388         }
37389         el.dom.style.width  = tm.maxWidth+'px';
37390         //tipBody.dom.style.width = '';
37391         tipBodyText.update(o.text);
37392         var p = getPad(), w = ce.width;
37393         if(!w){
37394             var td = tipBodyText.dom;
37395             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
37396             if(aw > tm.maxWidth){
37397                 w = tm.maxWidth;
37398             }else if(aw < tm.minWidth){
37399                 w = tm.minWidth;
37400             }else{
37401                 w = aw;
37402             }
37403         }
37404         //tipBody.setWidth(w);
37405         el.setWidth(parseInt(w, 10) + p);
37406         if(ce.autoHide === false){
37407             close.setDisplayed(true);
37408             if(dd){
37409                 dd.unlock();
37410             }
37411         }else{
37412             close.setDisplayed(false);
37413             if(dd){
37414                 dd.lock();
37415             }
37416         }
37417         if(xy){
37418             el.avoidY = xy[1]-18;
37419             el.setXY(xy);
37420         }
37421         if(tm.animate){
37422             el.setOpacity(.1);
37423             el.setStyle("visibility", "visible");
37424             el.fadeIn({callback: afterShow});
37425         }else{
37426             afterShow();
37427         }
37428     };
37429     
37430     var afterShow = function(){
37431         if(ce){
37432             el.show();
37433             esc.enable();
37434             if(tm.autoDismiss && ce.autoHide !== false){
37435                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
37436             }
37437         }
37438     };
37439     
37440     var hide = function(noanim){
37441         clearTimeout(dismissProc);
37442         clearTimeout(hideProc);
37443         ce = null;
37444         if(el.isVisible()){
37445             esc.disable();
37446             if(noanim !== true && tm.animate){
37447                 el.fadeOut({callback: afterHide});
37448             }else{
37449                 afterHide();
37450             } 
37451         }
37452     };
37453     
37454     var afterHide = function(){
37455         el.hide();
37456         if(removeCls){
37457             el.removeClass(removeCls);
37458             removeCls = null;
37459         }
37460     };
37461     
37462     return {
37463         /**
37464         * @cfg {Number} minWidth
37465         * The minimum width of the quick tip (defaults to 40)
37466         */
37467        minWidth : 40,
37468         /**
37469         * @cfg {Number} maxWidth
37470         * The maximum width of the quick tip (defaults to 300)
37471         */
37472        maxWidth : 300,
37473         /**
37474         * @cfg {Boolean} interceptTitles
37475         * True to automatically use the element's DOM title value if available (defaults to false)
37476         */
37477        interceptTitles : false,
37478         /**
37479         * @cfg {Boolean} trackMouse
37480         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
37481         */
37482        trackMouse : false,
37483         /**
37484         * @cfg {Boolean} hideOnClick
37485         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
37486         */
37487        hideOnClick : true,
37488         /**
37489         * @cfg {Number} showDelay
37490         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
37491         */
37492        showDelay : 500,
37493         /**
37494         * @cfg {Number} hideDelay
37495         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
37496         */
37497        hideDelay : 200,
37498         /**
37499         * @cfg {Boolean} autoHide
37500         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
37501         * Used in conjunction with hideDelay.
37502         */
37503        autoHide : true,
37504         /**
37505         * @cfg {Boolean}
37506         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
37507         * (defaults to true).  Used in conjunction with autoDismissDelay.
37508         */
37509        autoDismiss : true,
37510         /**
37511         * @cfg {Number}
37512         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
37513         */
37514        autoDismissDelay : 5000,
37515        /**
37516         * @cfg {Boolean} animate
37517         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
37518         */
37519        animate : false,
37520
37521        /**
37522         * @cfg {String} title
37523         * Title text to display (defaults to '').  This can be any valid HTML markup.
37524         */
37525         title: '',
37526        /**
37527         * @cfg {String} text
37528         * Body text to display (defaults to '').  This can be any valid HTML markup.
37529         */
37530         text : '',
37531        /**
37532         * @cfg {String} cls
37533         * A CSS class to apply to the base quick tip element (defaults to '').
37534         */
37535         cls : '',
37536        /**
37537         * @cfg {Number} width
37538         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
37539         * minWidth or maxWidth.
37540         */
37541         width : null,
37542
37543     /**
37544      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
37545      * or display QuickTips in a page.
37546      */
37547        init : function(){
37548           tm = Roo.QuickTips;
37549           cfg = tm.tagConfig;
37550           if(!inited){
37551               if(!Roo.isReady){ // allow calling of init() before onReady
37552                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
37553                   return;
37554               }
37555               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
37556               el.fxDefaults = {stopFx: true};
37557               // maximum custom styling
37558               //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>');
37559               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>');              
37560               tipTitle = el.child('h3');
37561               tipTitle.enableDisplayMode("block");
37562               tipBody = el.child('div.x-tip-bd');
37563               tipBodyText = el.child('div.x-tip-bd-inner');
37564               //bdLeft = el.child('div.x-tip-bd-left');
37565               //bdRight = el.child('div.x-tip-bd-right');
37566               close = el.child('div.x-tip-close');
37567               close.enableDisplayMode("block");
37568               close.on("click", hide);
37569               var d = Roo.get(document);
37570               d.on("mousedown", onDown);
37571               d.on("mouseover", onOver);
37572               d.on("mouseout", onOut);
37573               d.on("mousemove", onMove);
37574               esc = d.addKeyListener(27, hide);
37575               esc.disable();
37576               if(Roo.dd.DD){
37577                   dd = el.initDD("default", null, {
37578                       onDrag : function(){
37579                           el.sync();  
37580                       }
37581                   });
37582                   dd.setHandleElId(tipTitle.id);
37583                   dd.lock();
37584               }
37585               inited = true;
37586           }
37587           this.enable(); 
37588        },
37589
37590     /**
37591      * Configures a new quick tip instance and assigns it to a target element.  The following config options
37592      * are supported:
37593      * <pre>
37594 Property    Type                   Description
37595 ----------  ---------------------  ------------------------------------------------------------------------
37596 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
37597      * </ul>
37598      * @param {Object} config The config object
37599      */
37600        register : function(config){
37601            var cs = config instanceof Array ? config : arguments;
37602            for(var i = 0, len = cs.length; i < len; i++) {
37603                var c = cs[i];
37604                var target = c.target;
37605                if(target){
37606                    if(target instanceof Array){
37607                        for(var j = 0, jlen = target.length; j < jlen; j++){
37608                            tagEls[target[j]] = c;
37609                        }
37610                    }else{
37611                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
37612                    }
37613                }
37614            }
37615        },
37616
37617     /**
37618      * Removes this quick tip from its element and destroys it.
37619      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
37620      */
37621        unregister : function(el){
37622            delete tagEls[Roo.id(el)];
37623        },
37624
37625     /**
37626      * Enable this quick tip.
37627      */
37628        enable : function(){
37629            if(inited && disabled){
37630                locks.pop();
37631                if(locks.length < 1){
37632                    disabled = false;
37633                }
37634            }
37635        },
37636
37637     /**
37638      * Disable this quick tip.
37639      */
37640        disable : function(){
37641           disabled = true;
37642           clearTimeout(showProc);
37643           clearTimeout(hideProc);
37644           clearTimeout(dismissProc);
37645           if(ce){
37646               hide(true);
37647           }
37648           locks.push(1);
37649        },
37650
37651     /**
37652      * Returns true if the quick tip is enabled, else false.
37653      */
37654        isEnabled : function(){
37655             return !disabled;
37656        },
37657
37658         // private
37659        tagConfig : {
37660            namespace : "roo", // was ext?? this may break..
37661            alt_namespace : "ext",
37662            attribute : "qtip",
37663            width : "width",
37664            target : "target",
37665            title : "qtitle",
37666            hide : "hide",
37667            cls : "qclass"
37668        }
37669    };
37670 }();
37671
37672 // backwards compat
37673 Roo.QuickTips.tips = Roo.QuickTips.register;/*
37674  * Based on:
37675  * Ext JS Library 1.1.1
37676  * Copyright(c) 2006-2007, Ext JS, LLC.
37677  *
37678  * Originally Released Under LGPL - original licence link has changed is not relivant.
37679  *
37680  * Fork - LGPL
37681  * <script type="text/javascript">
37682  */
37683  
37684
37685 /**
37686  * @class Roo.tree.TreePanel
37687  * @extends Roo.data.Tree
37688  * @cfg {Roo.tree.TreeNode} root The root node
37689  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
37690  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
37691  * @cfg {Boolean} enableDD true to enable drag and drop
37692  * @cfg {Boolean} enableDrag true to enable just drag
37693  * @cfg {Boolean} enableDrop true to enable just drop
37694  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
37695  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
37696  * @cfg {String} ddGroup The DD group this TreePanel belongs to
37697  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
37698  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
37699  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
37700  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
37701  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
37702  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
37703  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
37704  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
37705  * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
37706  * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
37707  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
37708  * @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>
37709  * @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>
37710  * 
37711  * @constructor
37712  * @param {String/HTMLElement/Element} el The container element
37713  * @param {Object} config
37714  */
37715 Roo.tree.TreePanel = function(el, config){
37716     var root = false;
37717     var loader = false;
37718     if (config.root) {
37719         root = config.root;
37720         delete config.root;
37721     }
37722     if (config.loader) {
37723         loader = config.loader;
37724         delete config.loader;
37725     }
37726     
37727     Roo.apply(this, config);
37728     Roo.tree.TreePanel.superclass.constructor.call(this);
37729     this.el = Roo.get(el);
37730     this.el.addClass('x-tree');
37731     //console.log(root);
37732     if (root) {
37733         this.setRootNode( Roo.factory(root, Roo.tree));
37734     }
37735     if (loader) {
37736         this.loader = Roo.factory(loader, Roo.tree);
37737     }
37738    /**
37739     * Read-only. The id of the container element becomes this TreePanel's id.
37740     */
37741     this.id = this.el.id;
37742     this.addEvents({
37743         /**
37744         * @event beforeload
37745         * Fires before a node is loaded, return false to cancel
37746         * @param {Node} node The node being loaded
37747         */
37748         "beforeload" : true,
37749         /**
37750         * @event load
37751         * Fires when a node is loaded
37752         * @param {Node} node The node that was loaded
37753         */
37754         "load" : true,
37755         /**
37756         * @event textchange
37757         * Fires when the text for a node is changed
37758         * @param {Node} node The node
37759         * @param {String} text The new text
37760         * @param {String} oldText The old text
37761         */
37762         "textchange" : true,
37763         /**
37764         * @event beforeexpand
37765         * Fires before a node is expanded, return false to cancel.
37766         * @param {Node} node The node
37767         * @param {Boolean} deep
37768         * @param {Boolean} anim
37769         */
37770         "beforeexpand" : true,
37771         /**
37772         * @event beforecollapse
37773         * Fires before a node is collapsed, return false to cancel.
37774         * @param {Node} node The node
37775         * @param {Boolean} deep
37776         * @param {Boolean} anim
37777         */
37778         "beforecollapse" : true,
37779         /**
37780         * @event expand
37781         * Fires when a node is expanded
37782         * @param {Node} node The node
37783         */
37784         "expand" : true,
37785         /**
37786         * @event disabledchange
37787         * Fires when the disabled status of a node changes
37788         * @param {Node} node The node
37789         * @param {Boolean} disabled
37790         */
37791         "disabledchange" : true,
37792         /**
37793         * @event collapse
37794         * Fires when a node is collapsed
37795         * @param {Node} node The node
37796         */
37797         "collapse" : true,
37798         /**
37799         * @event beforeclick
37800         * Fires before click processing on a node. Return false to cancel the default action.
37801         * @param {Node} node The node
37802         * @param {Roo.EventObject} e The event object
37803         */
37804         "beforeclick":true,
37805         /**
37806         * @event checkchange
37807         * Fires when a node with a checkbox's checked property changes
37808         * @param {Node} this This node
37809         * @param {Boolean} checked
37810         */
37811         "checkchange":true,
37812         /**
37813         * @event click
37814         * Fires when a node is clicked
37815         * @param {Node} node The node
37816         * @param {Roo.EventObject} e The event object
37817         */
37818         "click":true,
37819         /**
37820         * @event dblclick
37821         * Fires when a node is double clicked
37822         * @param {Node} node The node
37823         * @param {Roo.EventObject} e The event object
37824         */
37825         "dblclick":true,
37826         /**
37827         * @event contextmenu
37828         * Fires when a node is right clicked
37829         * @param {Node} node The node
37830         * @param {Roo.EventObject} e The event object
37831         */
37832         "contextmenu":true,
37833         /**
37834         * @event beforechildrenrendered
37835         * Fires right before the child nodes for a node are rendered
37836         * @param {Node} node The node
37837         */
37838         "beforechildrenrendered":true,
37839         /**
37840         * @event startdrag
37841         * Fires when a node starts being dragged
37842         * @param {Roo.tree.TreePanel} this
37843         * @param {Roo.tree.TreeNode} node
37844         * @param {event} e The raw browser event
37845         */ 
37846        "startdrag" : true,
37847        /**
37848         * @event enddrag
37849         * Fires when a drag operation is complete
37850         * @param {Roo.tree.TreePanel} this
37851         * @param {Roo.tree.TreeNode} node
37852         * @param {event} e The raw browser event
37853         */
37854        "enddrag" : true,
37855        /**
37856         * @event dragdrop
37857         * Fires when a dragged node is dropped on a valid DD target
37858         * @param {Roo.tree.TreePanel} this
37859         * @param {Roo.tree.TreeNode} node
37860         * @param {DD} dd The dd it was dropped on
37861         * @param {event} e The raw browser event
37862         */
37863        "dragdrop" : true,
37864        /**
37865         * @event beforenodedrop
37866         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
37867         * passed to handlers has the following properties:<br />
37868         * <ul style="padding:5px;padding-left:16px;">
37869         * <li>tree - The TreePanel</li>
37870         * <li>target - The node being targeted for the drop</li>
37871         * <li>data - The drag data from the drag source</li>
37872         * <li>point - The point of the drop - append, above or below</li>
37873         * <li>source - The drag source</li>
37874         * <li>rawEvent - Raw mouse event</li>
37875         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
37876         * to be inserted by setting them on this object.</li>
37877         * <li>cancel - Set this to true to cancel the drop.</li>
37878         * </ul>
37879         * @param {Object} dropEvent
37880         */
37881        "beforenodedrop" : true,
37882        /**
37883         * @event nodedrop
37884         * Fires after a DD object is dropped on a node in this tree. The dropEvent
37885         * passed to handlers has the following properties:<br />
37886         * <ul style="padding:5px;padding-left:16px;">
37887         * <li>tree - The TreePanel</li>
37888         * <li>target - The node being targeted for the drop</li>
37889         * <li>data - The drag data from the drag source</li>
37890         * <li>point - The point of the drop - append, above or below</li>
37891         * <li>source - The drag source</li>
37892         * <li>rawEvent - Raw mouse event</li>
37893         * <li>dropNode - Dropped node(s).</li>
37894         * </ul>
37895         * @param {Object} dropEvent
37896         */
37897        "nodedrop" : true,
37898         /**
37899         * @event nodedragover
37900         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
37901         * passed to handlers has the following properties:<br />
37902         * <ul style="padding:5px;padding-left:16px;">
37903         * <li>tree - The TreePanel</li>
37904         * <li>target - The node being targeted for the drop</li>
37905         * <li>data - The drag data from the drag source</li>
37906         * <li>point - The point of the drop - append, above or below</li>
37907         * <li>source - The drag source</li>
37908         * <li>rawEvent - Raw mouse event</li>
37909         * <li>dropNode - Drop node(s) provided by the source.</li>
37910         * <li>cancel - Set this to true to signal drop not allowed.</li>
37911         * </ul>
37912         * @param {Object} dragOverEvent
37913         */
37914        "nodedragover" : true,
37915        /**
37916         * @event appendnode
37917         * Fires when append node to the tree
37918         * @param {Roo.tree.TreePanel} this
37919         * @param {Roo.tree.TreeNode} node
37920         * @param {Number} index The index of the newly appended node
37921         */
37922        "appendnode" : true
37923         
37924     });
37925     if(this.singleExpand){
37926        this.on("beforeexpand", this.restrictExpand, this);
37927     }
37928     if (this.editor) {
37929         this.editor.tree = this;
37930         this.editor = Roo.factory(this.editor, Roo.tree);
37931     }
37932     
37933     if (this.selModel) {
37934         this.selModel = Roo.factory(this.selModel, Roo.tree);
37935     }
37936    
37937 };
37938 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
37939     rootVisible : true,
37940     animate: Roo.enableFx,
37941     lines : true,
37942     enableDD : false,
37943     hlDrop : Roo.enableFx,
37944   
37945     renderer: false,
37946     
37947     rendererTip: false,
37948     // private
37949     restrictExpand : function(node){
37950         var p = node.parentNode;
37951         if(p){
37952             if(p.expandedChild && p.expandedChild.parentNode == p){
37953                 p.expandedChild.collapse();
37954             }
37955             p.expandedChild = node;
37956         }
37957     },
37958
37959     // private override
37960     setRootNode : function(node){
37961         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
37962         if(!this.rootVisible){
37963             node.ui = new Roo.tree.RootTreeNodeUI(node);
37964         }
37965         return node;
37966     },
37967
37968     /**
37969      * Returns the container element for this TreePanel
37970      */
37971     getEl : function(){
37972         return this.el;
37973     },
37974
37975     /**
37976      * Returns the default TreeLoader for this TreePanel
37977      */
37978     getLoader : function(){
37979         return this.loader;
37980     },
37981
37982     /**
37983      * Expand all nodes
37984      */
37985     expandAll : function(){
37986         this.root.expand(true);
37987     },
37988
37989     /**
37990      * Collapse all nodes
37991      */
37992     collapseAll : function(){
37993         this.root.collapse(true);
37994     },
37995
37996     /**
37997      * Returns the selection model used by this TreePanel
37998      */
37999     getSelectionModel : function(){
38000         if(!this.selModel){
38001             this.selModel = new Roo.tree.DefaultSelectionModel();
38002         }
38003         return this.selModel;
38004     },
38005
38006     /**
38007      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
38008      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
38009      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
38010      * @return {Array}
38011      */
38012     getChecked : function(a, startNode){
38013         startNode = startNode || this.root;
38014         var r = [];
38015         var f = function(){
38016             if(this.attributes.checked){
38017                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
38018             }
38019         }
38020         startNode.cascade(f);
38021         return r;
38022     },
38023
38024     /**
38025      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
38026      * @param {String} path
38027      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
38028      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
38029      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
38030      */
38031     expandPath : function(path, attr, callback){
38032         attr = attr || "id";
38033         var keys = path.split(this.pathSeparator);
38034         var curNode = this.root;
38035         if(curNode.attributes[attr] != keys[1]){ // invalid root
38036             if(callback){
38037                 callback(false, null);
38038             }
38039             return;
38040         }
38041         var index = 1;
38042         var f = function(){
38043             if(++index == keys.length){
38044                 if(callback){
38045                     callback(true, curNode);
38046                 }
38047                 return;
38048             }
38049             var c = curNode.findChild(attr, keys[index]);
38050             if(!c){
38051                 if(callback){
38052                     callback(false, curNode);
38053                 }
38054                 return;
38055             }
38056             curNode = c;
38057             c.expand(false, false, f);
38058         };
38059         curNode.expand(false, false, f);
38060     },
38061
38062     /**
38063      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
38064      * @param {String} path
38065      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
38066      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
38067      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
38068      */
38069     selectPath : function(path, attr, callback){
38070         attr = attr || "id";
38071         var keys = path.split(this.pathSeparator);
38072         var v = keys.pop();
38073         if(keys.length > 0){
38074             var f = function(success, node){
38075                 if(success && node){
38076                     var n = node.findChild(attr, v);
38077                     if(n){
38078                         n.select();
38079                         if(callback){
38080                             callback(true, n);
38081                         }
38082                     }else if(callback){
38083                         callback(false, n);
38084                     }
38085                 }else{
38086                     if(callback){
38087                         callback(false, n);
38088                     }
38089                 }
38090             };
38091             this.expandPath(keys.join(this.pathSeparator), attr, f);
38092         }else{
38093             this.root.select();
38094             if(callback){
38095                 callback(true, this.root);
38096             }
38097         }
38098     },
38099
38100     getTreeEl : function(){
38101         return this.el;
38102     },
38103
38104     /**
38105      * Trigger rendering of this TreePanel
38106      */
38107     render : function(){
38108         if (this.innerCt) {
38109             return this; // stop it rendering more than once!!
38110         }
38111         
38112         this.innerCt = this.el.createChild({tag:"ul",
38113                cls:"x-tree-root-ct " +
38114                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
38115
38116         if(this.containerScroll){
38117             Roo.dd.ScrollManager.register(this.el);
38118         }
38119         if((this.enableDD || this.enableDrop) && !this.dropZone){
38120            /**
38121             * The dropZone used by this tree if drop is enabled
38122             * @type Roo.tree.TreeDropZone
38123             */
38124              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
38125                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
38126            });
38127         }
38128         if((this.enableDD || this.enableDrag) && !this.dragZone){
38129            /**
38130             * The dragZone used by this tree if drag is enabled
38131             * @type Roo.tree.TreeDragZone
38132             */
38133             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
38134                ddGroup: this.ddGroup || "TreeDD",
38135                scroll: this.ddScroll
38136            });
38137         }
38138         this.getSelectionModel().init(this);
38139         if (!this.root) {
38140             Roo.log("ROOT not set in tree");
38141             return this;
38142         }
38143         this.root.render();
38144         if(!this.rootVisible){
38145             this.root.renderChildren();
38146         }
38147         return this;
38148     }
38149 });/*
38150  * Based on:
38151  * Ext JS Library 1.1.1
38152  * Copyright(c) 2006-2007, Ext JS, LLC.
38153  *
38154  * Originally Released Under LGPL - original licence link has changed is not relivant.
38155  *
38156  * Fork - LGPL
38157  * <script type="text/javascript">
38158  */
38159  
38160
38161 /**
38162  * @class Roo.tree.DefaultSelectionModel
38163  * @extends Roo.util.Observable
38164  * The default single selection for a TreePanel.
38165  * @param {Object} cfg Configuration
38166  */
38167 Roo.tree.DefaultSelectionModel = function(cfg){
38168    this.selNode = null;
38169    
38170    
38171    
38172    this.addEvents({
38173        /**
38174         * @event selectionchange
38175         * Fires when the selected node changes
38176         * @param {DefaultSelectionModel} this
38177         * @param {TreeNode} node the new selection
38178         */
38179        "selectionchange" : true,
38180
38181        /**
38182         * @event beforeselect
38183         * Fires before the selected node changes, return false to cancel the change
38184         * @param {DefaultSelectionModel} this
38185         * @param {TreeNode} node the new selection
38186         * @param {TreeNode} node the old selection
38187         */
38188        "beforeselect" : true
38189    });
38190    
38191     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
38192 };
38193
38194 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
38195     init : function(tree){
38196         this.tree = tree;
38197         tree.getTreeEl().on("keydown", this.onKeyDown, this);
38198         tree.on("click", this.onNodeClick, this);
38199     },
38200     
38201     onNodeClick : function(node, e){
38202         if (e.ctrlKey && this.selNode == node)  {
38203             this.unselect(node);
38204             return;
38205         }
38206         this.select(node);
38207     },
38208     
38209     /**
38210      * Select a node.
38211      * @param {TreeNode} node The node to select
38212      * @return {TreeNode} The selected node
38213      */
38214     select : function(node){
38215         var last = this.selNode;
38216         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
38217             if(last){
38218                 last.ui.onSelectedChange(false);
38219             }
38220             this.selNode = node;
38221             node.ui.onSelectedChange(true);
38222             this.fireEvent("selectionchange", this, node, last);
38223         }
38224         return node;
38225     },
38226     
38227     /**
38228      * Deselect a node.
38229      * @param {TreeNode} node The node to unselect
38230      */
38231     unselect : function(node){
38232         if(this.selNode == node){
38233             this.clearSelections();
38234         }    
38235     },
38236     
38237     /**
38238      * Clear all selections
38239      */
38240     clearSelections : function(){
38241         var n = this.selNode;
38242         if(n){
38243             n.ui.onSelectedChange(false);
38244             this.selNode = null;
38245             this.fireEvent("selectionchange", this, null);
38246         }
38247         return n;
38248     },
38249     
38250     /**
38251      * Get the selected node
38252      * @return {TreeNode} The selected node
38253      */
38254     getSelectedNode : function(){
38255         return this.selNode;    
38256     },
38257     
38258     /**
38259      * Returns true if the node is selected
38260      * @param {TreeNode} node The node to check
38261      * @return {Boolean}
38262      */
38263     isSelected : function(node){
38264         return this.selNode == node;  
38265     },
38266
38267     /**
38268      * Selects the node above the selected node in the tree, intelligently walking the nodes
38269      * @return TreeNode The new selection
38270      */
38271     selectPrevious : function(){
38272         var s = this.selNode || this.lastSelNode;
38273         if(!s){
38274             return null;
38275         }
38276         var ps = s.previousSibling;
38277         if(ps){
38278             if(!ps.isExpanded() || ps.childNodes.length < 1){
38279                 return this.select(ps);
38280             } else{
38281                 var lc = ps.lastChild;
38282                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
38283                     lc = lc.lastChild;
38284                 }
38285                 return this.select(lc);
38286             }
38287         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
38288             return this.select(s.parentNode);
38289         }
38290         return null;
38291     },
38292
38293     /**
38294      * Selects the node above the selected node in the tree, intelligently walking the nodes
38295      * @return TreeNode The new selection
38296      */
38297     selectNext : function(){
38298         var s = this.selNode || this.lastSelNode;
38299         if(!s){
38300             return null;
38301         }
38302         if(s.firstChild && s.isExpanded()){
38303              return this.select(s.firstChild);
38304          }else if(s.nextSibling){
38305              return this.select(s.nextSibling);
38306          }else if(s.parentNode){
38307             var newS = null;
38308             s.parentNode.bubble(function(){
38309                 if(this.nextSibling){
38310                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
38311                     return false;
38312                 }
38313             });
38314             return newS;
38315          }
38316         return null;
38317     },
38318
38319     onKeyDown : function(e){
38320         var s = this.selNode || this.lastSelNode;
38321         // undesirable, but required
38322         var sm = this;
38323         if(!s){
38324             return;
38325         }
38326         var k = e.getKey();
38327         switch(k){
38328              case e.DOWN:
38329                  e.stopEvent();
38330                  this.selectNext();
38331              break;
38332              case e.UP:
38333                  e.stopEvent();
38334                  this.selectPrevious();
38335              break;
38336              case e.RIGHT:
38337                  e.preventDefault();
38338                  if(s.hasChildNodes()){
38339                      if(!s.isExpanded()){
38340                          s.expand();
38341                      }else if(s.firstChild){
38342                          this.select(s.firstChild, e);
38343                      }
38344                  }
38345              break;
38346              case e.LEFT:
38347                  e.preventDefault();
38348                  if(s.hasChildNodes() && s.isExpanded()){
38349                      s.collapse();
38350                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
38351                      this.select(s.parentNode, e);
38352                  }
38353              break;
38354         };
38355     }
38356 });
38357
38358 /**
38359  * @class Roo.tree.MultiSelectionModel
38360  * @extends Roo.util.Observable
38361  * Multi selection for a TreePanel.
38362  * @param {Object} cfg Configuration
38363  */
38364 Roo.tree.MultiSelectionModel = function(){
38365    this.selNodes = [];
38366    this.selMap = {};
38367    this.addEvents({
38368        /**
38369         * @event selectionchange
38370         * Fires when the selected nodes change
38371         * @param {MultiSelectionModel} this
38372         * @param {Array} nodes Array of the selected nodes
38373         */
38374        "selectionchange" : true
38375    });
38376    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
38377    
38378 };
38379
38380 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
38381     init : function(tree){
38382         this.tree = tree;
38383         tree.getTreeEl().on("keydown", this.onKeyDown, this);
38384         tree.on("click", this.onNodeClick, this);
38385     },
38386     
38387     onNodeClick : function(node, e){
38388         this.select(node, e, e.ctrlKey);
38389     },
38390     
38391     /**
38392      * Select a node.
38393      * @param {TreeNode} node The node to select
38394      * @param {EventObject} e (optional) An event associated with the selection
38395      * @param {Boolean} keepExisting True to retain existing selections
38396      * @return {TreeNode} The selected node
38397      */
38398     select : function(node, e, keepExisting){
38399         if(keepExisting !== true){
38400             this.clearSelections(true);
38401         }
38402         if(this.isSelected(node)){
38403             this.lastSelNode = node;
38404             return node;
38405         }
38406         this.selNodes.push(node);
38407         this.selMap[node.id] = node;
38408         this.lastSelNode = node;
38409         node.ui.onSelectedChange(true);
38410         this.fireEvent("selectionchange", this, this.selNodes);
38411         return node;
38412     },
38413     
38414     /**
38415      * Deselect a node.
38416      * @param {TreeNode} node The node to unselect
38417      */
38418     unselect : function(node){
38419         if(this.selMap[node.id]){
38420             node.ui.onSelectedChange(false);
38421             var sn = this.selNodes;
38422             var index = -1;
38423             if(sn.indexOf){
38424                 index = sn.indexOf(node);
38425             }else{
38426                 for(var i = 0, len = sn.length; i < len; i++){
38427                     if(sn[i] == node){
38428                         index = i;
38429                         break;
38430                     }
38431                 }
38432             }
38433             if(index != -1){
38434                 this.selNodes.splice(index, 1);
38435             }
38436             delete this.selMap[node.id];
38437             this.fireEvent("selectionchange", this, this.selNodes);
38438         }
38439     },
38440     
38441     /**
38442      * Clear all selections
38443      */
38444     clearSelections : function(suppressEvent){
38445         var sn = this.selNodes;
38446         if(sn.length > 0){
38447             for(var i = 0, len = sn.length; i < len; i++){
38448                 sn[i].ui.onSelectedChange(false);
38449             }
38450             this.selNodes = [];
38451             this.selMap = {};
38452             if(suppressEvent !== true){
38453                 this.fireEvent("selectionchange", this, this.selNodes);
38454             }
38455         }
38456     },
38457     
38458     /**
38459      * Returns true if the node is selected
38460      * @param {TreeNode} node The node to check
38461      * @return {Boolean}
38462      */
38463     isSelected : function(node){
38464         return this.selMap[node.id] ? true : false;  
38465     },
38466     
38467     /**
38468      * Returns an array of the selected nodes
38469      * @return {Array}
38470      */
38471     getSelectedNodes : function(){
38472         return this.selNodes;    
38473     },
38474
38475     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
38476
38477     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
38478
38479     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
38480 });/*
38481  * Based on:
38482  * Ext JS Library 1.1.1
38483  * Copyright(c) 2006-2007, Ext JS, LLC.
38484  *
38485  * Originally Released Under LGPL - original licence link has changed is not relivant.
38486  *
38487  * Fork - LGPL
38488  * <script type="text/javascript">
38489  */
38490  
38491 /**
38492  * @class Roo.tree.TreeNode
38493  * @extends Roo.data.Node
38494  * @cfg {String} text The text for this node
38495  * @cfg {Boolean} expanded true to start the node expanded
38496  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
38497  * @cfg {Boolean} allowDrop false if this node cannot be drop on
38498  * @cfg {Boolean} disabled true to start the node disabled
38499  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
38500  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
38501  * @cfg {String} cls A css class to be added to the node
38502  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
38503  * @cfg {String} href URL of the link used for the node (defaults to #)
38504  * @cfg {String} hrefTarget target frame for the link
38505  * @cfg {String} qtip An Ext QuickTip for the node
38506  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
38507  * @cfg {Boolean} singleClickExpand True for single click expand on this node
38508  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
38509  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
38510  * (defaults to undefined with no checkbox rendered)
38511  * @constructor
38512  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
38513  */
38514 Roo.tree.TreeNode = function(attributes){
38515     attributes = attributes || {};
38516     if(typeof attributes == "string"){
38517         attributes = {text: attributes};
38518     }
38519     this.childrenRendered = false;
38520     this.rendered = false;
38521     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
38522     this.expanded = attributes.expanded === true;
38523     this.isTarget = attributes.isTarget !== false;
38524     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
38525     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
38526
38527     /**
38528      * Read-only. The text for this node. To change it use setText().
38529      * @type String
38530      */
38531     this.text = attributes.text;
38532     /**
38533      * True if this node is disabled.
38534      * @type Boolean
38535      */
38536     this.disabled = attributes.disabled === true;
38537
38538     this.addEvents({
38539         /**
38540         * @event textchange
38541         * Fires when the text for this node is changed
38542         * @param {Node} this This node
38543         * @param {String} text The new text
38544         * @param {String} oldText The old text
38545         */
38546         "textchange" : true,
38547         /**
38548         * @event beforeexpand
38549         * Fires before this node is expanded, return false to cancel.
38550         * @param {Node} this This node
38551         * @param {Boolean} deep
38552         * @param {Boolean} anim
38553         */
38554         "beforeexpand" : true,
38555         /**
38556         * @event beforecollapse
38557         * Fires before this node is collapsed, return false to cancel.
38558         * @param {Node} this This node
38559         * @param {Boolean} deep
38560         * @param {Boolean} anim
38561         */
38562         "beforecollapse" : true,
38563         /**
38564         * @event expand
38565         * Fires when this node is expanded
38566         * @param {Node} this This node
38567         */
38568         "expand" : true,
38569         /**
38570         * @event disabledchange
38571         * Fires when the disabled status of this node changes
38572         * @param {Node} this This node
38573         * @param {Boolean} disabled
38574         */
38575         "disabledchange" : true,
38576         /**
38577         * @event collapse
38578         * Fires when this node is collapsed
38579         * @param {Node} this This node
38580         */
38581         "collapse" : true,
38582         /**
38583         * @event beforeclick
38584         * Fires before click processing. Return false to cancel the default action.
38585         * @param {Node} this This node
38586         * @param {Roo.EventObject} e The event object
38587         */
38588         "beforeclick":true,
38589         /**
38590         * @event checkchange
38591         * Fires when a node with a checkbox's checked property changes
38592         * @param {Node} this This node
38593         * @param {Boolean} checked
38594         */
38595         "checkchange":true,
38596         /**
38597         * @event click
38598         * Fires when this node is clicked
38599         * @param {Node} this This node
38600         * @param {Roo.EventObject} e The event object
38601         */
38602         "click":true,
38603         /**
38604         * @event dblclick
38605         * Fires when this node is double clicked
38606         * @param {Node} this This node
38607         * @param {Roo.EventObject} e The event object
38608         */
38609         "dblclick":true,
38610         /**
38611         * @event contextmenu
38612         * Fires when this node is right clicked
38613         * @param {Node} this This node
38614         * @param {Roo.EventObject} e The event object
38615         */
38616         "contextmenu":true,
38617         /**
38618         * @event beforechildrenrendered
38619         * Fires right before the child nodes for this node are rendered
38620         * @param {Node} this This node
38621         */
38622         "beforechildrenrendered":true
38623     });
38624
38625     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
38626
38627     /**
38628      * Read-only. The UI for this node
38629      * @type TreeNodeUI
38630      */
38631     this.ui = new uiClass(this);
38632     
38633     // finally support items[]
38634     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
38635         return;
38636     }
38637     
38638     
38639     Roo.each(this.attributes.items, function(c) {
38640         this.appendChild(Roo.factory(c,Roo.Tree));
38641     }, this);
38642     delete this.attributes.items;
38643     
38644     
38645     
38646 };
38647 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
38648     preventHScroll: true,
38649     /**
38650      * Returns true if this node is expanded
38651      * @return {Boolean}
38652      */
38653     isExpanded : function(){
38654         return this.expanded;
38655     },
38656
38657     /**
38658      * Returns the UI object for this node
38659      * @return {TreeNodeUI}
38660      */
38661     getUI : function(){
38662         return this.ui;
38663     },
38664
38665     // private override
38666     setFirstChild : function(node){
38667         var of = this.firstChild;
38668         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
38669         if(this.childrenRendered && of && node != of){
38670             of.renderIndent(true, true);
38671         }
38672         if(this.rendered){
38673             this.renderIndent(true, true);
38674         }
38675     },
38676
38677     // private override
38678     setLastChild : function(node){
38679         var ol = this.lastChild;
38680         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
38681         if(this.childrenRendered && ol && node != ol){
38682             ol.renderIndent(true, true);
38683         }
38684         if(this.rendered){
38685             this.renderIndent(true, true);
38686         }
38687     },
38688
38689     // these methods are overridden to provide lazy rendering support
38690     // private override
38691     appendChild : function()
38692     {
38693         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
38694         if(node && this.childrenRendered){
38695             node.render();
38696         }
38697         this.ui.updateExpandIcon();
38698         return node;
38699     },
38700
38701     // private override
38702     removeChild : function(node){
38703         this.ownerTree.getSelectionModel().unselect(node);
38704         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
38705         // if it's been rendered remove dom node
38706         if(this.childrenRendered){
38707             node.ui.remove();
38708         }
38709         if(this.childNodes.length < 1){
38710             this.collapse(false, false);
38711         }else{
38712             this.ui.updateExpandIcon();
38713         }
38714         if(!this.firstChild) {
38715             this.childrenRendered = false;
38716         }
38717         return node;
38718     },
38719
38720     // private override
38721     insertBefore : function(node, refNode){
38722         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
38723         if(newNode && refNode && this.childrenRendered){
38724             node.render();
38725         }
38726         this.ui.updateExpandIcon();
38727         return newNode;
38728     },
38729
38730     /**
38731      * Sets the text for this node
38732      * @param {String} text
38733      */
38734     setText : function(text){
38735         var oldText = this.text;
38736         this.text = text;
38737         this.attributes.text = text;
38738         if(this.rendered){ // event without subscribing
38739             this.ui.onTextChange(this, text, oldText);
38740         }
38741         this.fireEvent("textchange", this, text, oldText);
38742     },
38743
38744     /**
38745      * Triggers selection of this node
38746      */
38747     select : function(){
38748         this.getOwnerTree().getSelectionModel().select(this);
38749     },
38750
38751     /**
38752      * Triggers deselection of this node
38753      */
38754     unselect : function(){
38755         this.getOwnerTree().getSelectionModel().unselect(this);
38756     },
38757
38758     /**
38759      * Returns true if this node is selected
38760      * @return {Boolean}
38761      */
38762     isSelected : function(){
38763         return this.getOwnerTree().getSelectionModel().isSelected(this);
38764     },
38765
38766     /**
38767      * Expand this node.
38768      * @param {Boolean} deep (optional) True to expand all children as well
38769      * @param {Boolean} anim (optional) false to cancel the default animation
38770      * @param {Function} callback (optional) A callback to be called when
38771      * expanding this node completes (does not wait for deep expand to complete).
38772      * Called with 1 parameter, this node.
38773      */
38774     expand : function(deep, anim, callback){
38775         if(!this.expanded){
38776             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
38777                 return;
38778             }
38779             if(!this.childrenRendered){
38780                 this.renderChildren();
38781             }
38782             this.expanded = true;
38783             
38784             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
38785                 this.ui.animExpand(function(){
38786                     this.fireEvent("expand", this);
38787                     if(typeof callback == "function"){
38788                         callback(this);
38789                     }
38790                     if(deep === true){
38791                         this.expandChildNodes(true);
38792                     }
38793                 }.createDelegate(this));
38794                 return;
38795             }else{
38796                 this.ui.expand();
38797                 this.fireEvent("expand", this);
38798                 if(typeof callback == "function"){
38799                     callback(this);
38800                 }
38801             }
38802         }else{
38803            if(typeof callback == "function"){
38804                callback(this);
38805            }
38806         }
38807         if(deep === true){
38808             this.expandChildNodes(true);
38809         }
38810     },
38811
38812     isHiddenRoot : function(){
38813         return this.isRoot && !this.getOwnerTree().rootVisible;
38814     },
38815
38816     /**
38817      * Collapse this node.
38818      * @param {Boolean} deep (optional) True to collapse all children as well
38819      * @param {Boolean} anim (optional) false to cancel the default animation
38820      */
38821     collapse : function(deep, anim){
38822         if(this.expanded && !this.isHiddenRoot()){
38823             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
38824                 return;
38825             }
38826             this.expanded = false;
38827             if((this.getOwnerTree().animate && anim !== false) || anim){
38828                 this.ui.animCollapse(function(){
38829                     this.fireEvent("collapse", this);
38830                     if(deep === true){
38831                         this.collapseChildNodes(true);
38832                     }
38833                 }.createDelegate(this));
38834                 return;
38835             }else{
38836                 this.ui.collapse();
38837                 this.fireEvent("collapse", this);
38838             }
38839         }
38840         if(deep === true){
38841             var cs = this.childNodes;
38842             for(var i = 0, len = cs.length; i < len; i++) {
38843                 cs[i].collapse(true, false);
38844             }
38845         }
38846     },
38847
38848     // private
38849     delayedExpand : function(delay){
38850         if(!this.expandProcId){
38851             this.expandProcId = this.expand.defer(delay, this);
38852         }
38853     },
38854
38855     // private
38856     cancelExpand : function(){
38857         if(this.expandProcId){
38858             clearTimeout(this.expandProcId);
38859         }
38860         this.expandProcId = false;
38861     },
38862
38863     /**
38864      * Toggles expanded/collapsed state of the node
38865      */
38866     toggle : function(){
38867         if(this.expanded){
38868             this.collapse();
38869         }else{
38870             this.expand();
38871         }
38872     },
38873
38874     /**
38875      * Ensures all parent nodes are expanded
38876      */
38877     ensureVisible : function(callback){
38878         var tree = this.getOwnerTree();
38879         tree.expandPath(this.parentNode.getPath(), false, function(){
38880             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
38881             Roo.callback(callback);
38882         }.createDelegate(this));
38883     },
38884
38885     /**
38886      * Expand all child nodes
38887      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
38888      */
38889     expandChildNodes : function(deep){
38890         var cs = this.childNodes;
38891         for(var i = 0, len = cs.length; i < len; i++) {
38892                 cs[i].expand(deep);
38893         }
38894     },
38895
38896     /**
38897      * Collapse all child nodes
38898      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
38899      */
38900     collapseChildNodes : function(deep){
38901         var cs = this.childNodes;
38902         for(var i = 0, len = cs.length; i < len; i++) {
38903                 cs[i].collapse(deep);
38904         }
38905     },
38906
38907     /**
38908      * Disables this node
38909      */
38910     disable : function(){
38911         this.disabled = true;
38912         this.unselect();
38913         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
38914             this.ui.onDisableChange(this, true);
38915         }
38916         this.fireEvent("disabledchange", this, true);
38917     },
38918
38919     /**
38920      * Enables this node
38921      */
38922     enable : function(){
38923         this.disabled = false;
38924         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
38925             this.ui.onDisableChange(this, false);
38926         }
38927         this.fireEvent("disabledchange", this, false);
38928     },
38929
38930     // private
38931     renderChildren : function(suppressEvent){
38932         if(suppressEvent !== false){
38933             this.fireEvent("beforechildrenrendered", this);
38934         }
38935         var cs = this.childNodes;
38936         for(var i = 0, len = cs.length; i < len; i++){
38937             cs[i].render(true);
38938         }
38939         this.childrenRendered = true;
38940     },
38941
38942     // private
38943     sort : function(fn, scope){
38944         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
38945         if(this.childrenRendered){
38946             var cs = this.childNodes;
38947             for(var i = 0, len = cs.length; i < len; i++){
38948                 cs[i].render(true);
38949             }
38950         }
38951     },
38952
38953     // private
38954     render : function(bulkRender){
38955         this.ui.render(bulkRender);
38956         if(!this.rendered){
38957             this.rendered = true;
38958             if(this.expanded){
38959                 this.expanded = false;
38960                 this.expand(false, false);
38961             }
38962         }
38963     },
38964
38965     // private
38966     renderIndent : function(deep, refresh){
38967         if(refresh){
38968             this.ui.childIndent = null;
38969         }
38970         this.ui.renderIndent();
38971         if(deep === true && this.childrenRendered){
38972             var cs = this.childNodes;
38973             for(var i = 0, len = cs.length; i < len; i++){
38974                 cs[i].renderIndent(true, refresh);
38975             }
38976         }
38977     }
38978 });/*
38979  * Based on:
38980  * Ext JS Library 1.1.1
38981  * Copyright(c) 2006-2007, Ext JS, LLC.
38982  *
38983  * Originally Released Under LGPL - original licence link has changed is not relivant.
38984  *
38985  * Fork - LGPL
38986  * <script type="text/javascript">
38987  */
38988  
38989 /**
38990  * @class Roo.tree.AsyncTreeNode
38991  * @extends Roo.tree.TreeNode
38992  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
38993  * @constructor
38994  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
38995  */
38996  Roo.tree.AsyncTreeNode = function(config){
38997     this.loaded = false;
38998     this.loading = false;
38999     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
39000     /**
39001     * @event beforeload
39002     * Fires before this node is loaded, return false to cancel
39003     * @param {Node} this This node
39004     */
39005     this.addEvents({'beforeload':true, 'load': true});
39006     /**
39007     * @event load
39008     * Fires when this node is loaded
39009     * @param {Node} this This node
39010     */
39011     /**
39012      * The loader used by this node (defaults to using the tree's defined loader)
39013      * @type TreeLoader
39014      * @property loader
39015      */
39016 };
39017 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
39018     expand : function(deep, anim, callback){
39019         if(this.loading){ // if an async load is already running, waiting til it's done
39020             var timer;
39021             var f = function(){
39022                 if(!this.loading){ // done loading
39023                     clearInterval(timer);
39024                     this.expand(deep, anim, callback);
39025                 }
39026             }.createDelegate(this);
39027             timer = setInterval(f, 200);
39028             return;
39029         }
39030         if(!this.loaded){
39031             if(this.fireEvent("beforeload", this) === false){
39032                 return;
39033             }
39034             this.loading = true;
39035             this.ui.beforeLoad(this);
39036             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
39037             if(loader){
39038                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
39039                 return;
39040             }
39041         }
39042         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
39043     },
39044     
39045     /**
39046      * Returns true if this node is currently loading
39047      * @return {Boolean}
39048      */
39049     isLoading : function(){
39050         return this.loading;  
39051     },
39052     
39053     loadComplete : function(deep, anim, callback){
39054         this.loading = false;
39055         this.loaded = true;
39056         this.ui.afterLoad(this);
39057         this.fireEvent("load", this);
39058         this.expand(deep, anim, callback);
39059     },
39060     
39061     /**
39062      * Returns true if this node has been loaded
39063      * @return {Boolean}
39064      */
39065     isLoaded : function(){
39066         return this.loaded;
39067     },
39068     
39069     hasChildNodes : function(){
39070         if(!this.isLeaf() && !this.loaded){
39071             return true;
39072         }else{
39073             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
39074         }
39075     },
39076
39077     /**
39078      * Trigger a reload for this node
39079      * @param {Function} callback
39080      */
39081     reload : function(callback){
39082         this.collapse(false, false);
39083         while(this.firstChild){
39084             this.removeChild(this.firstChild);
39085         }
39086         this.childrenRendered = false;
39087         this.loaded = false;
39088         if(this.isHiddenRoot()){
39089             this.expanded = false;
39090         }
39091         this.expand(false, false, callback);
39092     }
39093 });/*
39094  * Based on:
39095  * Ext JS Library 1.1.1
39096  * Copyright(c) 2006-2007, Ext JS, LLC.
39097  *
39098  * Originally Released Under LGPL - original licence link has changed is not relivant.
39099  *
39100  * Fork - LGPL
39101  * <script type="text/javascript">
39102  */
39103  
39104 /**
39105  * @class Roo.tree.TreeNodeUI
39106  * @constructor
39107  * @param {Object} node The node to render
39108  * The TreeNode UI implementation is separate from the
39109  * tree implementation. Unless you are customizing the tree UI,
39110  * you should never have to use this directly.
39111  */
39112 Roo.tree.TreeNodeUI = function(node){
39113     this.node = node;
39114     this.rendered = false;
39115     this.animating = false;
39116     this.emptyIcon = Roo.BLANK_IMAGE_URL;
39117 };
39118
39119 Roo.tree.TreeNodeUI.prototype = {
39120     removeChild : function(node){
39121         if(this.rendered){
39122             this.ctNode.removeChild(node.ui.getEl());
39123         }
39124     },
39125
39126     beforeLoad : function(){
39127          this.addClass("x-tree-node-loading");
39128     },
39129
39130     afterLoad : function(){
39131          this.removeClass("x-tree-node-loading");
39132     },
39133
39134     onTextChange : function(node, text, oldText){
39135         if(this.rendered){
39136             this.textNode.innerHTML = text;
39137         }
39138     },
39139
39140     onDisableChange : function(node, state){
39141         this.disabled = state;
39142         if(state){
39143             this.addClass("x-tree-node-disabled");
39144         }else{
39145             this.removeClass("x-tree-node-disabled");
39146         }
39147     },
39148
39149     onSelectedChange : function(state){
39150         if(state){
39151             this.focus();
39152             this.addClass("x-tree-selected");
39153         }else{
39154             //this.blur();
39155             this.removeClass("x-tree-selected");
39156         }
39157     },
39158
39159     onMove : function(tree, node, oldParent, newParent, index, refNode){
39160         this.childIndent = null;
39161         if(this.rendered){
39162             var targetNode = newParent.ui.getContainer();
39163             if(!targetNode){//target not rendered
39164                 this.holder = document.createElement("div");
39165                 this.holder.appendChild(this.wrap);
39166                 return;
39167             }
39168             var insertBefore = refNode ? refNode.ui.getEl() : null;
39169             if(insertBefore){
39170                 targetNode.insertBefore(this.wrap, insertBefore);
39171             }else{
39172                 targetNode.appendChild(this.wrap);
39173             }
39174             this.node.renderIndent(true);
39175         }
39176     },
39177
39178     addClass : function(cls){
39179         if(this.elNode){
39180             Roo.fly(this.elNode).addClass(cls);
39181         }
39182     },
39183
39184     removeClass : function(cls){
39185         if(this.elNode){
39186             Roo.fly(this.elNode).removeClass(cls);
39187         }
39188     },
39189
39190     remove : function(){
39191         if(this.rendered){
39192             this.holder = document.createElement("div");
39193             this.holder.appendChild(this.wrap);
39194         }
39195     },
39196
39197     fireEvent : function(){
39198         return this.node.fireEvent.apply(this.node, arguments);
39199     },
39200
39201     initEvents : function(){
39202         this.node.on("move", this.onMove, this);
39203         var E = Roo.EventManager;
39204         var a = this.anchor;
39205
39206         var el = Roo.fly(a, '_treeui');
39207
39208         if(Roo.isOpera){ // opera render bug ignores the CSS
39209             el.setStyle("text-decoration", "none");
39210         }
39211
39212         el.on("click", this.onClick, this);
39213         el.on("dblclick", this.onDblClick, this);
39214
39215         if(this.checkbox){
39216             Roo.EventManager.on(this.checkbox,
39217                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
39218         }
39219
39220         el.on("contextmenu", this.onContextMenu, this);
39221
39222         var icon = Roo.fly(this.iconNode);
39223         icon.on("click", this.onClick, this);
39224         icon.on("dblclick", this.onDblClick, this);
39225         icon.on("contextmenu", this.onContextMenu, this);
39226         E.on(this.ecNode, "click", this.ecClick, this, true);
39227
39228         if(this.node.disabled){
39229             this.addClass("x-tree-node-disabled");
39230         }
39231         if(this.node.hidden){
39232             this.addClass("x-tree-node-disabled");
39233         }
39234         var ot = this.node.getOwnerTree();
39235         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
39236         if(dd && (!this.node.isRoot || ot.rootVisible)){
39237             Roo.dd.Registry.register(this.elNode, {
39238                 node: this.node,
39239                 handles: this.getDDHandles(),
39240                 isHandle: false
39241             });
39242         }
39243     },
39244
39245     getDDHandles : function(){
39246         return [this.iconNode, this.textNode];
39247     },
39248
39249     hide : function(){
39250         if(this.rendered){
39251             this.wrap.style.display = "none";
39252         }
39253     },
39254
39255     show : function(){
39256         if(this.rendered){
39257             this.wrap.style.display = "";
39258         }
39259     },
39260
39261     onContextMenu : function(e){
39262         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
39263             e.preventDefault();
39264             this.focus();
39265             this.fireEvent("contextmenu", this.node, e);
39266         }
39267     },
39268
39269     onClick : function(e){
39270         if(this.dropping){
39271             e.stopEvent();
39272             return;
39273         }
39274         if(this.fireEvent("beforeclick", this.node, e) !== false){
39275             if(!this.disabled && this.node.attributes.href){
39276                 this.fireEvent("click", this.node, e);
39277                 return;
39278             }
39279             e.preventDefault();
39280             if(this.disabled){
39281                 return;
39282             }
39283
39284             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
39285                 this.node.toggle();
39286             }
39287
39288             this.fireEvent("click", this.node, e);
39289         }else{
39290             e.stopEvent();
39291         }
39292     },
39293
39294     onDblClick : function(e){
39295         e.preventDefault();
39296         if(this.disabled){
39297             return;
39298         }
39299         if(this.checkbox){
39300             this.toggleCheck();
39301         }
39302         if(!this.animating && this.node.hasChildNodes()){
39303             this.node.toggle();
39304         }
39305         this.fireEvent("dblclick", this.node, e);
39306     },
39307
39308     onCheckChange : function(){
39309         var checked = this.checkbox.checked;
39310         this.node.attributes.checked = checked;
39311         this.fireEvent('checkchange', this.node, checked);
39312     },
39313
39314     ecClick : function(e){
39315         if(!this.animating && this.node.hasChildNodes()){
39316             this.node.toggle();
39317         }
39318     },
39319
39320     startDrop : function(){
39321         this.dropping = true;
39322     },
39323
39324     // delayed drop so the click event doesn't get fired on a drop
39325     endDrop : function(){
39326        setTimeout(function(){
39327            this.dropping = false;
39328        }.createDelegate(this), 50);
39329     },
39330
39331     expand : function(){
39332         this.updateExpandIcon();
39333         this.ctNode.style.display = "";
39334     },
39335
39336     focus : function(){
39337         if(!this.node.preventHScroll){
39338             try{this.anchor.focus();
39339             }catch(e){}
39340         }else if(!Roo.isIE){
39341             try{
39342                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
39343                 var l = noscroll.scrollLeft;
39344                 this.anchor.focus();
39345                 noscroll.scrollLeft = l;
39346             }catch(e){}
39347         }
39348     },
39349
39350     toggleCheck : function(value){
39351         var cb = this.checkbox;
39352         if(cb){
39353             cb.checked = (value === undefined ? !cb.checked : value);
39354         }
39355     },
39356
39357     blur : function(){
39358         try{
39359             this.anchor.blur();
39360         }catch(e){}
39361     },
39362
39363     animExpand : function(callback){
39364         var ct = Roo.get(this.ctNode);
39365         ct.stopFx();
39366         if(!this.node.hasChildNodes()){
39367             this.updateExpandIcon();
39368             this.ctNode.style.display = "";
39369             Roo.callback(callback);
39370             return;
39371         }
39372         this.animating = true;
39373         this.updateExpandIcon();
39374
39375         ct.slideIn('t', {
39376            callback : function(){
39377                this.animating = false;
39378                Roo.callback(callback);
39379             },
39380             scope: this,
39381             duration: this.node.ownerTree.duration || .25
39382         });
39383     },
39384
39385     highlight : function(){
39386         var tree = this.node.getOwnerTree();
39387         Roo.fly(this.wrap).highlight(
39388             tree.hlColor || "C3DAF9",
39389             {endColor: tree.hlBaseColor}
39390         );
39391     },
39392
39393     collapse : function(){
39394         this.updateExpandIcon();
39395         this.ctNode.style.display = "none";
39396     },
39397
39398     animCollapse : function(callback){
39399         var ct = Roo.get(this.ctNode);
39400         ct.enableDisplayMode('block');
39401         ct.stopFx();
39402
39403         this.animating = true;
39404         this.updateExpandIcon();
39405
39406         ct.slideOut('t', {
39407             callback : function(){
39408                this.animating = false;
39409                Roo.callback(callback);
39410             },
39411             scope: this,
39412             duration: this.node.ownerTree.duration || .25
39413         });
39414     },
39415
39416     getContainer : function(){
39417         return this.ctNode;
39418     },
39419
39420     getEl : function(){
39421         return this.wrap;
39422     },
39423
39424     appendDDGhost : function(ghostNode){
39425         ghostNode.appendChild(this.elNode.cloneNode(true));
39426     },
39427
39428     getDDRepairXY : function(){
39429         return Roo.lib.Dom.getXY(this.iconNode);
39430     },
39431
39432     onRender : function(){
39433         this.render();
39434     },
39435
39436     render : function(bulkRender){
39437         var n = this.node, a = n.attributes;
39438         var targetNode = n.parentNode ?
39439               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
39440
39441         if(!this.rendered){
39442             this.rendered = true;
39443
39444             this.renderElements(n, a, targetNode, bulkRender);
39445
39446             if(a.qtip){
39447                if(this.textNode.setAttributeNS){
39448                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
39449                    if(a.qtipTitle){
39450                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
39451                    }
39452                }else{
39453                    this.textNode.setAttribute("ext:qtip", a.qtip);
39454                    if(a.qtipTitle){
39455                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
39456                    }
39457                }
39458             }else if(a.qtipCfg){
39459                 a.qtipCfg.target = Roo.id(this.textNode);
39460                 Roo.QuickTips.register(a.qtipCfg);
39461             }
39462             this.initEvents();
39463             if(!this.node.expanded){
39464                 this.updateExpandIcon();
39465             }
39466         }else{
39467             if(bulkRender === true) {
39468                 targetNode.appendChild(this.wrap);
39469             }
39470         }
39471     },
39472
39473     renderElements : function(n, a, targetNode, bulkRender)
39474     {
39475         // add some indent caching, this helps performance when rendering a large tree
39476         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
39477         var t = n.getOwnerTree();
39478         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
39479         if (typeof(n.attributes.html) != 'undefined') {
39480             txt = n.attributes.html;
39481         }
39482         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
39483         var cb = typeof a.checked == 'boolean';
39484         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
39485         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
39486             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
39487             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
39488             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
39489             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
39490             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
39491              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
39492                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
39493             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
39494             "</li>"];
39495
39496         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
39497             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
39498                                 n.nextSibling.ui.getEl(), buf.join(""));
39499         }else{
39500             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
39501         }
39502
39503         this.elNode = this.wrap.childNodes[0];
39504         this.ctNode = this.wrap.childNodes[1];
39505         var cs = this.elNode.childNodes;
39506         this.indentNode = cs[0];
39507         this.ecNode = cs[1];
39508         this.iconNode = cs[2];
39509         var index = 3;
39510         if(cb){
39511             this.checkbox = cs[3];
39512             index++;
39513         }
39514         this.anchor = cs[index];
39515         this.textNode = cs[index].firstChild;
39516     },
39517
39518     getAnchor : function(){
39519         return this.anchor;
39520     },
39521
39522     getTextEl : function(){
39523         return this.textNode;
39524     },
39525
39526     getIconEl : function(){
39527         return this.iconNode;
39528     },
39529
39530     isChecked : function(){
39531         return this.checkbox ? this.checkbox.checked : false;
39532     },
39533
39534     updateExpandIcon : function(){
39535         if(this.rendered){
39536             var n = this.node, c1, c2;
39537             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
39538             var hasChild = n.hasChildNodes();
39539             if(hasChild){
39540                 if(n.expanded){
39541                     cls += "-minus";
39542                     c1 = "x-tree-node-collapsed";
39543                     c2 = "x-tree-node-expanded";
39544                 }else{
39545                     cls += "-plus";
39546                     c1 = "x-tree-node-expanded";
39547                     c2 = "x-tree-node-collapsed";
39548                 }
39549                 if(this.wasLeaf){
39550                     this.removeClass("x-tree-node-leaf");
39551                     this.wasLeaf = false;
39552                 }
39553                 if(this.c1 != c1 || this.c2 != c2){
39554                     Roo.fly(this.elNode).replaceClass(c1, c2);
39555                     this.c1 = c1; this.c2 = c2;
39556                 }
39557             }else{
39558                 // this changes non-leafs into leafs if they have no children.
39559                 // it's not very rational behaviour..
39560                 
39561                 if(!this.wasLeaf && this.node.leaf){
39562                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
39563                     delete this.c1;
39564                     delete this.c2;
39565                     this.wasLeaf = true;
39566                 }
39567             }
39568             var ecc = "x-tree-ec-icon "+cls;
39569             if(this.ecc != ecc){
39570                 this.ecNode.className = ecc;
39571                 this.ecc = ecc;
39572             }
39573         }
39574     },
39575
39576     getChildIndent : function(){
39577         if(!this.childIndent){
39578             var buf = [];
39579             var p = this.node;
39580             while(p){
39581                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
39582                     if(!p.isLast()) {
39583                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
39584                     } else {
39585                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
39586                     }
39587                 }
39588                 p = p.parentNode;
39589             }
39590             this.childIndent = buf.join("");
39591         }
39592         return this.childIndent;
39593     },
39594
39595     renderIndent : function(){
39596         if(this.rendered){
39597             var indent = "";
39598             var p = this.node.parentNode;
39599             if(p){
39600                 indent = p.ui.getChildIndent();
39601             }
39602             if(this.indentMarkup != indent){ // don't rerender if not required
39603                 this.indentNode.innerHTML = indent;
39604                 this.indentMarkup = indent;
39605             }
39606             this.updateExpandIcon();
39607         }
39608     }
39609 };
39610
39611 Roo.tree.RootTreeNodeUI = function(){
39612     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
39613 };
39614 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
39615     render : function(){
39616         if(!this.rendered){
39617             var targetNode = this.node.ownerTree.innerCt.dom;
39618             this.node.expanded = true;
39619             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
39620             this.wrap = this.ctNode = targetNode.firstChild;
39621         }
39622     },
39623     collapse : function(){
39624     },
39625     expand : function(){
39626     }
39627 });/*
39628  * Based on:
39629  * Ext JS Library 1.1.1
39630  * Copyright(c) 2006-2007, Ext JS, LLC.
39631  *
39632  * Originally Released Under LGPL - original licence link has changed is not relivant.
39633  *
39634  * Fork - LGPL
39635  * <script type="text/javascript">
39636  */
39637 /**
39638  * @class Roo.tree.TreeLoader
39639  * @extends Roo.util.Observable
39640  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
39641  * nodes from a specified URL. The response must be a javascript Array definition
39642  * who's elements are node definition objects. eg:
39643  * <pre><code>
39644 {  success : true,
39645    data :      [
39646    
39647     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
39648     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
39649     ]
39650 }
39651
39652
39653 </code></pre>
39654  * <br><br>
39655  * The old style respose with just an array is still supported, but not recommended.
39656  * <br><br>
39657  *
39658  * A server request is sent, and child nodes are loaded only when a node is expanded.
39659  * The loading node's id is passed to the server under the parameter name "node" to
39660  * enable the server to produce the correct child nodes.
39661  * <br><br>
39662  * To pass extra parameters, an event handler may be attached to the "beforeload"
39663  * event, and the parameters specified in the TreeLoader's baseParams property:
39664  * <pre><code>
39665     myTreeLoader.on("beforeload", function(treeLoader, node) {
39666         this.baseParams.category = node.attributes.category;
39667     }, this);
39668     
39669 </code></pre>
39670  *
39671  * This would pass an HTTP parameter called "category" to the server containing
39672  * the value of the Node's "category" attribute.
39673  * @constructor
39674  * Creates a new Treeloader.
39675  * @param {Object} config A config object containing config properties.
39676  */
39677 Roo.tree.TreeLoader = function(config){
39678     this.baseParams = {};
39679     this.requestMethod = "POST";
39680     Roo.apply(this, config);
39681
39682     this.addEvents({
39683     
39684         /**
39685          * @event beforeload
39686          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
39687          * @param {Object} This TreeLoader object.
39688          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
39689          * @param {Object} callback The callback function specified in the {@link #load} call.
39690          */
39691         beforeload : true,
39692         /**
39693          * @event load
39694          * Fires when the node has been successfuly loaded.
39695          * @param {Object} This TreeLoader object.
39696          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
39697          * @param {Object} response The response object containing the data from the server.
39698          */
39699         load : true,
39700         /**
39701          * @event loadexception
39702          * Fires if the network request failed.
39703          * @param {Object} This TreeLoader object.
39704          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
39705          * @param {Object} response The response object containing the data from the server.
39706          */
39707         loadexception : true,
39708         /**
39709          * @event create
39710          * Fires before a node is created, enabling you to return custom Node types 
39711          * @param {Object} This TreeLoader object.
39712          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
39713          */
39714         create : true
39715     });
39716
39717     Roo.tree.TreeLoader.superclass.constructor.call(this);
39718 };
39719
39720 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
39721     /**
39722     * @cfg {String} dataUrl The URL from which to request a Json string which
39723     * specifies an array of node definition object representing the child nodes
39724     * to be loaded.
39725     */
39726     /**
39727     * @cfg {String} requestMethod either GET or POST
39728     * defaults to POST (due to BC)
39729     * to be loaded.
39730     */
39731     /**
39732     * @cfg {Object} baseParams (optional) An object containing properties which
39733     * specify HTTP parameters to be passed to each request for child nodes.
39734     */
39735     /**
39736     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
39737     * created by this loader. If the attributes sent by the server have an attribute in this object,
39738     * they take priority.
39739     */
39740     /**
39741     * @cfg {Object} uiProviders (optional) An object containing properties which
39742     * 
39743     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
39744     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
39745     * <i>uiProvider</i> attribute of a returned child node is a string rather
39746     * than a reference to a TreeNodeUI implementation, this that string value
39747     * is used as a property name in the uiProviders object. You can define the provider named
39748     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
39749     */
39750     uiProviders : {},
39751
39752     /**
39753     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
39754     * child nodes before loading.
39755     */
39756     clearOnLoad : true,
39757
39758     /**
39759     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
39760     * property on loading, rather than expecting an array. (eg. more compatible to a standard
39761     * Grid query { data : [ .....] }
39762     */
39763     
39764     root : false,
39765      /**
39766     * @cfg {String} queryParam (optional) 
39767     * Name of the query as it will be passed on the querystring (defaults to 'node')
39768     * eg. the request will be ?node=[id]
39769     */
39770     
39771     
39772     queryParam: false,
39773     
39774     /**
39775      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
39776      * This is called automatically when a node is expanded, but may be used to reload
39777      * a node (or append new children if the {@link #clearOnLoad} option is false.)
39778      * @param {Roo.tree.TreeNode} node
39779      * @param {Function} callback
39780      */
39781     load : function(node, callback){
39782         if(this.clearOnLoad){
39783             while(node.firstChild){
39784                 node.removeChild(node.firstChild);
39785             }
39786         }
39787         if(node.attributes.children){ // preloaded json children
39788             var cs = node.attributes.children;
39789             for(var i = 0, len = cs.length; i < len; i++){
39790                 node.appendChild(this.createNode(cs[i]));
39791             }
39792             if(typeof callback == "function"){
39793                 callback();
39794             }
39795         }else if(this.dataUrl){
39796             this.requestData(node, callback);
39797         }
39798     },
39799
39800     getParams: function(node){
39801         var buf = [], bp = this.baseParams;
39802         for(var key in bp){
39803             if(typeof bp[key] != "function"){
39804                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
39805             }
39806         }
39807         var n = this.queryParam === false ? 'node' : this.queryParam;
39808         buf.push(n + "=", encodeURIComponent(node.id));
39809         return buf.join("");
39810     },
39811
39812     requestData : function(node, callback){
39813         if(this.fireEvent("beforeload", this, node, callback) !== false){
39814             this.transId = Roo.Ajax.request({
39815                 method:this.requestMethod,
39816                 url: this.dataUrl||this.url,
39817                 success: this.handleResponse,
39818                 failure: this.handleFailure,
39819                 scope: this,
39820                 argument: {callback: callback, node: node},
39821                 params: this.getParams(node)
39822             });
39823         }else{
39824             // if the load is cancelled, make sure we notify
39825             // the node that we are done
39826             if(typeof callback == "function"){
39827                 callback();
39828             }
39829         }
39830     },
39831
39832     isLoading : function(){
39833         return this.transId ? true : false;
39834     },
39835
39836     abort : function(){
39837         if(this.isLoading()){
39838             Roo.Ajax.abort(this.transId);
39839         }
39840     },
39841
39842     // private
39843     createNode : function(attr)
39844     {
39845         // apply baseAttrs, nice idea Corey!
39846         if(this.baseAttrs){
39847             Roo.applyIf(attr, this.baseAttrs);
39848         }
39849         if(this.applyLoader !== false){
39850             attr.loader = this;
39851         }
39852         // uiProvider = depreciated..
39853         
39854         if(typeof(attr.uiProvider) == 'string'){
39855            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
39856                 /**  eval:var:attr */ eval(attr.uiProvider);
39857         }
39858         if(typeof(this.uiProviders['default']) != 'undefined') {
39859             attr.uiProvider = this.uiProviders['default'];
39860         }
39861         
39862         this.fireEvent('create', this, attr);
39863         
39864         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
39865         return(attr.leaf ?
39866                         new Roo.tree.TreeNode(attr) :
39867                         new Roo.tree.AsyncTreeNode(attr));
39868     },
39869
39870     processResponse : function(response, node, callback)
39871     {
39872         var json = response.responseText;
39873         try {
39874             
39875             var o = Roo.decode(json);
39876             
39877             if (this.root === false && typeof(o.success) != undefined) {
39878                 this.root = 'data'; // the default behaviour for list like data..
39879                 }
39880                 
39881             if (this.root !== false &&  !o.success) {
39882                 // it's a failure condition.
39883                 var a = response.argument;
39884                 this.fireEvent("loadexception", this, a.node, response);
39885                 Roo.log("Load failed - should have a handler really");
39886                 return;
39887             }
39888             
39889             
39890             
39891             if (this.root !== false) {
39892                  o = o[this.root];
39893             }
39894             
39895             for(var i = 0, len = o.length; i < len; i++){
39896                 var n = this.createNode(o[i]);
39897                 if(n){
39898                     node.appendChild(n);
39899                 }
39900             }
39901             if(typeof callback == "function"){
39902                 callback(this, node);
39903             }
39904         }catch(e){
39905             this.handleFailure(response);
39906         }
39907     },
39908
39909     handleResponse : function(response){
39910         this.transId = false;
39911         var a = response.argument;
39912         this.processResponse(response, a.node, a.callback);
39913         this.fireEvent("load", this, a.node, response);
39914     },
39915
39916     handleFailure : function(response)
39917     {
39918         // should handle failure better..
39919         this.transId = false;
39920         var a = response.argument;
39921         this.fireEvent("loadexception", this, a.node, response);
39922         if(typeof a.callback == "function"){
39923             a.callback(this, a.node);
39924         }
39925     }
39926 });/*
39927  * Based on:
39928  * Ext JS Library 1.1.1
39929  * Copyright(c) 2006-2007, Ext JS, LLC.
39930  *
39931  * Originally Released Under LGPL - original licence link has changed is not relivant.
39932  *
39933  * Fork - LGPL
39934  * <script type="text/javascript">
39935  */
39936
39937 /**
39938 * @class Roo.tree.TreeFilter
39939 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
39940 * @param {TreePanel} tree
39941 * @param {Object} config (optional)
39942  */
39943 Roo.tree.TreeFilter = function(tree, config){
39944     this.tree = tree;
39945     this.filtered = {};
39946     Roo.apply(this, config);
39947 };
39948
39949 Roo.tree.TreeFilter.prototype = {
39950     clearBlank:false,
39951     reverse:false,
39952     autoClear:false,
39953     remove:false,
39954
39955      /**
39956      * Filter the data by a specific attribute.
39957      * @param {String/RegExp} value Either string that the attribute value
39958      * should start with or a RegExp to test against the attribute
39959      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
39960      * @param {TreeNode} startNode (optional) The node to start the filter at.
39961      */
39962     filter : function(value, attr, startNode){
39963         attr = attr || "text";
39964         var f;
39965         if(typeof value == "string"){
39966             var vlen = value.length;
39967             // auto clear empty filter
39968             if(vlen == 0 && this.clearBlank){
39969                 this.clear();
39970                 return;
39971             }
39972             value = value.toLowerCase();
39973             f = function(n){
39974                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
39975             };
39976         }else if(value.exec){ // regex?
39977             f = function(n){
39978                 return value.test(n.attributes[attr]);
39979             };
39980         }else{
39981             throw 'Illegal filter type, must be string or regex';
39982         }
39983         this.filterBy(f, null, startNode);
39984         },
39985
39986     /**
39987      * Filter by a function. The passed function will be called with each
39988      * node in the tree (or from the startNode). If the function returns true, the node is kept
39989      * otherwise it is filtered. If a node is filtered, its children are also filtered.
39990      * @param {Function} fn The filter function
39991      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
39992      */
39993     filterBy : function(fn, scope, startNode){
39994         startNode = startNode || this.tree.root;
39995         if(this.autoClear){
39996             this.clear();
39997         }
39998         var af = this.filtered, rv = this.reverse;
39999         var f = function(n){
40000             if(n == startNode){
40001                 return true;
40002             }
40003             if(af[n.id]){
40004                 return false;
40005             }
40006             var m = fn.call(scope || n, n);
40007             if(!m || rv){
40008                 af[n.id] = n;
40009                 n.ui.hide();
40010                 return false;
40011             }
40012             return true;
40013         };
40014         startNode.cascade(f);
40015         if(this.remove){
40016            for(var id in af){
40017                if(typeof id != "function"){
40018                    var n = af[id];
40019                    if(n && n.parentNode){
40020                        n.parentNode.removeChild(n);
40021                    }
40022                }
40023            }
40024         }
40025     },
40026
40027     /**
40028      * Clears the current filter. Note: with the "remove" option
40029      * set a filter cannot be cleared.
40030      */
40031     clear : function(){
40032         var t = this.tree;
40033         var af = this.filtered;
40034         for(var id in af){
40035             if(typeof id != "function"){
40036                 var n = af[id];
40037                 if(n){
40038                     n.ui.show();
40039                 }
40040             }
40041         }
40042         this.filtered = {};
40043     }
40044 };
40045 /*
40046  * Based on:
40047  * Ext JS Library 1.1.1
40048  * Copyright(c) 2006-2007, Ext JS, LLC.
40049  *
40050  * Originally Released Under LGPL - original licence link has changed is not relivant.
40051  *
40052  * Fork - LGPL
40053  * <script type="text/javascript">
40054  */
40055  
40056
40057 /**
40058  * @class Roo.tree.TreeSorter
40059  * Provides sorting of nodes in a TreePanel
40060  * 
40061  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
40062  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
40063  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
40064  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
40065  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
40066  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
40067  * @constructor
40068  * @param {TreePanel} tree
40069  * @param {Object} config
40070  */
40071 Roo.tree.TreeSorter = function(tree, config){
40072     Roo.apply(this, config);
40073     tree.on("beforechildrenrendered", this.doSort, this);
40074     tree.on("append", this.updateSort, this);
40075     tree.on("insert", this.updateSort, this);
40076     
40077     var dsc = this.dir && this.dir.toLowerCase() == "desc";
40078     var p = this.property || "text";
40079     var sortType = this.sortType;
40080     var fs = this.folderSort;
40081     var cs = this.caseSensitive === true;
40082     var leafAttr = this.leafAttr || 'leaf';
40083
40084     this.sortFn = function(n1, n2){
40085         if(fs){
40086             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
40087                 return 1;
40088             }
40089             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
40090                 return -1;
40091             }
40092         }
40093         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
40094         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
40095         if(v1 < v2){
40096                         return dsc ? +1 : -1;
40097                 }else if(v1 > v2){
40098                         return dsc ? -1 : +1;
40099         }else{
40100                 return 0;
40101         }
40102     };
40103 };
40104
40105 Roo.tree.TreeSorter.prototype = {
40106     doSort : function(node){
40107         node.sort(this.sortFn);
40108     },
40109     
40110     compareNodes : function(n1, n2){
40111         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
40112     },
40113     
40114     updateSort : function(tree, node){
40115         if(node.childrenRendered){
40116             this.doSort.defer(1, this, [node]);
40117         }
40118     }
40119 };/*
40120  * Based on:
40121  * Ext JS Library 1.1.1
40122  * Copyright(c) 2006-2007, Ext JS, LLC.
40123  *
40124  * Originally Released Under LGPL - original licence link has changed is not relivant.
40125  *
40126  * Fork - LGPL
40127  * <script type="text/javascript">
40128  */
40129
40130 if(Roo.dd.DropZone){
40131     
40132 Roo.tree.TreeDropZone = function(tree, config){
40133     this.allowParentInsert = false;
40134     this.allowContainerDrop = false;
40135     this.appendOnly = false;
40136     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
40137     this.tree = tree;
40138     this.lastInsertClass = "x-tree-no-status";
40139     this.dragOverData = {};
40140 };
40141
40142 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
40143     ddGroup : "TreeDD",
40144     scroll:  true,
40145     
40146     expandDelay : 1000,
40147     
40148     expandNode : function(node){
40149         if(node.hasChildNodes() && !node.isExpanded()){
40150             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
40151         }
40152     },
40153     
40154     queueExpand : function(node){
40155         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
40156     },
40157     
40158     cancelExpand : function(){
40159         if(this.expandProcId){
40160             clearTimeout(this.expandProcId);
40161             this.expandProcId = false;
40162         }
40163     },
40164     
40165     isValidDropPoint : function(n, pt, dd, e, data){
40166         if(!n || !data){ return false; }
40167         var targetNode = n.node;
40168         var dropNode = data.node;
40169         // default drop rules
40170         if(!(targetNode && targetNode.isTarget && pt)){
40171             return false;
40172         }
40173         if(pt == "append" && targetNode.allowChildren === false){
40174             return false;
40175         }
40176         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
40177             return false;
40178         }
40179         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
40180             return false;
40181         }
40182         // reuse the object
40183         var overEvent = this.dragOverData;
40184         overEvent.tree = this.tree;
40185         overEvent.target = targetNode;
40186         overEvent.data = data;
40187         overEvent.point = pt;
40188         overEvent.source = dd;
40189         overEvent.rawEvent = e;
40190         overEvent.dropNode = dropNode;
40191         overEvent.cancel = false;  
40192         var result = this.tree.fireEvent("nodedragover", overEvent);
40193         return overEvent.cancel === false && result !== false;
40194     },
40195     
40196     getDropPoint : function(e, n, dd)
40197     {
40198         var tn = n.node;
40199         if(tn.isRoot){
40200             return tn.allowChildren !== false ? "append" : false; // always append for root
40201         }
40202         var dragEl = n.ddel;
40203         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
40204         var y = Roo.lib.Event.getPageY(e);
40205         //var noAppend = tn.allowChildren === false || tn.isLeaf();
40206         
40207         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
40208         var noAppend = tn.allowChildren === false;
40209         if(this.appendOnly || tn.parentNode.allowChildren === false){
40210             return noAppend ? false : "append";
40211         }
40212         var noBelow = false;
40213         if(!this.allowParentInsert){
40214             noBelow = tn.hasChildNodes() && tn.isExpanded();
40215         }
40216         var q = (b - t) / (noAppend ? 2 : 3);
40217         if(y >= t && y < (t + q)){
40218             return "above";
40219         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
40220             return "below";
40221         }else{
40222             return "append";
40223         }
40224     },
40225     
40226     onNodeEnter : function(n, dd, e, data)
40227     {
40228         this.cancelExpand();
40229     },
40230     
40231     onNodeOver : function(n, dd, e, data)
40232     {
40233        
40234         var pt = this.getDropPoint(e, n, dd);
40235         var node = n.node;
40236         
40237         // auto node expand check
40238         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
40239             this.queueExpand(node);
40240         }else if(pt != "append"){
40241             this.cancelExpand();
40242         }
40243         
40244         // set the insert point style on the target node
40245         var returnCls = this.dropNotAllowed;
40246         if(this.isValidDropPoint(n, pt, dd, e, data)){
40247            if(pt){
40248                var el = n.ddel;
40249                var cls;
40250                if(pt == "above"){
40251                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
40252                    cls = "x-tree-drag-insert-above";
40253                }else if(pt == "below"){
40254                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
40255                    cls = "x-tree-drag-insert-below";
40256                }else{
40257                    returnCls = "x-tree-drop-ok-append";
40258                    cls = "x-tree-drag-append";
40259                }
40260                if(this.lastInsertClass != cls){
40261                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
40262                    this.lastInsertClass = cls;
40263                }
40264            }
40265        }
40266        return returnCls;
40267     },
40268     
40269     onNodeOut : function(n, dd, e, data){
40270         
40271         this.cancelExpand();
40272         this.removeDropIndicators(n);
40273     },
40274     
40275     onNodeDrop : function(n, dd, e, data){
40276         var point = this.getDropPoint(e, n, dd);
40277         var targetNode = n.node;
40278         targetNode.ui.startDrop();
40279         if(!this.isValidDropPoint(n, point, dd, e, data)){
40280             targetNode.ui.endDrop();
40281             return false;
40282         }
40283         // first try to find the drop node
40284         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
40285         var dropEvent = {
40286             tree : this.tree,
40287             target: targetNode,
40288             data: data,
40289             point: point,
40290             source: dd,
40291             rawEvent: e,
40292             dropNode: dropNode,
40293             cancel: !dropNode   
40294         };
40295         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
40296         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
40297             targetNode.ui.endDrop();
40298             return false;
40299         }
40300         // allow target changing
40301         targetNode = dropEvent.target;
40302         if(point == "append" && !targetNode.isExpanded()){
40303             targetNode.expand(false, null, function(){
40304                 this.completeDrop(dropEvent);
40305             }.createDelegate(this));
40306         }else{
40307             this.completeDrop(dropEvent);
40308         }
40309         return true;
40310     },
40311     
40312     completeDrop : function(de){
40313         var ns = de.dropNode, p = de.point, t = de.target;
40314         if(!(ns instanceof Array)){
40315             ns = [ns];
40316         }
40317         var n;
40318         for(var i = 0, len = ns.length; i < len; i++){
40319             n = ns[i];
40320             if(p == "above"){
40321                 t.parentNode.insertBefore(n, t);
40322             }else if(p == "below"){
40323                 t.parentNode.insertBefore(n, t.nextSibling);
40324             }else{
40325                 t.appendChild(n);
40326             }
40327         }
40328         n.ui.focus();
40329         if(this.tree.hlDrop){
40330             n.ui.highlight();
40331         }
40332         t.ui.endDrop();
40333         this.tree.fireEvent("nodedrop", de);
40334     },
40335     
40336     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
40337         if(this.tree.hlDrop){
40338             dropNode.ui.focus();
40339             dropNode.ui.highlight();
40340         }
40341         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
40342     },
40343     
40344     getTree : function(){
40345         return this.tree;
40346     },
40347     
40348     removeDropIndicators : function(n){
40349         if(n && n.ddel){
40350             var el = n.ddel;
40351             Roo.fly(el).removeClass([
40352                     "x-tree-drag-insert-above",
40353                     "x-tree-drag-insert-below",
40354                     "x-tree-drag-append"]);
40355             this.lastInsertClass = "_noclass";
40356         }
40357     },
40358     
40359     beforeDragDrop : function(target, e, id){
40360         this.cancelExpand();
40361         return true;
40362     },
40363     
40364     afterRepair : function(data){
40365         if(data && Roo.enableFx){
40366             data.node.ui.highlight();
40367         }
40368         this.hideProxy();
40369     } 
40370     
40371 });
40372
40373 }
40374 /*
40375  * Based on:
40376  * Ext JS Library 1.1.1
40377  * Copyright(c) 2006-2007, Ext JS, LLC.
40378  *
40379  * Originally Released Under LGPL - original licence link has changed is not relivant.
40380  *
40381  * Fork - LGPL
40382  * <script type="text/javascript">
40383  */
40384  
40385
40386 if(Roo.dd.DragZone){
40387 Roo.tree.TreeDragZone = function(tree, config){
40388     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
40389     this.tree = tree;
40390 };
40391
40392 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
40393     ddGroup : "TreeDD",
40394    
40395     onBeforeDrag : function(data, e){
40396         var n = data.node;
40397         return n && n.draggable && !n.disabled;
40398     },
40399      
40400     
40401     onInitDrag : function(e){
40402         var data = this.dragData;
40403         this.tree.getSelectionModel().select(data.node);
40404         this.proxy.update("");
40405         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
40406         this.tree.fireEvent("startdrag", this.tree, data.node, e);
40407     },
40408     
40409     getRepairXY : function(e, data){
40410         return data.node.ui.getDDRepairXY();
40411     },
40412     
40413     onEndDrag : function(data, e){
40414         this.tree.fireEvent("enddrag", this.tree, data.node, e);
40415         
40416         
40417     },
40418     
40419     onValidDrop : function(dd, e, id){
40420         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
40421         this.hideProxy();
40422     },
40423     
40424     beforeInvalidDrop : function(e, id){
40425         // this scrolls the original position back into view
40426         var sm = this.tree.getSelectionModel();
40427         sm.clearSelections();
40428         sm.select(this.dragData.node);
40429     }
40430 });
40431 }/*
40432  * Based on:
40433  * Ext JS Library 1.1.1
40434  * Copyright(c) 2006-2007, Ext JS, LLC.
40435  *
40436  * Originally Released Under LGPL - original licence link has changed is not relivant.
40437  *
40438  * Fork - LGPL
40439  * <script type="text/javascript">
40440  */
40441 /**
40442  * @class Roo.tree.TreeEditor
40443  * @extends Roo.Editor
40444  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
40445  * as the editor field.
40446  * @constructor
40447  * @param {Object} config (used to be the tree panel.)
40448  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
40449  * 
40450  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
40451  * @cfg {Roo.form.TextField} field [required] The field configuration
40452  *
40453  * 
40454  */
40455 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
40456     var tree = config;
40457     var field;
40458     if (oldconfig) { // old style..
40459         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
40460     } else {
40461         // new style..
40462         tree = config.tree;
40463         config.field = config.field  || {};
40464         config.field.xtype = 'TextField';
40465         field = Roo.factory(config.field, Roo.form);
40466     }
40467     config = config || {};
40468     
40469     
40470     this.addEvents({
40471         /**
40472          * @event beforenodeedit
40473          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
40474          * false from the handler of this event.
40475          * @param {Editor} this
40476          * @param {Roo.tree.Node} node 
40477          */
40478         "beforenodeedit" : true
40479     });
40480     
40481     //Roo.log(config);
40482     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
40483
40484     this.tree = tree;
40485
40486     tree.on('beforeclick', this.beforeNodeClick, this);
40487     tree.getTreeEl().on('mousedown', this.hide, this);
40488     this.on('complete', this.updateNode, this);
40489     this.on('beforestartedit', this.fitToTree, this);
40490     this.on('startedit', this.bindScroll, this, {delay:10});
40491     this.on('specialkey', this.onSpecialKey, this);
40492 };
40493
40494 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
40495     /**
40496      * @cfg {String} alignment
40497      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
40498      */
40499     alignment: "l-l",
40500     // inherit
40501     autoSize: false,
40502     /**
40503      * @cfg {Boolean} hideEl
40504      * True to hide the bound element while the editor is displayed (defaults to false)
40505      */
40506     hideEl : false,
40507     /**
40508      * @cfg {String} cls
40509      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
40510      */
40511     cls: "x-small-editor x-tree-editor",
40512     /**
40513      * @cfg {Boolean} shim
40514      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
40515      */
40516     shim:false,
40517     // inherit
40518     shadow:"frame",
40519     /**
40520      * @cfg {Number} maxWidth
40521      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
40522      * the containing tree element's size, it will be automatically limited for you to the container width, taking
40523      * scroll and client offsets into account prior to each edit.
40524      */
40525     maxWidth: 250,
40526
40527     editDelay : 350,
40528
40529     // private
40530     fitToTree : function(ed, el){
40531         var td = this.tree.getTreeEl().dom, nd = el.dom;
40532         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
40533             td.scrollLeft = nd.offsetLeft;
40534         }
40535         var w = Math.min(
40536                 this.maxWidth,
40537                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
40538         this.setSize(w, '');
40539         
40540         return this.fireEvent('beforenodeedit', this, this.editNode);
40541         
40542     },
40543
40544     // private
40545     triggerEdit : function(node){
40546         this.completeEdit();
40547         this.editNode = node;
40548         this.startEdit(node.ui.textNode, node.text);
40549     },
40550
40551     // private
40552     bindScroll : function(){
40553         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
40554     },
40555
40556     // private
40557     beforeNodeClick : function(node, e){
40558         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
40559         this.lastClick = new Date();
40560         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
40561             e.stopEvent();
40562             this.triggerEdit(node);
40563             return false;
40564         }
40565         return true;
40566     },
40567
40568     // private
40569     updateNode : function(ed, value){
40570         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
40571         this.editNode.setText(value);
40572     },
40573
40574     // private
40575     onHide : function(){
40576         Roo.tree.TreeEditor.superclass.onHide.call(this);
40577         if(this.editNode){
40578             this.editNode.ui.focus();
40579         }
40580     },
40581
40582     // private
40583     onSpecialKey : function(field, e){
40584         var k = e.getKey();
40585         if(k == e.ESC){
40586             e.stopEvent();
40587             this.cancelEdit();
40588         }else if(k == e.ENTER && !e.hasModifier()){
40589             e.stopEvent();
40590             this.completeEdit();
40591         }
40592     }
40593 });//<Script type="text/javascript">
40594 /*
40595  * Based on:
40596  * Ext JS Library 1.1.1
40597  * Copyright(c) 2006-2007, Ext JS, LLC.
40598  *
40599  * Originally Released Under LGPL - original licence link has changed is not relivant.
40600  *
40601  * Fork - LGPL
40602  * <script type="text/javascript">
40603  */
40604  
40605 /**
40606  * Not documented??? - probably should be...
40607  */
40608
40609 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
40610     //focus: Roo.emptyFn, // prevent odd scrolling behavior
40611     
40612     renderElements : function(n, a, targetNode, bulkRender){
40613         //consel.log("renderElements?");
40614         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
40615
40616         var t = n.getOwnerTree();
40617         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
40618         
40619         var cols = t.columns;
40620         var bw = t.borderWidth;
40621         var c = cols[0];
40622         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
40623          var cb = typeof a.checked == "boolean";
40624         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
40625         var colcls = 'x-t-' + tid + '-c0';
40626         var buf = [
40627             '<li class="x-tree-node">',
40628             
40629                 
40630                 '<div class="x-tree-node-el ', a.cls,'">',
40631                     // extran...
40632                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
40633                 
40634                 
40635                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
40636                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
40637                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
40638                            (a.icon ? ' x-tree-node-inline-icon' : ''),
40639                            (a.iconCls ? ' '+a.iconCls : ''),
40640                            '" unselectable="on" />',
40641                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
40642                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
40643                              
40644                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
40645                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
40646                             '<span unselectable="on" qtip="' + tx + '">',
40647                              tx,
40648                              '</span></a>' ,
40649                     '</div>',
40650                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
40651                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
40652                  ];
40653         for(var i = 1, len = cols.length; i < len; i++){
40654             c = cols[i];
40655             colcls = 'x-t-' + tid + '-c' +i;
40656             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
40657             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
40658                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
40659                       "</div>");
40660          }
40661          
40662          buf.push(
40663             '</a>',
40664             '<div class="x-clear"></div></div>',
40665             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
40666             "</li>");
40667         
40668         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
40669             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
40670                                 n.nextSibling.ui.getEl(), buf.join(""));
40671         }else{
40672             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
40673         }
40674         var el = this.wrap.firstChild;
40675         this.elRow = el;
40676         this.elNode = el.firstChild;
40677         this.ranchor = el.childNodes[1];
40678         this.ctNode = this.wrap.childNodes[1];
40679         var cs = el.firstChild.childNodes;
40680         this.indentNode = cs[0];
40681         this.ecNode = cs[1];
40682         this.iconNode = cs[2];
40683         var index = 3;
40684         if(cb){
40685             this.checkbox = cs[3];
40686             index++;
40687         }
40688         this.anchor = cs[index];
40689         
40690         this.textNode = cs[index].firstChild;
40691         
40692         //el.on("click", this.onClick, this);
40693         //el.on("dblclick", this.onDblClick, this);
40694         
40695         
40696        // console.log(this);
40697     },
40698     initEvents : function(){
40699         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
40700         
40701             
40702         var a = this.ranchor;
40703
40704         var el = Roo.get(a);
40705
40706         if(Roo.isOpera){ // opera render bug ignores the CSS
40707             el.setStyle("text-decoration", "none");
40708         }
40709
40710         el.on("click", this.onClick, this);
40711         el.on("dblclick", this.onDblClick, this);
40712         el.on("contextmenu", this.onContextMenu, this);
40713         
40714     },
40715     
40716     /*onSelectedChange : function(state){
40717         if(state){
40718             this.focus();
40719             this.addClass("x-tree-selected");
40720         }else{
40721             //this.blur();
40722             this.removeClass("x-tree-selected");
40723         }
40724     },*/
40725     addClass : function(cls){
40726         if(this.elRow){
40727             Roo.fly(this.elRow).addClass(cls);
40728         }
40729         
40730     },
40731     
40732     
40733     removeClass : function(cls){
40734         if(this.elRow){
40735             Roo.fly(this.elRow).removeClass(cls);
40736         }
40737     }
40738
40739     
40740     
40741 });//<Script type="text/javascript">
40742
40743 /*
40744  * Based on:
40745  * Ext JS Library 1.1.1
40746  * Copyright(c) 2006-2007, Ext JS, LLC.
40747  *
40748  * Originally Released Under LGPL - original licence link has changed is not relivant.
40749  *
40750  * Fork - LGPL
40751  * <script type="text/javascript">
40752  */
40753  
40754
40755 /**
40756  * @class Roo.tree.ColumnTree
40757  * @extends Roo.tree.TreePanel
40758  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
40759  * @cfg {int} borderWidth  compined right/left border allowance
40760  * @constructor
40761  * @param {String/HTMLElement/Element} el The container element
40762  * @param {Object} config
40763  */
40764 Roo.tree.ColumnTree =  function(el, config)
40765 {
40766    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
40767    this.addEvents({
40768         /**
40769         * @event resize
40770         * Fire this event on a container when it resizes
40771         * @param {int} w Width
40772         * @param {int} h Height
40773         */
40774        "resize" : true
40775     });
40776     this.on('resize', this.onResize, this);
40777 };
40778
40779 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
40780     //lines:false,
40781     
40782     
40783     borderWidth: Roo.isBorderBox ? 0 : 2, 
40784     headEls : false,
40785     
40786     render : function(){
40787         // add the header.....
40788        
40789         Roo.tree.ColumnTree.superclass.render.apply(this);
40790         
40791         this.el.addClass('x-column-tree');
40792         
40793         this.headers = this.el.createChild(
40794             {cls:'x-tree-headers'},this.innerCt.dom);
40795    
40796         var cols = this.columns, c;
40797         var totalWidth = 0;
40798         this.headEls = [];
40799         var  len = cols.length;
40800         for(var i = 0; i < len; i++){
40801              c = cols[i];
40802              totalWidth += c.width;
40803             this.headEls.push(this.headers.createChild({
40804                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
40805                  cn: {
40806                      cls:'x-tree-hd-text',
40807                      html: c.header
40808                  },
40809                  style:'width:'+(c.width-this.borderWidth)+'px;'
40810              }));
40811         }
40812         this.headers.createChild({cls:'x-clear'});
40813         // prevent floats from wrapping when clipped
40814         this.headers.setWidth(totalWidth);
40815         //this.innerCt.setWidth(totalWidth);
40816         this.innerCt.setStyle({ overflow: 'auto' });
40817         this.onResize(this.width, this.height);
40818              
40819         
40820     },
40821     onResize : function(w,h)
40822     {
40823         this.height = h;
40824         this.width = w;
40825         // resize cols..
40826         this.innerCt.setWidth(this.width);
40827         this.innerCt.setHeight(this.height-20);
40828         
40829         // headers...
40830         var cols = this.columns, c;
40831         var totalWidth = 0;
40832         var expEl = false;
40833         var len = cols.length;
40834         for(var i = 0; i < len; i++){
40835             c = cols[i];
40836             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
40837                 // it's the expander..
40838                 expEl  = this.headEls[i];
40839                 continue;
40840             }
40841             totalWidth += c.width;
40842             
40843         }
40844         if (expEl) {
40845             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
40846         }
40847         this.headers.setWidth(w-20);
40848
40849         
40850         
40851         
40852     }
40853 });
40854 /*
40855  * Based on:
40856  * Ext JS Library 1.1.1
40857  * Copyright(c) 2006-2007, Ext JS, LLC.
40858  *
40859  * Originally Released Under LGPL - original licence link has changed is not relivant.
40860  *
40861  * Fork - LGPL
40862  * <script type="text/javascript">
40863  */
40864  
40865 /**
40866  * @class Roo.menu.Menu
40867  * @extends Roo.util.Observable
40868  * @children Roo.menu.Item Roo.menu.Separator Roo.menu.TextItem
40869  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
40870  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
40871  * @constructor
40872  * Creates a new Menu
40873  * @param {Object} config Configuration options
40874  */
40875 Roo.menu.Menu = function(config){
40876     
40877     Roo.menu.Menu.superclass.constructor.call(this, config);
40878     
40879     this.id = this.id || Roo.id();
40880     this.addEvents({
40881         /**
40882          * @event beforeshow
40883          * Fires before this menu is displayed
40884          * @param {Roo.menu.Menu} this
40885          */
40886         beforeshow : true,
40887         /**
40888          * @event beforehide
40889          * Fires before this menu is hidden
40890          * @param {Roo.menu.Menu} this
40891          */
40892         beforehide : true,
40893         /**
40894          * @event show
40895          * Fires after this menu is displayed
40896          * @param {Roo.menu.Menu} this
40897          */
40898         show : true,
40899         /**
40900          * @event hide
40901          * Fires after this menu is hidden
40902          * @param {Roo.menu.Menu} this
40903          */
40904         hide : true,
40905         /**
40906          * @event click
40907          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
40908          * @param {Roo.menu.Menu} this
40909          * @param {Roo.menu.Item} menuItem The menu item that was clicked
40910          * @param {Roo.EventObject} e
40911          */
40912         click : true,
40913         /**
40914          * @event mouseover
40915          * Fires when the mouse is hovering over this menu
40916          * @param {Roo.menu.Menu} this
40917          * @param {Roo.EventObject} e
40918          * @param {Roo.menu.Item} menuItem The menu item that was clicked
40919          */
40920         mouseover : true,
40921         /**
40922          * @event mouseout
40923          * Fires when the mouse exits this menu
40924          * @param {Roo.menu.Menu} this
40925          * @param {Roo.EventObject} e
40926          * @param {Roo.menu.Item} menuItem The menu item that was clicked
40927          */
40928         mouseout : true,
40929         /**
40930          * @event itemclick
40931          * Fires when a menu item contained in this menu is clicked
40932          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
40933          * @param {Roo.EventObject} e
40934          */
40935         itemclick: true
40936     });
40937     if (this.registerMenu) {
40938         Roo.menu.MenuMgr.register(this);
40939     }
40940     
40941     var mis = this.items;
40942     this.items = new Roo.util.MixedCollection();
40943     if(mis){
40944         this.add.apply(this, mis);
40945     }
40946 };
40947
40948 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
40949     /**
40950      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
40951      */
40952     minWidth : 120,
40953     /**
40954      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
40955      * for bottom-right shadow (defaults to "sides")
40956      */
40957     shadow : "sides",
40958     /**
40959      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
40960      * this menu (defaults to "tl-tr?")
40961      */
40962     subMenuAlign : "tl-tr?",
40963     /**
40964      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
40965      * relative to its element of origin (defaults to "tl-bl?")
40966      */
40967     defaultAlign : "tl-bl?",
40968     /**
40969      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
40970      */
40971     allowOtherMenus : false,
40972     /**
40973      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
40974      */
40975     registerMenu : true,
40976
40977     hidden:true,
40978
40979     // private
40980     render : function(){
40981         if(this.el){
40982             return;
40983         }
40984         var el = this.el = new Roo.Layer({
40985             cls: "x-menu",
40986             shadow:this.shadow,
40987             constrain: false,
40988             parentEl: this.parentEl || document.body,
40989             zindex:15000
40990         });
40991
40992         this.keyNav = new Roo.menu.MenuNav(this);
40993
40994         if(this.plain){
40995             el.addClass("x-menu-plain");
40996         }
40997         if(this.cls){
40998             el.addClass(this.cls);
40999         }
41000         // generic focus element
41001         this.focusEl = el.createChild({
41002             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
41003         });
41004         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
41005         //disabling touch- as it's causing issues ..
41006         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
41007         ul.on('click'   , this.onClick, this);
41008         
41009         
41010         ul.on("mouseover", this.onMouseOver, this);
41011         ul.on("mouseout", this.onMouseOut, this);
41012         this.items.each(function(item){
41013             if (item.hidden) {
41014                 return;
41015             }
41016             
41017             var li = document.createElement("li");
41018             li.className = "x-menu-list-item";
41019             ul.dom.appendChild(li);
41020             item.render(li, this);
41021         }, this);
41022         this.ul = ul;
41023         this.autoWidth();
41024     },
41025
41026     // private
41027     autoWidth : function(){
41028         var el = this.el, ul = this.ul;
41029         if(!el){
41030             return;
41031         }
41032         var w = this.width;
41033         if(w){
41034             el.setWidth(w);
41035         }else if(Roo.isIE){
41036             el.setWidth(this.minWidth);
41037             var t = el.dom.offsetWidth; // force recalc
41038             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
41039         }
41040     },
41041
41042     // private
41043     delayAutoWidth : function(){
41044         if(this.rendered){
41045             if(!this.awTask){
41046                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
41047             }
41048             this.awTask.delay(20);
41049         }
41050     },
41051
41052     // private
41053     findTargetItem : function(e){
41054         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
41055         if(t && t.menuItemId){
41056             return this.items.get(t.menuItemId);
41057         }
41058     },
41059
41060     // private
41061     onClick : function(e){
41062         Roo.log("menu.onClick");
41063         var t = this.findTargetItem(e);
41064         if(!t){
41065             return;
41066         }
41067         Roo.log(e);
41068         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
41069             if(t == this.activeItem && t.shouldDeactivate(e)){
41070                 this.activeItem.deactivate();
41071                 delete this.activeItem;
41072                 return;
41073             }
41074             if(t.canActivate){
41075                 this.setActiveItem(t, true);
41076             }
41077             return;
41078             
41079             
41080         }
41081         
41082         t.onClick(e);
41083         this.fireEvent("click", this, t, e);
41084     },
41085
41086     // private
41087     setActiveItem : function(item, autoExpand){
41088         if(item != this.activeItem){
41089             if(this.activeItem){
41090                 this.activeItem.deactivate();
41091             }
41092             this.activeItem = item;
41093             item.activate(autoExpand);
41094         }else if(autoExpand){
41095             item.expandMenu();
41096         }
41097     },
41098
41099     // private
41100     tryActivate : function(start, step){
41101         var items = this.items;
41102         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
41103             var item = items.get(i);
41104             if(!item.disabled && item.canActivate){
41105                 this.setActiveItem(item, false);
41106                 return item;
41107             }
41108         }
41109         return false;
41110     },
41111
41112     // private
41113     onMouseOver : function(e){
41114         var t;
41115         if(t = this.findTargetItem(e)){
41116             if(t.canActivate && !t.disabled){
41117                 this.setActiveItem(t, true);
41118             }
41119         }
41120         this.fireEvent("mouseover", this, e, t);
41121     },
41122
41123     // private
41124     onMouseOut : function(e){
41125         var t;
41126         if(t = this.findTargetItem(e)){
41127             if(t == this.activeItem && t.shouldDeactivate(e)){
41128                 this.activeItem.deactivate();
41129                 delete this.activeItem;
41130             }
41131         }
41132         this.fireEvent("mouseout", this, e, t);
41133     },
41134
41135     /**
41136      * Read-only.  Returns true if the menu is currently displayed, else false.
41137      * @type Boolean
41138      */
41139     isVisible : function(){
41140         return this.el && !this.hidden;
41141     },
41142
41143     /**
41144      * Displays this menu relative to another element
41145      * @param {String/HTMLElement/Roo.Element} element The element to align to
41146      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
41147      * the element (defaults to this.defaultAlign)
41148      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
41149      */
41150     show : function(el, pos, parentMenu){
41151         this.parentMenu = parentMenu;
41152         if(!this.el){
41153             this.render();
41154         }
41155         this.fireEvent("beforeshow", this);
41156         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
41157     },
41158
41159     /**
41160      * Displays this menu at a specific xy position
41161      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
41162      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
41163      */
41164     showAt : function(xy, parentMenu, /* private: */_e){
41165         this.parentMenu = parentMenu;
41166         if(!this.el){
41167             this.render();
41168         }
41169         if(_e !== false){
41170             this.fireEvent("beforeshow", this);
41171             xy = this.el.adjustForConstraints(xy);
41172         }
41173         this.el.setXY(xy);
41174         this.el.show();
41175         this.hidden = false;
41176         this.focus();
41177         this.fireEvent("show", this);
41178     },
41179
41180     focus : function(){
41181         if(!this.hidden){
41182             this.doFocus.defer(50, this);
41183         }
41184     },
41185
41186     doFocus : function(){
41187         if(!this.hidden){
41188             this.focusEl.focus();
41189         }
41190     },
41191
41192     /**
41193      * Hides this menu and optionally all parent menus
41194      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
41195      */
41196     hide : function(deep){
41197         if(this.el && this.isVisible()){
41198             this.fireEvent("beforehide", this);
41199             if(this.activeItem){
41200                 this.activeItem.deactivate();
41201                 this.activeItem = null;
41202             }
41203             this.el.hide();
41204             this.hidden = true;
41205             this.fireEvent("hide", this);
41206         }
41207         if(deep === true && this.parentMenu){
41208             this.parentMenu.hide(true);
41209         }
41210     },
41211
41212     /**
41213      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
41214      * Any of the following are valid:
41215      * <ul>
41216      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
41217      * <li>An HTMLElement object which will be converted to a menu item</li>
41218      * <li>A menu item config object that will be created as a new menu item</li>
41219      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
41220      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
41221      * </ul>
41222      * Usage:
41223      * <pre><code>
41224 // Create the menu
41225 var menu = new Roo.menu.Menu();
41226
41227 // Create a menu item to add by reference
41228 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
41229
41230 // Add a bunch of items at once using different methods.
41231 // Only the last item added will be returned.
41232 var item = menu.add(
41233     menuItem,                // add existing item by ref
41234     'Dynamic Item',          // new TextItem
41235     '-',                     // new separator
41236     { text: 'Config Item' }  // new item by config
41237 );
41238 </code></pre>
41239      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
41240      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
41241      */
41242     add : function(){
41243         var a = arguments, l = a.length, item;
41244         for(var i = 0; i < l; i++){
41245             var el = a[i];
41246             if ((typeof(el) == "object") && el.xtype && el.xns) {
41247                 el = Roo.factory(el, Roo.menu);
41248             }
41249             
41250             if(el.render){ // some kind of Item
41251                 item = this.addItem(el);
41252             }else if(typeof el == "string"){ // string
41253                 if(el == "separator" || el == "-"){
41254                     item = this.addSeparator();
41255                 }else{
41256                     item = this.addText(el);
41257                 }
41258             }else if(el.tagName || el.el){ // element
41259                 item = this.addElement(el);
41260             }else if(typeof el == "object"){ // must be menu item config?
41261                 item = this.addMenuItem(el);
41262             }
41263         }
41264         return item;
41265     },
41266
41267     /**
41268      * Returns this menu's underlying {@link Roo.Element} object
41269      * @return {Roo.Element} The element
41270      */
41271     getEl : function(){
41272         if(!this.el){
41273             this.render();
41274         }
41275         return this.el;
41276     },
41277
41278     /**
41279      * Adds a separator bar to the menu
41280      * @return {Roo.menu.Item} The menu item that was added
41281      */
41282     addSeparator : function(){
41283         return this.addItem(new Roo.menu.Separator());
41284     },
41285
41286     /**
41287      * Adds an {@link Roo.Element} object to the menu
41288      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
41289      * @return {Roo.menu.Item} The menu item that was added
41290      */
41291     addElement : function(el){
41292         return this.addItem(new Roo.menu.BaseItem(el));
41293     },
41294
41295     /**
41296      * Adds an existing object based on {@link Roo.menu.Item} to the menu
41297      * @param {Roo.menu.Item} item The menu item to add
41298      * @return {Roo.menu.Item} The menu item that was added
41299      */
41300     addItem : function(item){
41301         this.items.add(item);
41302         if(this.ul){
41303             var li = document.createElement("li");
41304             li.className = "x-menu-list-item";
41305             this.ul.dom.appendChild(li);
41306             item.render(li, this);
41307             this.delayAutoWidth();
41308         }
41309         return item;
41310     },
41311
41312     /**
41313      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
41314      * @param {Object} config A MenuItem config object
41315      * @return {Roo.menu.Item} The menu item that was added
41316      */
41317     addMenuItem : function(config){
41318         if(!(config instanceof Roo.menu.Item)){
41319             if(typeof config.checked == "boolean"){ // must be check menu item config?
41320                 config = new Roo.menu.CheckItem(config);
41321             }else{
41322                 config = new Roo.menu.Item(config);
41323             }
41324         }
41325         return this.addItem(config);
41326     },
41327
41328     /**
41329      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
41330      * @param {String} text The text to display in the menu item
41331      * @return {Roo.menu.Item} The menu item that was added
41332      */
41333     addText : function(text){
41334         return this.addItem(new Roo.menu.TextItem({ text : text }));
41335     },
41336
41337     /**
41338      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
41339      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
41340      * @param {Roo.menu.Item} item The menu item to add
41341      * @return {Roo.menu.Item} The menu item that was added
41342      */
41343     insert : function(index, item){
41344         this.items.insert(index, item);
41345         if(this.ul){
41346             var li = document.createElement("li");
41347             li.className = "x-menu-list-item";
41348             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
41349             item.render(li, this);
41350             this.delayAutoWidth();
41351         }
41352         return item;
41353     },
41354
41355     /**
41356      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
41357      * @param {Roo.menu.Item} item The menu item to remove
41358      */
41359     remove : function(item){
41360         this.items.removeKey(item.id);
41361         item.destroy();
41362     },
41363
41364     /**
41365      * Removes and destroys all items in the menu
41366      */
41367     removeAll : function(){
41368         var f;
41369         while(f = this.items.first()){
41370             this.remove(f);
41371         }
41372     }
41373 });
41374
41375 // MenuNav is a private utility class used internally by the Menu
41376 Roo.menu.MenuNav = function(menu){
41377     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
41378     this.scope = this.menu = menu;
41379 };
41380
41381 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
41382     doRelay : function(e, h){
41383         var k = e.getKey();
41384         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
41385             this.menu.tryActivate(0, 1);
41386             return false;
41387         }
41388         return h.call(this.scope || this, e, this.menu);
41389     },
41390
41391     up : function(e, m){
41392         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
41393             m.tryActivate(m.items.length-1, -1);
41394         }
41395     },
41396
41397     down : function(e, m){
41398         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
41399             m.tryActivate(0, 1);
41400         }
41401     },
41402
41403     right : function(e, m){
41404         if(m.activeItem){
41405             m.activeItem.expandMenu(true);
41406         }
41407     },
41408
41409     left : function(e, m){
41410         m.hide();
41411         if(m.parentMenu && m.parentMenu.activeItem){
41412             m.parentMenu.activeItem.activate();
41413         }
41414     },
41415
41416     enter : function(e, m){
41417         if(m.activeItem){
41418             e.stopPropagation();
41419             m.activeItem.onClick(e);
41420             m.fireEvent("click", this, m.activeItem);
41421             return true;
41422         }
41423     }
41424 });/*
41425  * Based on:
41426  * Ext JS Library 1.1.1
41427  * Copyright(c) 2006-2007, Ext JS, LLC.
41428  *
41429  * Originally Released Under LGPL - original licence link has changed is not relivant.
41430  *
41431  * Fork - LGPL
41432  * <script type="text/javascript">
41433  */
41434  
41435 /**
41436  * @class Roo.menu.MenuMgr
41437  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
41438  * @static
41439  */
41440 Roo.menu.MenuMgr = function(){
41441    var menus, active, groups = {}, attached = false, lastShow = new Date();
41442
41443    // private - called when first menu is created
41444    function init(){
41445        menus = {};
41446        active = new Roo.util.MixedCollection();
41447        Roo.get(document).addKeyListener(27, function(){
41448            if(active.length > 0){
41449                hideAll();
41450            }
41451        });
41452    }
41453
41454    // private
41455    function hideAll(){
41456        if(active && active.length > 0){
41457            var c = active.clone();
41458            c.each(function(m){
41459                m.hide();
41460            });
41461        }
41462    }
41463
41464    // private
41465    function onHide(m){
41466        active.remove(m);
41467        if(active.length < 1){
41468            Roo.get(document).un("mousedown", onMouseDown);
41469            attached = false;
41470        }
41471    }
41472
41473    // private
41474    function onShow(m){
41475        var last = active.last();
41476        lastShow = new Date();
41477        active.add(m);
41478        if(!attached){
41479            Roo.get(document).on("mousedown", onMouseDown);
41480            attached = true;
41481        }
41482        if(m.parentMenu){
41483           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
41484           m.parentMenu.activeChild = m;
41485        }else if(last && last.isVisible()){
41486           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
41487        }
41488    }
41489
41490    // private
41491    function onBeforeHide(m){
41492        if(m.activeChild){
41493            m.activeChild.hide();
41494        }
41495        if(m.autoHideTimer){
41496            clearTimeout(m.autoHideTimer);
41497            delete m.autoHideTimer;
41498        }
41499    }
41500
41501    // private
41502    function onBeforeShow(m){
41503        var pm = m.parentMenu;
41504        if(!pm && !m.allowOtherMenus){
41505            hideAll();
41506        }else if(pm && pm.activeChild && active != m){
41507            pm.activeChild.hide();
41508        }
41509    }
41510
41511    // private
41512    function onMouseDown(e){
41513        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
41514            hideAll();
41515        }
41516    }
41517
41518    // private
41519    function onBeforeCheck(mi, state){
41520        if(state){
41521            var g = groups[mi.group];
41522            for(var i = 0, l = g.length; i < l; i++){
41523                if(g[i] != mi){
41524                    g[i].setChecked(false);
41525                }
41526            }
41527        }
41528    }
41529
41530    return {
41531
41532        /**
41533         * Hides all menus that are currently visible
41534         */
41535        hideAll : function(){
41536             hideAll();  
41537        },
41538
41539        // private
41540        register : function(menu){
41541            if(!menus){
41542                init();
41543            }
41544            menus[menu.id] = menu;
41545            menu.on("beforehide", onBeforeHide);
41546            menu.on("hide", onHide);
41547            menu.on("beforeshow", onBeforeShow);
41548            menu.on("show", onShow);
41549            var g = menu.group;
41550            if(g && menu.events["checkchange"]){
41551                if(!groups[g]){
41552                    groups[g] = [];
41553                }
41554                groups[g].push(menu);
41555                menu.on("checkchange", onCheck);
41556            }
41557        },
41558
41559         /**
41560          * Returns a {@link Roo.menu.Menu} object
41561          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
41562          * be used to generate and return a new Menu instance.
41563          */
41564        get : function(menu){
41565            if(typeof menu == "string"){ // menu id
41566                return menus[menu];
41567            }else if(menu.events){  // menu instance
41568                return menu;
41569            }else if(typeof menu.length == 'number'){ // array of menu items?
41570                return new Roo.menu.Menu({items:menu});
41571            }else{ // otherwise, must be a config
41572                return new Roo.menu.Menu(menu);
41573            }
41574        },
41575
41576        // private
41577        unregister : function(menu){
41578            delete menus[menu.id];
41579            menu.un("beforehide", onBeforeHide);
41580            menu.un("hide", onHide);
41581            menu.un("beforeshow", onBeforeShow);
41582            menu.un("show", onShow);
41583            var g = menu.group;
41584            if(g && menu.events["checkchange"]){
41585                groups[g].remove(menu);
41586                menu.un("checkchange", onCheck);
41587            }
41588        },
41589
41590        // private
41591        registerCheckable : function(menuItem){
41592            var g = menuItem.group;
41593            if(g){
41594                if(!groups[g]){
41595                    groups[g] = [];
41596                }
41597                groups[g].push(menuItem);
41598                menuItem.on("beforecheckchange", onBeforeCheck);
41599            }
41600        },
41601
41602        // private
41603        unregisterCheckable : function(menuItem){
41604            var g = menuItem.group;
41605            if(g){
41606                groups[g].remove(menuItem);
41607                menuItem.un("beforecheckchange", onBeforeCheck);
41608            }
41609        }
41610    };
41611 }();/*
41612  * Based on:
41613  * Ext JS Library 1.1.1
41614  * Copyright(c) 2006-2007, Ext JS, LLC.
41615  *
41616  * Originally Released Under LGPL - original licence link has changed is not relivant.
41617  *
41618  * Fork - LGPL
41619  * <script type="text/javascript">
41620  */
41621  
41622
41623 /**
41624  * @class Roo.menu.BaseItem
41625  * @extends Roo.Component
41626  * @abstract
41627  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
41628  * management and base configuration options shared by all menu components.
41629  * @constructor
41630  * Creates a new BaseItem
41631  * @param {Object} config Configuration options
41632  */
41633 Roo.menu.BaseItem = function(config){
41634     Roo.menu.BaseItem.superclass.constructor.call(this, config);
41635
41636     this.addEvents({
41637         /**
41638          * @event click
41639          * Fires when this item is clicked
41640          * @param {Roo.menu.BaseItem} this
41641          * @param {Roo.EventObject} e
41642          */
41643         click: true,
41644         /**
41645          * @event activate
41646          * Fires when this item is activated
41647          * @param {Roo.menu.BaseItem} this
41648          */
41649         activate : true,
41650         /**
41651          * @event deactivate
41652          * Fires when this item is deactivated
41653          * @param {Roo.menu.BaseItem} this
41654          */
41655         deactivate : true
41656     });
41657
41658     if(this.handler){
41659         this.on("click", this.handler, this.scope, true);
41660     }
41661 };
41662
41663 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
41664     /**
41665      * @cfg {Function} handler
41666      * A function that will handle the click event of this menu item (defaults to undefined)
41667      */
41668     /**
41669      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
41670      */
41671     canActivate : false,
41672     
41673      /**
41674      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
41675      */
41676     hidden: false,
41677     
41678     /**
41679      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
41680      */
41681     activeClass : "x-menu-item-active",
41682     /**
41683      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
41684      */
41685     hideOnClick : true,
41686     /**
41687      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
41688      */
41689     hideDelay : 100,
41690
41691     // private
41692     ctype: "Roo.menu.BaseItem",
41693
41694     // private
41695     actionMode : "container",
41696
41697     // private
41698     render : function(container, parentMenu){
41699         this.parentMenu = parentMenu;
41700         Roo.menu.BaseItem.superclass.render.call(this, container);
41701         this.container.menuItemId = this.id;
41702     },
41703
41704     // private
41705     onRender : function(container, position){
41706         this.el = Roo.get(this.el);
41707         container.dom.appendChild(this.el.dom);
41708     },
41709
41710     // private
41711     onClick : function(e){
41712         if(!this.disabled && this.fireEvent("click", this, e) !== false
41713                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
41714             this.handleClick(e);
41715         }else{
41716             e.stopEvent();
41717         }
41718     },
41719
41720     // private
41721     activate : function(){
41722         if(this.disabled){
41723             return false;
41724         }
41725         var li = this.container;
41726         li.addClass(this.activeClass);
41727         this.region = li.getRegion().adjust(2, 2, -2, -2);
41728         this.fireEvent("activate", this);
41729         return true;
41730     },
41731
41732     // private
41733     deactivate : function(){
41734         this.container.removeClass(this.activeClass);
41735         this.fireEvent("deactivate", this);
41736     },
41737
41738     // private
41739     shouldDeactivate : function(e){
41740         return !this.region || !this.region.contains(e.getPoint());
41741     },
41742
41743     // private
41744     handleClick : function(e){
41745         if(this.hideOnClick){
41746             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
41747         }
41748     },
41749
41750     // private
41751     expandMenu : function(autoActivate){
41752         // do nothing
41753     },
41754
41755     // private
41756     hideMenu : function(){
41757         // do nothing
41758     }
41759 });/*
41760  * Based on:
41761  * Ext JS Library 1.1.1
41762  * Copyright(c) 2006-2007, Ext JS, LLC.
41763  *
41764  * Originally Released Under LGPL - original licence link has changed is not relivant.
41765  *
41766  * Fork - LGPL
41767  * <script type="text/javascript">
41768  */
41769  
41770 /**
41771  * @class Roo.menu.Adapter
41772  * @extends Roo.menu.BaseItem
41773  * @abstract
41774  * 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.
41775  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
41776  * @constructor
41777  * Creates a new Adapter
41778  * @param {Object} config Configuration options
41779  */
41780 Roo.menu.Adapter = function(component, config){
41781     Roo.menu.Adapter.superclass.constructor.call(this, config);
41782     this.component = component;
41783 };
41784 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
41785     // private
41786     canActivate : true,
41787
41788     // private
41789     onRender : function(container, position){
41790         this.component.render(container);
41791         this.el = this.component.getEl();
41792     },
41793
41794     // private
41795     activate : function(){
41796         if(this.disabled){
41797             return false;
41798         }
41799         this.component.focus();
41800         this.fireEvent("activate", this);
41801         return true;
41802     },
41803
41804     // private
41805     deactivate : function(){
41806         this.fireEvent("deactivate", this);
41807     },
41808
41809     // private
41810     disable : function(){
41811         this.component.disable();
41812         Roo.menu.Adapter.superclass.disable.call(this);
41813     },
41814
41815     // private
41816     enable : function(){
41817         this.component.enable();
41818         Roo.menu.Adapter.superclass.enable.call(this);
41819     }
41820 });/*
41821  * Based on:
41822  * Ext JS Library 1.1.1
41823  * Copyright(c) 2006-2007, Ext JS, LLC.
41824  *
41825  * Originally Released Under LGPL - original licence link has changed is not relivant.
41826  *
41827  * Fork - LGPL
41828  * <script type="text/javascript">
41829  */
41830
41831 /**
41832  * @class Roo.menu.TextItem
41833  * @extends Roo.menu.BaseItem
41834  * Adds a static text string to a menu, usually used as either a heading or group separator.
41835  * Note: old style constructor with text is still supported.
41836  * 
41837  * @constructor
41838  * Creates a new TextItem
41839  * @param {Object} cfg Configuration
41840  */
41841 Roo.menu.TextItem = function(cfg){
41842     if (typeof(cfg) == 'string') {
41843         this.text = cfg;
41844     } else {
41845         Roo.apply(this,cfg);
41846     }
41847     
41848     Roo.menu.TextItem.superclass.constructor.call(this);
41849 };
41850
41851 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
41852     /**
41853      * @cfg {String} text Text to show on item.
41854      */
41855     text : '',
41856     
41857     /**
41858      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
41859      */
41860     hideOnClick : false,
41861     /**
41862      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
41863      */
41864     itemCls : "x-menu-text",
41865
41866     // private
41867     onRender : function(){
41868         var s = document.createElement("span");
41869         s.className = this.itemCls;
41870         s.innerHTML = this.text;
41871         this.el = s;
41872         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
41873     }
41874 });/*
41875  * Based on:
41876  * Ext JS Library 1.1.1
41877  * Copyright(c) 2006-2007, Ext JS, LLC.
41878  *
41879  * Originally Released Under LGPL - original licence link has changed is not relivant.
41880  *
41881  * Fork - LGPL
41882  * <script type="text/javascript">
41883  */
41884
41885 /**
41886  * @class Roo.menu.Separator
41887  * @extends Roo.menu.BaseItem
41888  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
41889  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
41890  * @constructor
41891  * @param {Object} config Configuration options
41892  */
41893 Roo.menu.Separator = function(config){
41894     Roo.menu.Separator.superclass.constructor.call(this, config);
41895 };
41896
41897 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
41898     /**
41899      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
41900      */
41901     itemCls : "x-menu-sep",
41902     /**
41903      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
41904      */
41905     hideOnClick : false,
41906
41907     // private
41908     onRender : function(li){
41909         var s = document.createElement("span");
41910         s.className = this.itemCls;
41911         s.innerHTML = "&#160;";
41912         this.el = s;
41913         li.addClass("x-menu-sep-li");
41914         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
41915     }
41916 });/*
41917  * Based on:
41918  * Ext JS Library 1.1.1
41919  * Copyright(c) 2006-2007, Ext JS, LLC.
41920  *
41921  * Originally Released Under LGPL - original licence link has changed is not relivant.
41922  *
41923  * Fork - LGPL
41924  * <script type="text/javascript">
41925  */
41926 /**
41927  * @class Roo.menu.Item
41928  * @extends Roo.menu.BaseItem
41929  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
41930  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
41931  * activation and click handling.
41932  * @constructor
41933  * Creates a new Item
41934  * @param {Object} config Configuration options
41935  */
41936 Roo.menu.Item = function(config){
41937     Roo.menu.Item.superclass.constructor.call(this, config);
41938     if(this.menu){
41939         this.menu = Roo.menu.MenuMgr.get(this.menu);
41940     }
41941 };
41942 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
41943     /**
41944      * @cfg {Roo.menu.Menu} menu
41945      * A Sub menu
41946      */
41947     /**
41948      * @cfg {String} text
41949      * The text to show on the menu item.
41950      */
41951     text: '',
41952      /**
41953      * @cfg {String} html to render in menu
41954      * The text to show on the menu item (HTML version).
41955      */
41956     html: '',
41957     /**
41958      * @cfg {String} icon
41959      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
41960      */
41961     icon: undefined,
41962     /**
41963      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
41964      */
41965     itemCls : "x-menu-item",
41966     /**
41967      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
41968      */
41969     canActivate : true,
41970     /**
41971      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
41972      */
41973     showDelay: 200,
41974     // doc'd in BaseItem
41975     hideDelay: 200,
41976
41977     // private
41978     ctype: "Roo.menu.Item",
41979     
41980     // private
41981     onRender : function(container, position){
41982         var el = document.createElement("a");
41983         el.hideFocus = true;
41984         el.unselectable = "on";
41985         el.href = this.href || "#";
41986         if(this.hrefTarget){
41987             el.target = this.hrefTarget;
41988         }
41989         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
41990         
41991         var html = this.html.length ? this.html  : String.format('{0}',this.text);
41992         
41993         el.innerHTML = String.format(
41994                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
41995                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
41996         this.el = el;
41997         Roo.menu.Item.superclass.onRender.call(this, container, position);
41998     },
41999
42000     /**
42001      * Sets the text to display in this menu item
42002      * @param {String} text The text to display
42003      * @param {Boolean} isHTML true to indicate text is pure html.
42004      */
42005     setText : function(text, isHTML){
42006         if (isHTML) {
42007             this.html = text;
42008         } else {
42009             this.text = text;
42010             this.html = '';
42011         }
42012         if(this.rendered){
42013             var html = this.html.length ? this.html  : String.format('{0}',this.text);
42014      
42015             this.el.update(String.format(
42016                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
42017                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
42018             this.parentMenu.autoWidth();
42019         }
42020     },
42021
42022     // private
42023     handleClick : function(e){
42024         if(!this.href){ // if no link defined, stop the event automatically
42025             e.stopEvent();
42026         }
42027         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
42028     },
42029
42030     // private
42031     activate : function(autoExpand){
42032         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
42033             this.focus();
42034             if(autoExpand){
42035                 this.expandMenu();
42036             }
42037         }
42038         return true;
42039     },
42040
42041     // private
42042     shouldDeactivate : function(e){
42043         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
42044             if(this.menu && this.menu.isVisible()){
42045                 return !this.menu.getEl().getRegion().contains(e.getPoint());
42046             }
42047             return true;
42048         }
42049         return false;
42050     },
42051
42052     // private
42053     deactivate : function(){
42054         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
42055         this.hideMenu();
42056     },
42057
42058     // private
42059     expandMenu : function(autoActivate){
42060         if(!this.disabled && this.menu){
42061             clearTimeout(this.hideTimer);
42062             delete this.hideTimer;
42063             if(!this.menu.isVisible() && !this.showTimer){
42064                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
42065             }else if (this.menu.isVisible() && autoActivate){
42066                 this.menu.tryActivate(0, 1);
42067             }
42068         }
42069     },
42070
42071     // private
42072     deferExpand : function(autoActivate){
42073         delete this.showTimer;
42074         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
42075         if(autoActivate){
42076             this.menu.tryActivate(0, 1);
42077         }
42078     },
42079
42080     // private
42081     hideMenu : function(){
42082         clearTimeout(this.showTimer);
42083         delete this.showTimer;
42084         if(!this.hideTimer && this.menu && this.menu.isVisible()){
42085             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
42086         }
42087     },
42088
42089     // private
42090     deferHide : function(){
42091         delete this.hideTimer;
42092         this.menu.hide();
42093     }
42094 });/*
42095  * Based on:
42096  * Ext JS Library 1.1.1
42097  * Copyright(c) 2006-2007, Ext JS, LLC.
42098  *
42099  * Originally Released Under LGPL - original licence link has changed is not relivant.
42100  *
42101  * Fork - LGPL
42102  * <script type="text/javascript">
42103  */
42104  
42105 /**
42106  * @class Roo.menu.CheckItem
42107  * @extends Roo.menu.Item
42108  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
42109  * @constructor
42110  * Creates a new CheckItem
42111  * @param {Object} config Configuration options
42112  */
42113 Roo.menu.CheckItem = function(config){
42114     Roo.menu.CheckItem.superclass.constructor.call(this, config);
42115     this.addEvents({
42116         /**
42117          * @event beforecheckchange
42118          * Fires before the checked value is set, providing an opportunity to cancel if needed
42119          * @param {Roo.menu.CheckItem} this
42120          * @param {Boolean} checked The new checked value that will be set
42121          */
42122         "beforecheckchange" : true,
42123         /**
42124          * @event checkchange
42125          * Fires after the checked value has been set
42126          * @param {Roo.menu.CheckItem} this
42127          * @param {Boolean} checked The checked value that was set
42128          */
42129         "checkchange" : true
42130     });
42131     if(this.checkHandler){
42132         this.on('checkchange', this.checkHandler, this.scope);
42133     }
42134 };
42135 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
42136     /**
42137      * @cfg {String} group
42138      * All check items with the same group name will automatically be grouped into a single-select
42139      * radio button group (defaults to '')
42140      */
42141     /**
42142      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
42143      */
42144     itemCls : "x-menu-item x-menu-check-item",
42145     /**
42146      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
42147      */
42148     groupClass : "x-menu-group-item",
42149
42150     /**
42151      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
42152      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
42153      * initialized with checked = true will be rendered as checked.
42154      */
42155     checked: false,
42156
42157     // private
42158     ctype: "Roo.menu.CheckItem",
42159
42160     // private
42161     onRender : function(c){
42162         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
42163         if(this.group){
42164             this.el.addClass(this.groupClass);
42165         }
42166         Roo.menu.MenuMgr.registerCheckable(this);
42167         if(this.checked){
42168             this.checked = false;
42169             this.setChecked(true, true);
42170         }
42171     },
42172
42173     // private
42174     destroy : function(){
42175         if(this.rendered){
42176             Roo.menu.MenuMgr.unregisterCheckable(this);
42177         }
42178         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
42179     },
42180
42181     /**
42182      * Set the checked state of this item
42183      * @param {Boolean} checked The new checked value
42184      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
42185      */
42186     setChecked : function(state, suppressEvent){
42187         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
42188             if(this.container){
42189                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
42190             }
42191             this.checked = state;
42192             if(suppressEvent !== true){
42193                 this.fireEvent("checkchange", this, state);
42194             }
42195         }
42196     },
42197
42198     // private
42199     handleClick : function(e){
42200        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
42201            this.setChecked(!this.checked);
42202        }
42203        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
42204     }
42205 });/*
42206  * Based on:
42207  * Ext JS Library 1.1.1
42208  * Copyright(c) 2006-2007, Ext JS, LLC.
42209  *
42210  * Originally Released Under LGPL - original licence link has changed is not relivant.
42211  *
42212  * Fork - LGPL
42213  * <script type="text/javascript">
42214  */
42215  
42216 /**
42217  * @class Roo.menu.DateItem
42218  * @extends Roo.menu.Adapter
42219  * A menu item that wraps the {@link Roo.DatPicker} component.
42220  * @constructor
42221  * Creates a new DateItem
42222  * @param {Object} config Configuration options
42223  */
42224 Roo.menu.DateItem = function(config){
42225     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
42226     /** The Roo.DatePicker object @type Roo.DatePicker */
42227     this.picker = this.component;
42228     this.addEvents({select: true});
42229     
42230     this.picker.on("render", function(picker){
42231         picker.getEl().swallowEvent("click");
42232         picker.container.addClass("x-menu-date-item");
42233     });
42234
42235     this.picker.on("select", this.onSelect, this);
42236 };
42237
42238 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
42239     // private
42240     onSelect : function(picker, date){
42241         this.fireEvent("select", this, date, picker);
42242         Roo.menu.DateItem.superclass.handleClick.call(this);
42243     }
42244 });/*
42245  * Based on:
42246  * Ext JS Library 1.1.1
42247  * Copyright(c) 2006-2007, Ext JS, LLC.
42248  *
42249  * Originally Released Under LGPL - original licence link has changed is not relivant.
42250  *
42251  * Fork - LGPL
42252  * <script type="text/javascript">
42253  */
42254  
42255 /**
42256  * @class Roo.menu.ColorItem
42257  * @extends Roo.menu.Adapter
42258  * A menu item that wraps the {@link Roo.ColorPalette} component.
42259  * @constructor
42260  * Creates a new ColorItem
42261  * @param {Object} config Configuration options
42262  */
42263 Roo.menu.ColorItem = function(config){
42264     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
42265     /** The Roo.ColorPalette object @type Roo.ColorPalette */
42266     this.palette = this.component;
42267     this.relayEvents(this.palette, ["select"]);
42268     if(this.selectHandler){
42269         this.on('select', this.selectHandler, this.scope);
42270     }
42271 };
42272 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
42273  * Based on:
42274  * Ext JS Library 1.1.1
42275  * Copyright(c) 2006-2007, Ext JS, LLC.
42276  *
42277  * Originally Released Under LGPL - original licence link has changed is not relivant.
42278  *
42279  * Fork - LGPL
42280  * <script type="text/javascript">
42281  */
42282  
42283
42284 /**
42285  * @class Roo.menu.DateMenu
42286  * @extends Roo.menu.Menu
42287  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
42288  * @constructor
42289  * Creates a new DateMenu
42290  * @param {Object} config Configuration options
42291  */
42292 Roo.menu.DateMenu = function(config){
42293     Roo.menu.DateMenu.superclass.constructor.call(this, config);
42294     this.plain = true;
42295     var di = new Roo.menu.DateItem(config);
42296     this.add(di);
42297     /**
42298      * The {@link Roo.DatePicker} instance for this DateMenu
42299      * @type DatePicker
42300      */
42301     this.picker = di.picker;
42302     /**
42303      * @event select
42304      * @param {DatePicker} picker
42305      * @param {Date} date
42306      */
42307     this.relayEvents(di, ["select"]);
42308     this.on('beforeshow', function(){
42309         if(this.picker){
42310             this.picker.hideMonthPicker(false);
42311         }
42312     }, this);
42313 };
42314 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
42315     cls:'x-date-menu'
42316 });/*
42317  * Based on:
42318  * Ext JS Library 1.1.1
42319  * Copyright(c) 2006-2007, Ext JS, LLC.
42320  *
42321  * Originally Released Under LGPL - original licence link has changed is not relivant.
42322  *
42323  * Fork - LGPL
42324  * <script type="text/javascript">
42325  */
42326  
42327
42328 /**
42329  * @class Roo.menu.ColorMenu
42330  * @extends Roo.menu.Menu
42331  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
42332  * @constructor
42333  * Creates a new ColorMenu
42334  * @param {Object} config Configuration options
42335  */
42336 Roo.menu.ColorMenu = function(config){
42337     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
42338     this.plain = true;
42339     var ci = new Roo.menu.ColorItem(config);
42340     this.add(ci);
42341     /**
42342      * The {@link Roo.ColorPalette} instance for this ColorMenu
42343      * @type ColorPalette
42344      */
42345     this.palette = ci.palette;
42346     /**
42347      * @event select
42348      * @param {ColorPalette} palette
42349      * @param {String} color
42350      */
42351     this.relayEvents(ci, ["select"]);
42352 };
42353 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
42354  * Based on:
42355  * Ext JS Library 1.1.1
42356  * Copyright(c) 2006-2007, Ext JS, LLC.
42357  *
42358  * Originally Released Under LGPL - original licence link has changed is not relivant.
42359  *
42360  * Fork - LGPL
42361  * <script type="text/javascript">
42362  */
42363  
42364 /**
42365  * @class Roo.form.TextItem
42366  * @extends Roo.BoxComponent
42367  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
42368  * @constructor
42369  * Creates a new TextItem
42370  * @param {Object} config Configuration options
42371  */
42372 Roo.form.TextItem = function(config){
42373     Roo.form.TextItem.superclass.constructor.call(this, config);
42374 };
42375
42376 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
42377     
42378     /**
42379      * @cfg {String} tag the tag for this item (default div)
42380      */
42381     tag : 'div',
42382     /**
42383      * @cfg {String} html the content for this item
42384      */
42385     html : '',
42386     
42387     getAutoCreate : function()
42388     {
42389         var cfg = {
42390             id: this.id,
42391             tag: this.tag,
42392             html: this.html,
42393             cls: 'x-form-item'
42394         };
42395         
42396         return cfg;
42397         
42398     },
42399     
42400     onRender : function(ct, position)
42401     {
42402         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
42403         
42404         if(!this.el){
42405             var cfg = this.getAutoCreate();
42406             if(!cfg.name){
42407                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
42408             }
42409             if (!cfg.name.length) {
42410                 delete cfg.name;
42411             }
42412             this.el = ct.createChild(cfg, position);
42413         }
42414     },
42415     /*
42416      * setHTML
42417      * @param {String} html update the Contents of the element.
42418      */
42419     setHTML : function(html)
42420     {
42421         this.fieldEl.dom.innerHTML = html;
42422     }
42423     
42424 });/*
42425  * Based on:
42426  * Ext JS Library 1.1.1
42427  * Copyright(c) 2006-2007, Ext JS, LLC.
42428  *
42429  * Originally Released Under LGPL - original licence link has changed is not relivant.
42430  *
42431  * Fork - LGPL
42432  * <script type="text/javascript">
42433  */
42434  
42435 /**
42436  * @class Roo.form.Field
42437  * @extends Roo.BoxComponent
42438  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
42439  * @constructor
42440  * Creates a new Field
42441  * @param {Object} config Configuration options
42442  */
42443 Roo.form.Field = function(config){
42444     Roo.form.Field.superclass.constructor.call(this, config);
42445 };
42446
42447 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
42448     /**
42449      * @cfg {String} fieldLabel Label to use when rendering a form.
42450      */
42451         /**
42452      * @cfg {String} labelSeparator the ':' after a field label (default :)  = set it to empty string to hide the field label.
42453      */
42454        /**
42455      * @cfg {String} qtip Mouse over tip
42456      */
42457      
42458     /**
42459      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
42460      */
42461     invalidClass : "x-form-invalid",
42462     /**
42463      * @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")
42464      */
42465     invalidText : "The value in this field is invalid",
42466     /**
42467      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
42468      */
42469     focusClass : "x-form-focus",
42470     /**
42471      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
42472       automatic validation (defaults to "keyup").
42473      */
42474     validationEvent : "keyup",
42475     /**
42476      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
42477      */
42478     validateOnBlur : true,
42479     /**
42480      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
42481      */
42482     validationDelay : 250,
42483     /**
42484      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42485      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
42486      */
42487     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
42488     /**
42489      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
42490      */
42491     fieldClass : "x-form-field",
42492     /**
42493      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
42494      *<pre>
42495 Value         Description
42496 -----------   ----------------------------------------------------------------------
42497 qtip          Display a quick tip when the user hovers over the field
42498 title         Display a default browser title attribute popup
42499 under         Add a block div beneath the field containing the error text
42500 side          Add an error icon to the right of the field with a popup on hover
42501 [element id]  Add the error text directly to the innerHTML of the specified element
42502 </pre>
42503      */
42504     msgTarget : 'qtip',
42505     /**
42506      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
42507      */
42508     msgFx : 'normal',
42509
42510     /**
42511      * @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.
42512      */
42513     readOnly : false,
42514
42515     /**
42516      * @cfg {Boolean} disabled True to disable the field (defaults to false).
42517      */
42518     disabled : false,
42519
42520     /**
42521      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
42522      */
42523     inputType : undefined,
42524     
42525     /**
42526      * @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).
42527          */
42528         tabIndex : undefined,
42529         
42530     // private
42531     isFormField : true,
42532
42533     // private
42534     hasFocus : false,
42535     /**
42536      * @property {Roo.Element} fieldEl
42537      * Element Containing the rendered Field (with label etc.)
42538      */
42539     /**
42540      * @cfg {Mixed} value A value to initialize this field with.
42541      */
42542     value : undefined,
42543
42544     /**
42545      * @cfg {String} name The field's HTML name attribute.
42546      */
42547     /**
42548      * @cfg {String} cls A CSS class to apply to the field's underlying element.
42549      */
42550     // private
42551     loadedValue : false,
42552      
42553      
42554         // private ??
42555         initComponent : function(){
42556         Roo.form.Field.superclass.initComponent.call(this);
42557         this.addEvents({
42558             /**
42559              * @event focus
42560              * Fires when this field receives input focus.
42561              * @param {Roo.form.Field} this
42562              */
42563             focus : true,
42564             /**
42565              * @event blur
42566              * Fires when this field loses input focus.
42567              * @param {Roo.form.Field} this
42568              */
42569             blur : true,
42570             /**
42571              * @event specialkey
42572              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
42573              * {@link Roo.EventObject#getKey} to determine which key was pressed.
42574              * @param {Roo.form.Field} this
42575              * @param {Roo.EventObject} e The event object
42576              */
42577             specialkey : true,
42578             /**
42579              * @event change
42580              * Fires just before the field blurs if the field value has changed.
42581              * @param {Roo.form.Field} this
42582              * @param {Mixed} newValue The new value
42583              * @param {Mixed} oldValue The original value
42584              */
42585             change : true,
42586             /**
42587              * @event invalid
42588              * Fires after the field has been marked as invalid.
42589              * @param {Roo.form.Field} this
42590              * @param {String} msg The validation message
42591              */
42592             invalid : true,
42593             /**
42594              * @event valid
42595              * Fires after the field has been validated with no errors.
42596              * @param {Roo.form.Field} this
42597              */
42598             valid : true,
42599              /**
42600              * @event keyup
42601              * Fires after the key up
42602              * @param {Roo.form.Field} this
42603              * @param {Roo.EventObject}  e The event Object
42604              */
42605             keyup : true
42606         });
42607     },
42608
42609     /**
42610      * Returns the name attribute of the field if available
42611      * @return {String} name The field name
42612      */
42613     getName: function(){
42614          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
42615     },
42616
42617     // private
42618     onRender : function(ct, position){
42619         Roo.form.Field.superclass.onRender.call(this, ct, position);
42620         if(!this.el){
42621             var cfg = this.getAutoCreate();
42622             if(!cfg.name){
42623                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
42624             }
42625             if (!cfg.name.length) {
42626                 delete cfg.name;
42627             }
42628             if(this.inputType){
42629                 cfg.type = this.inputType;
42630             }
42631             this.el = ct.createChild(cfg, position);
42632         }
42633         var type = this.el.dom.type;
42634         if(type){
42635             if(type == 'password'){
42636                 type = 'text';
42637             }
42638             this.el.addClass('x-form-'+type);
42639         }
42640         if(this.readOnly){
42641             this.el.dom.readOnly = true;
42642         }
42643         if(this.tabIndex !== undefined){
42644             this.el.dom.setAttribute('tabIndex', this.tabIndex);
42645         }
42646
42647         this.el.addClass([this.fieldClass, this.cls]);
42648         this.initValue();
42649     },
42650
42651     /**
42652      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
42653      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
42654      * @return {Roo.form.Field} this
42655      */
42656     applyTo : function(target){
42657         this.allowDomMove = false;
42658         this.el = Roo.get(target);
42659         this.render(this.el.dom.parentNode);
42660         return this;
42661     },
42662
42663     // private
42664     initValue : function(){
42665         if(this.value !== undefined){
42666             this.setValue(this.value);
42667         }else if(this.el.dom.value.length > 0){
42668             this.setValue(this.el.dom.value);
42669         }
42670     },
42671
42672     /**
42673      * Returns true if this field has been changed since it was originally loaded and is not disabled.
42674      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
42675      */
42676     isDirty : function() {
42677         if(this.disabled) {
42678             return false;
42679         }
42680         return String(this.getValue()) !== String(this.originalValue);
42681     },
42682
42683     /**
42684      * stores the current value in loadedValue
42685      */
42686     resetHasChanged : function()
42687     {
42688         this.loadedValue = String(this.getValue());
42689     },
42690     /**
42691      * checks the current value against the 'loaded' value.
42692      * Note - will return false if 'resetHasChanged' has not been called first.
42693      */
42694     hasChanged : function()
42695     {
42696         if(this.disabled || this.readOnly) {
42697             return false;
42698         }
42699         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
42700     },
42701     
42702     
42703     
42704     // private
42705     afterRender : function(){
42706         Roo.form.Field.superclass.afterRender.call(this);
42707         this.initEvents();
42708     },
42709
42710     // private
42711     fireKey : function(e){
42712         //Roo.log('field ' + e.getKey());
42713         if(e.isNavKeyPress()){
42714             this.fireEvent("specialkey", this, e);
42715         }
42716     },
42717
42718     /**
42719      * Resets the current field value to the originally loaded value and clears any validation messages
42720      */
42721     reset : function(){
42722         this.setValue(this.resetValue);
42723         this.originalValue = this.getValue();
42724         this.clearInvalid();
42725     },
42726
42727     // private
42728     initEvents : function(){
42729         // safari killled keypress - so keydown is now used..
42730         this.el.on("keydown" , this.fireKey,  this);
42731         this.el.on("focus", this.onFocus,  this);
42732         this.el.on("blur", this.onBlur,  this);
42733         this.el.relayEvent('keyup', this);
42734
42735         // reference to original value for reset
42736         this.originalValue = this.getValue();
42737         this.resetValue =  this.getValue();
42738     },
42739
42740     // private
42741     onFocus : function(){
42742         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
42743             this.el.addClass(this.focusClass);
42744         }
42745         if(!this.hasFocus){
42746             this.hasFocus = true;
42747             this.startValue = this.getValue();
42748             this.fireEvent("focus", this);
42749         }
42750     },
42751
42752     beforeBlur : Roo.emptyFn,
42753
42754     // private
42755     onBlur : function(){
42756         this.beforeBlur();
42757         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
42758             this.el.removeClass(this.focusClass);
42759         }
42760         this.hasFocus = false;
42761         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
42762             this.validate();
42763         }
42764         var v = this.getValue();
42765         if(String(v) !== String(this.startValue)){
42766             this.fireEvent('change', this, v, this.startValue);
42767         }
42768         this.fireEvent("blur", this);
42769     },
42770
42771     /**
42772      * Returns whether or not the field value is currently valid
42773      * @param {Boolean} preventMark True to disable marking the field invalid
42774      * @return {Boolean} True if the value is valid, else false
42775      */
42776     isValid : function(preventMark){
42777         if(this.disabled){
42778             return true;
42779         }
42780         var restore = this.preventMark;
42781         this.preventMark = preventMark === true;
42782         var v = this.validateValue(this.processValue(this.getRawValue()));
42783         this.preventMark = restore;
42784         return v;
42785     },
42786
42787     /**
42788      * Validates the field value
42789      * @return {Boolean} True if the value is valid, else false
42790      */
42791     validate : function(){
42792         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
42793             this.clearInvalid();
42794             return true;
42795         }
42796         return false;
42797     },
42798
42799     processValue : function(value){
42800         return value;
42801     },
42802
42803     // private
42804     // Subclasses should provide the validation implementation by overriding this
42805     validateValue : function(value){
42806         return true;
42807     },
42808
42809     /**
42810      * Mark this field as invalid
42811      * @param {String} msg The validation message
42812      */
42813     markInvalid : function(msg){
42814         if(!this.rendered || this.preventMark){ // not rendered
42815             return;
42816         }
42817         
42818         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
42819         
42820         obj.el.addClass(this.invalidClass);
42821         msg = msg || this.invalidText;
42822         switch(this.msgTarget){
42823             case 'qtip':
42824                 obj.el.dom.qtip = msg;
42825                 obj.el.dom.qclass = 'x-form-invalid-tip';
42826                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
42827                     Roo.QuickTips.enable();
42828                 }
42829                 break;
42830             case 'title':
42831                 this.el.dom.title = msg;
42832                 break;
42833             case 'under':
42834                 if(!this.errorEl){
42835                     var elp = this.el.findParent('.x-form-element', 5, true);
42836                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
42837                     this.errorEl.setWidth(elp.getWidth(true)-20);
42838                 }
42839                 this.errorEl.update(msg);
42840                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
42841                 break;
42842             case 'side':
42843                 if(!this.errorIcon){
42844                     var elp = this.el.findParent('.x-form-element', 5, true);
42845                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
42846                 }
42847                 this.alignErrorIcon();
42848                 this.errorIcon.dom.qtip = msg;
42849                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
42850                 this.errorIcon.show();
42851                 this.on('resize', this.alignErrorIcon, this);
42852                 break;
42853             default:
42854                 var t = Roo.getDom(this.msgTarget);
42855                 t.innerHTML = msg;
42856                 t.style.display = this.msgDisplay;
42857                 break;
42858         }
42859         this.fireEvent('invalid', this, msg);
42860     },
42861
42862     // private
42863     alignErrorIcon : function(){
42864         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
42865     },
42866
42867     /**
42868      * Clear any invalid styles/messages for this field
42869      */
42870     clearInvalid : function(){
42871         if(!this.rendered || this.preventMark){ // not rendered
42872             return;
42873         }
42874         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
42875         
42876         obj.el.removeClass(this.invalidClass);
42877         switch(this.msgTarget){
42878             case 'qtip':
42879                 obj.el.dom.qtip = '';
42880                 break;
42881             case 'title':
42882                 this.el.dom.title = '';
42883                 break;
42884             case 'under':
42885                 if(this.errorEl){
42886                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
42887                 }
42888                 break;
42889             case 'side':
42890                 if(this.errorIcon){
42891                     this.errorIcon.dom.qtip = '';
42892                     this.errorIcon.hide();
42893                     this.un('resize', this.alignErrorIcon, this);
42894                 }
42895                 break;
42896             default:
42897                 var t = Roo.getDom(this.msgTarget);
42898                 t.innerHTML = '';
42899                 t.style.display = 'none';
42900                 break;
42901         }
42902         this.fireEvent('valid', this);
42903     },
42904
42905     /**
42906      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
42907      * @return {Mixed} value The field value
42908      */
42909     getRawValue : function(){
42910         var v = this.el.getValue();
42911         
42912         return v;
42913     },
42914
42915     /**
42916      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
42917      * @return {Mixed} value The field value
42918      */
42919     getValue : function(){
42920         var v = this.el.getValue();
42921          
42922         return v;
42923     },
42924
42925     /**
42926      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
42927      * @param {Mixed} value The value to set
42928      */
42929     setRawValue : function(v){
42930         return this.el.dom.value = (v === null || v === undefined ? '' : v);
42931     },
42932
42933     /**
42934      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
42935      * @param {Mixed} value The value to set
42936      */
42937     setValue : function(v){
42938         this.value = v;
42939         if(this.rendered){
42940             this.el.dom.value = (v === null || v === undefined ? '' : v);
42941              this.validate();
42942         }
42943     },
42944
42945     adjustSize : function(w, h){
42946         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
42947         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
42948         return s;
42949     },
42950
42951     adjustWidth : function(tag, w){
42952         tag = tag.toLowerCase();
42953         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
42954             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
42955                 if(tag == 'input'){
42956                     return w + 2;
42957                 }
42958                 if(tag == 'textarea'){
42959                     return w-2;
42960                 }
42961             }else if(Roo.isOpera){
42962                 if(tag == 'input'){
42963                     return w + 2;
42964                 }
42965                 if(tag == 'textarea'){
42966                     return w-2;
42967                 }
42968             }
42969         }
42970         return w;
42971     }
42972 });
42973
42974
42975 // anything other than normal should be considered experimental
42976 Roo.form.Field.msgFx = {
42977     normal : {
42978         show: function(msgEl, f){
42979             msgEl.setDisplayed('block');
42980         },
42981
42982         hide : function(msgEl, f){
42983             msgEl.setDisplayed(false).update('');
42984         }
42985     },
42986
42987     slide : {
42988         show: function(msgEl, f){
42989             msgEl.slideIn('t', {stopFx:true});
42990         },
42991
42992         hide : function(msgEl, f){
42993             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
42994         }
42995     },
42996
42997     slideRight : {
42998         show: function(msgEl, f){
42999             msgEl.fixDisplay();
43000             msgEl.alignTo(f.el, 'tl-tr');
43001             msgEl.slideIn('l', {stopFx:true});
43002         },
43003
43004         hide : function(msgEl, f){
43005             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
43006         }
43007     }
43008 };/*
43009  * Based on:
43010  * Ext JS Library 1.1.1
43011  * Copyright(c) 2006-2007, Ext JS, LLC.
43012  *
43013  * Originally Released Under LGPL - original licence link has changed is not relivant.
43014  *
43015  * Fork - LGPL
43016  * <script type="text/javascript">
43017  */
43018  
43019
43020 /**
43021  * @class Roo.form.TextField
43022  * @extends Roo.form.Field
43023  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
43024  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
43025  * @constructor
43026  * Creates a new TextField
43027  * @param {Object} config Configuration options
43028  */
43029 Roo.form.TextField = function(config){
43030     Roo.form.TextField.superclass.constructor.call(this, config);
43031     this.addEvents({
43032         /**
43033          * @event autosize
43034          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
43035          * according to the default logic, but this event provides a hook for the developer to apply additional
43036          * logic at runtime to resize the field if needed.
43037              * @param {Roo.form.Field} this This text field
43038              * @param {Number} width The new field width
43039              */
43040         autosize : true
43041     });
43042 };
43043
43044 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
43045     /**
43046      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
43047      */
43048     grow : false,
43049     /**
43050      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
43051      */
43052     growMin : 30,
43053     /**
43054      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
43055      */
43056     growMax : 800,
43057     /**
43058      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
43059      */
43060     vtype : null,
43061     /**
43062      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
43063      */
43064     maskRe : null,
43065     /**
43066      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
43067      */
43068     disableKeyFilter : false,
43069     /**
43070      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
43071      */
43072     allowBlank : true,
43073     /**
43074      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
43075      */
43076     minLength : 0,
43077     /**
43078      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
43079      */
43080     maxLength : Number.MAX_VALUE,
43081     /**
43082      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
43083      */
43084     minLengthText : "The minimum length for this field is {0}",
43085     /**
43086      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
43087      */
43088     maxLengthText : "The maximum length for this field is {0}",
43089     /**
43090      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
43091      */
43092     selectOnFocus : false,
43093     /**
43094      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
43095      */    
43096     allowLeadingSpace : false,
43097     /**
43098      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
43099      */
43100     blankText : "This field is required",
43101     /**
43102      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
43103      * If available, this function will be called only after the basic validators all return true, and will be passed the
43104      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
43105      */
43106     validator : null,
43107     /**
43108      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
43109      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
43110      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
43111      */
43112     regex : null,
43113     /**
43114      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
43115      */
43116     regexText : "",
43117     /**
43118      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
43119      */
43120     emptyText : null,
43121    
43122
43123     // private
43124     initEvents : function()
43125     {
43126         if (this.emptyText) {
43127             this.el.attr('placeholder', this.emptyText);
43128         }
43129         
43130         Roo.form.TextField.superclass.initEvents.call(this);
43131         if(this.validationEvent == 'keyup'){
43132             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43133             this.el.on('keyup', this.filterValidation, this);
43134         }
43135         else if(this.validationEvent !== false){
43136             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43137         }
43138         
43139         if(this.selectOnFocus){
43140             this.on("focus", this.preFocus, this);
43141         }
43142                 if (!this.allowLeadingSpace) {
43143                         this.on('blur', this.cleanLeadingSpace, this);
43144                 }
43145         
43146         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43147             this.el.on("keypress", this.filterKeys, this);
43148         }
43149         if(this.grow){
43150             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
43151             this.el.on("click", this.autoSize,  this);
43152         }
43153         if(this.el.is('input[type=password]') && Roo.isSafari){
43154             this.el.on('keydown', this.SafariOnKeyDown, this);
43155         }
43156     },
43157
43158     processValue : function(value){
43159         if(this.stripCharsRe){
43160             var newValue = value.replace(this.stripCharsRe, '');
43161             if(newValue !== value){
43162                 this.setRawValue(newValue);
43163                 return newValue;
43164             }
43165         }
43166         return value;
43167     },
43168
43169     filterValidation : function(e){
43170         if(!e.isNavKeyPress()){
43171             this.validationTask.delay(this.validationDelay);
43172         }
43173     },
43174
43175     // private
43176     onKeyUp : function(e){
43177         if(!e.isNavKeyPress()){
43178             this.autoSize();
43179         }
43180     },
43181     // private - clean the leading white space
43182     cleanLeadingSpace : function(e)
43183     {
43184         if ( this.inputType == 'file') {
43185             return;
43186         }
43187         
43188         this.setValue((this.getValue() + '').replace(/^\s+/,''));
43189     },
43190     /**
43191      * Resets the current field value to the originally-loaded value and clears any validation messages.
43192      *  
43193      */
43194     reset : function(){
43195         Roo.form.TextField.superclass.reset.call(this);
43196        
43197     }, 
43198     // private
43199     preFocus : function(){
43200         
43201         if(this.selectOnFocus){
43202             this.el.dom.select();
43203         }
43204     },
43205
43206     
43207     // private
43208     filterKeys : function(e){
43209         var k = e.getKey();
43210         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
43211             return;
43212         }
43213         var c = e.getCharCode(), cc = String.fromCharCode(c);
43214         if(Roo.isIE && (e.isSpecialKey() || !cc)){
43215             return;
43216         }
43217         if(!this.maskRe.test(cc)){
43218             e.stopEvent();
43219         }
43220     },
43221
43222     setValue : function(v){
43223         
43224         Roo.form.TextField.superclass.setValue.apply(this, arguments);
43225         
43226         this.autoSize();
43227     },
43228
43229     /**
43230      * Validates a value according to the field's validation rules and marks the field as invalid
43231      * if the validation fails
43232      * @param {Mixed} value The value to validate
43233      * @return {Boolean} True if the value is valid, else false
43234      */
43235     validateValue : function(value){
43236         if(value.length < 1)  { // if it's blank
43237              if(this.allowBlank){
43238                 this.clearInvalid();
43239                 return true;
43240              }else{
43241                 this.markInvalid(this.blankText);
43242                 return false;
43243              }
43244         }
43245         if(value.length < this.minLength){
43246             this.markInvalid(String.format(this.minLengthText, this.minLength));
43247             return false;
43248         }
43249         if(value.length > this.maxLength){
43250             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
43251             return false;
43252         }
43253         if(this.vtype){
43254             var vt = Roo.form.VTypes;
43255                         if (value.trim() != value) { // trim before checking email (and other stuf??)
43256                                 value = value.trim();
43257                                 this.el.dom.value  = value;
43258                         }
43259                         
43260             if(!vt[this.vtype](value, this)){
43261                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
43262                 return false;
43263             }
43264         }
43265         if(typeof this.validator == "function"){
43266             var msg = this.validator(value);
43267             if(msg !== true){
43268                 this.markInvalid(msg);
43269                 return false;
43270             }
43271         }
43272         if(this.regex && !this.regex.test(value)){
43273             this.markInvalid(this.regexText);
43274             return false;
43275         }
43276         return true;
43277     },
43278
43279     /**
43280      * Selects text in this field
43281      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
43282      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
43283      */
43284     selectText : function(start, end){
43285         var v = this.getRawValue();
43286         if(v.length > 0){
43287             start = start === undefined ? 0 : start;
43288             end = end === undefined ? v.length : end;
43289             var d = this.el.dom;
43290             if(d.setSelectionRange){
43291                 d.setSelectionRange(start, end);
43292             }else if(d.createTextRange){
43293                 var range = d.createTextRange();
43294                 range.moveStart("character", start);
43295                 range.moveEnd("character", v.length-end);
43296                 range.select();
43297             }
43298         }
43299     },
43300
43301     /**
43302      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
43303      * This only takes effect if grow = true, and fires the autosize event.
43304      */
43305     autoSize : function(){
43306         if(!this.grow || !this.rendered){
43307             return;
43308         }
43309         if(!this.metrics){
43310             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
43311         }
43312         var el = this.el;
43313         var v = el.dom.value;
43314         var d = document.createElement('div');
43315         d.appendChild(document.createTextNode(v));
43316         v = d.innerHTML;
43317         d = null;
43318         v += "&#160;";
43319         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
43320         this.el.setWidth(w);
43321         this.fireEvent("autosize", this, w);
43322     },
43323     
43324     // private
43325     SafariOnKeyDown : function(event)
43326     {
43327         // this is a workaround for a password hang bug on chrome/ webkit.
43328         
43329         var isSelectAll = false;
43330         
43331         if(this.el.dom.selectionEnd > 0){
43332             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
43333         }
43334         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
43335             event.preventDefault();
43336             this.setValue('');
43337             return;
43338         }
43339         
43340         // skip handling paste
43341         if(isSelectAll && event.getCharCode() > 31 && !(event.ctrlKey && event.getCharCode() == 86)){ // backspace and delete key
43342             
43343             event.preventDefault();
43344             // this is very hacky as keydown always get's upper case.
43345             
43346             var cc = String.fromCharCode(event.getCharCode());
43347             
43348             
43349             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
43350             
43351         }
43352         
43353         
43354     }
43355 });Roo.form.Password = function(config){
43356     Roo.form.Password.superclass.constructor.call(this, config);
43357
43358     this.inputType = 'password';
43359 };
43360
43361 Roo.extend(Roo.form.Password, Roo.form.TextField,  {
43362     onRender : function(ct, position)
43363     {
43364         Roo.form.Password.superclass.onRender.call(this, ct, position);
43365
43366         this.parentEl().addClass('form-password');
43367
43368         this.wrap = this.el.wrap({
43369             cls : 'password-wrap'
43370         });
43371
43372         this.toggle = this.wrap.createChild({
43373             tag : 'Button',
43374             cls : 'password-toggle'
43375         });
43376
43377
43378         this.toggleEl().addClass('password-hidden');
43379
43380         this.toggleEl().on('click', this.onToggleClick, this);;
43381     },
43382     
43383     parentEl : function()
43384     {
43385         return this.el.findParent('.x-form-element', 5, true);
43386     },
43387
43388     toggleEl: function()
43389     {
43390         return this.parentEl().select('button.password-toggle',true).first();
43391     },
43392
43393     onToggleClick : function(e) 
43394     {
43395         var input = this.el;
43396         var toggle = this.toggleEl();
43397
43398         toggle.removeClass(['password-visible', 'password-hidden']);
43399
43400         if(input.attr('type') == 'password') {
43401             input.attr('type', 'text');
43402             toggle.addClass('password-visible');
43403         }
43404         else {
43405             input.attr('type', 'password');
43406             toggle.addClass('password-hidden');
43407         }
43408     }
43409 });/*
43410  * Based on:
43411  * Ext JS Library 1.1.1
43412  * Copyright(c) 2006-2007, Ext JS, LLC.
43413  *
43414  * Originally Released Under LGPL - original licence link has changed is not relivant.
43415  *
43416  * Fork - LGPL
43417  * <script type="text/javascript">
43418  */
43419  
43420 /**
43421  * @class Roo.form.Hidden
43422  * @extends Roo.form.TextField
43423  * Simple Hidden element used on forms 
43424  * 
43425  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
43426  * 
43427  * @constructor
43428  * Creates a new Hidden form element.
43429  * @param {Object} config Configuration options
43430  */
43431
43432
43433
43434 // easy hidden field...
43435 Roo.form.Hidden = function(config){
43436     Roo.form.Hidden.superclass.constructor.call(this, config);
43437 };
43438   
43439 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
43440     fieldLabel:      '',
43441     inputType:      'hidden',
43442     width:          50,
43443     allowBlank:     true,
43444     labelSeparator: '',
43445     hidden:         true,
43446     itemCls :       'x-form-item-display-none'
43447
43448
43449 });
43450
43451
43452 /*
43453  * Based on:
43454  * Ext JS Library 1.1.1
43455  * Copyright(c) 2006-2007, Ext JS, LLC.
43456  *
43457  * Originally Released Under LGPL - original licence link has changed is not relivant.
43458  *
43459  * Fork - LGPL
43460  * <script type="text/javascript">
43461  */
43462  
43463 /**
43464  * @class Roo.form.TriggerField
43465  * @extends Roo.form.TextField
43466  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
43467  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
43468  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
43469  * for which you can provide a custom implementation.  For example:
43470  * <pre><code>
43471 var trigger = new Roo.form.TriggerField();
43472 trigger.onTriggerClick = myTriggerFn;
43473 trigger.applyTo('my-field');
43474 </code></pre>
43475  *
43476  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
43477  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
43478  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
43479  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
43480  * @constructor
43481  * Create a new TriggerField.
43482  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
43483  * to the base TextField)
43484  */
43485 Roo.form.TriggerField = function(config){
43486     this.mimicing = false;
43487     Roo.form.TriggerField.superclass.constructor.call(this, config);
43488 };
43489
43490 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
43491     /**
43492      * @cfg {String} triggerClass A CSS class to apply to the trigger
43493      */
43494     /**
43495      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43496      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
43497      */
43498     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
43499     /**
43500      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
43501      */
43502     hideTrigger:false,
43503
43504     /** @cfg {Boolean} grow @hide */
43505     /** @cfg {Number} growMin @hide */
43506     /** @cfg {Number} growMax @hide */
43507
43508     /**
43509      * @hide 
43510      * @method
43511      */
43512     autoSize: Roo.emptyFn,
43513     // private
43514     monitorTab : true,
43515     // private
43516     deferHeight : true,
43517
43518     
43519     actionMode : 'wrap',
43520     // private
43521     onResize : function(w, h){
43522         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
43523         if(typeof w == 'number'){
43524             var x = w - this.trigger.getWidth();
43525             this.el.setWidth(this.adjustWidth('input', x));
43526             this.trigger.setStyle('left', x+'px');
43527         }
43528     },
43529
43530     // private
43531     adjustSize : Roo.BoxComponent.prototype.adjustSize,
43532
43533     // private
43534     getResizeEl : function(){
43535         return this.wrap;
43536     },
43537
43538     // private
43539     getPositionEl : function(){
43540         return this.wrap;
43541     },
43542
43543     // private
43544     alignErrorIcon : function(){
43545         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
43546     },
43547
43548     // private
43549     onRender : function(ct, position){
43550         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
43551         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
43552         this.trigger = this.wrap.createChild(this.triggerConfig ||
43553                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
43554         if(this.hideTrigger){
43555             this.trigger.setDisplayed(false);
43556         }
43557         this.initTrigger();
43558         if(!this.width){
43559             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
43560         }
43561     },
43562
43563     // private
43564     initTrigger : function(){
43565         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43566         this.trigger.addClassOnOver('x-form-trigger-over');
43567         this.trigger.addClassOnClick('x-form-trigger-click');
43568     },
43569
43570     // private
43571     onDestroy : function(){
43572         if(this.trigger){
43573             this.trigger.removeAllListeners();
43574             this.trigger.remove();
43575         }
43576         if(this.wrap){
43577             this.wrap.remove();
43578         }
43579         Roo.form.TriggerField.superclass.onDestroy.call(this);
43580     },
43581
43582     // private
43583     onFocus : function(){
43584         Roo.form.TriggerField.superclass.onFocus.call(this);
43585         if(!this.mimicing){
43586             this.wrap.addClass('x-trigger-wrap-focus');
43587             this.mimicing = true;
43588             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
43589             if(this.monitorTab){
43590                 this.el.on("keydown", this.checkTab, this);
43591             }
43592         }
43593     },
43594
43595     // private
43596     checkTab : function(e){
43597         if(e.getKey() == e.TAB){
43598             this.triggerBlur();
43599         }
43600     },
43601
43602     // private
43603     onBlur : function(){
43604         // do nothing
43605     },
43606
43607     // private
43608     mimicBlur : function(e, t){
43609         if(!this.wrap.contains(t) && this.validateBlur()){
43610             this.triggerBlur();
43611         }
43612     },
43613
43614     // private
43615     triggerBlur : function(){
43616         this.mimicing = false;
43617         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
43618         if(this.monitorTab){
43619             this.el.un("keydown", this.checkTab, this);
43620         }
43621         this.wrap.removeClass('x-trigger-wrap-focus');
43622         Roo.form.TriggerField.superclass.onBlur.call(this);
43623     },
43624
43625     // private
43626     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
43627     validateBlur : function(e, t){
43628         return true;
43629     },
43630
43631     // private
43632     onDisable : function(){
43633         Roo.form.TriggerField.superclass.onDisable.call(this);
43634         if(this.wrap){
43635             this.wrap.addClass('x-item-disabled');
43636         }
43637     },
43638
43639     // private
43640     onEnable : function(){
43641         Roo.form.TriggerField.superclass.onEnable.call(this);
43642         if(this.wrap){
43643             this.wrap.removeClass('x-item-disabled');
43644         }
43645     },
43646
43647     // private
43648     onShow : function(){
43649         var ae = this.getActionEl();
43650         
43651         if(ae){
43652             ae.dom.style.display = '';
43653             ae.dom.style.visibility = 'visible';
43654         }
43655     },
43656
43657     // private
43658     
43659     onHide : function(){
43660         var ae = this.getActionEl();
43661         ae.dom.style.display = 'none';
43662     },
43663
43664     /**
43665      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
43666      * by an implementing function.
43667      * @method
43668      * @param {EventObject} e
43669      */
43670     onTriggerClick : Roo.emptyFn
43671 });
43672
43673 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
43674 // to be extended by an implementing class.  For an example of implementing this class, see the custom
43675 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
43676 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
43677     initComponent : function(){
43678         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
43679
43680         this.triggerConfig = {
43681             tag:'span', cls:'x-form-twin-triggers', cn:[
43682             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
43683             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
43684         ]};
43685     },
43686
43687     getTrigger : function(index){
43688         return this.triggers[index];
43689     },
43690
43691     initTrigger : function(){
43692         var ts = this.trigger.select('.x-form-trigger', true);
43693         this.wrap.setStyle('overflow', 'hidden');
43694         var triggerField = this;
43695         ts.each(function(t, all, index){
43696             t.hide = function(){
43697                 var w = triggerField.wrap.getWidth();
43698                 this.dom.style.display = 'none';
43699                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
43700             };
43701             t.show = function(){
43702                 var w = triggerField.wrap.getWidth();
43703                 this.dom.style.display = '';
43704                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
43705             };
43706             var triggerIndex = 'Trigger'+(index+1);
43707
43708             if(this['hide'+triggerIndex]){
43709                 t.dom.style.display = 'none';
43710             }
43711             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
43712             t.addClassOnOver('x-form-trigger-over');
43713             t.addClassOnClick('x-form-trigger-click');
43714         }, this);
43715         this.triggers = ts.elements;
43716     },
43717
43718     onTrigger1Click : Roo.emptyFn,
43719     onTrigger2Click : Roo.emptyFn
43720 });/*
43721  * Based on:
43722  * Ext JS Library 1.1.1
43723  * Copyright(c) 2006-2007, Ext JS, LLC.
43724  *
43725  * Originally Released Under LGPL - original licence link has changed is not relivant.
43726  *
43727  * Fork - LGPL
43728  * <script type="text/javascript">
43729  */
43730  
43731 /**
43732  * @class Roo.form.TextArea
43733  * @extends Roo.form.TextField
43734  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
43735  * support for auto-sizing.
43736  * @constructor
43737  * Creates a new TextArea
43738  * @param {Object} config Configuration options
43739  */
43740 Roo.form.TextArea = function(config){
43741     Roo.form.TextArea.superclass.constructor.call(this, config);
43742     // these are provided exchanges for backwards compat
43743     // minHeight/maxHeight were replaced by growMin/growMax to be
43744     // compatible with TextField growing config values
43745     if(this.minHeight !== undefined){
43746         this.growMin = this.minHeight;
43747     }
43748     if(this.maxHeight !== undefined){
43749         this.growMax = this.maxHeight;
43750     }
43751 };
43752
43753 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
43754     /**
43755      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
43756      */
43757     growMin : 60,
43758     /**
43759      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
43760      */
43761     growMax: 1000,
43762     /**
43763      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
43764      * in the field (equivalent to setting overflow: hidden, defaults to false)
43765      */
43766     preventScrollbars: false,
43767     /**
43768      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43769      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
43770      */
43771
43772     // private
43773     onRender : function(ct, position){
43774         if(!this.el){
43775             this.defaultAutoCreate = {
43776                 tag: "textarea",
43777                 style:"width:300px;height:60px;",
43778                 autocomplete: "new-password"
43779             };
43780         }
43781         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
43782         if(this.grow){
43783             this.textSizeEl = Roo.DomHelper.append(document.body, {
43784                 tag: "pre", cls: "x-form-grow-sizer"
43785             });
43786             if(this.preventScrollbars){
43787                 this.el.setStyle("overflow", "hidden");
43788             }
43789             this.el.setHeight(this.growMin);
43790         }
43791     },
43792
43793     onDestroy : function(){
43794         if(this.textSizeEl){
43795             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
43796         }
43797         Roo.form.TextArea.superclass.onDestroy.call(this);
43798     },
43799
43800     // private
43801     onKeyUp : function(e){
43802         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
43803             this.autoSize();
43804         }
43805     },
43806
43807     /**
43808      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
43809      * This only takes effect if grow = true, and fires the autosize event if the height changes.
43810      */
43811     autoSize : function(){
43812         if(!this.grow || !this.textSizeEl){
43813             return;
43814         }
43815         var el = this.el;
43816         var v = el.dom.value;
43817         var ts = this.textSizeEl;
43818
43819         ts.innerHTML = '';
43820         ts.appendChild(document.createTextNode(v));
43821         v = ts.innerHTML;
43822
43823         Roo.fly(ts).setWidth(this.el.getWidth());
43824         if(v.length < 1){
43825             v = "&#160;&#160;";
43826         }else{
43827             if(Roo.isIE){
43828                 v = v.replace(/\n/g, '<p>&#160;</p>');
43829             }
43830             v += "&#160;\n&#160;";
43831         }
43832         ts.innerHTML = v;
43833         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
43834         if(h != this.lastHeight){
43835             this.lastHeight = h;
43836             this.el.setHeight(h);
43837             this.fireEvent("autosize", this, h);
43838         }
43839     }
43840 });/*
43841  * Based on:
43842  * Ext JS Library 1.1.1
43843  * Copyright(c) 2006-2007, Ext JS, LLC.
43844  *
43845  * Originally Released Under LGPL - original licence link has changed is not relivant.
43846  *
43847  * Fork - LGPL
43848  * <script type="text/javascript">
43849  */
43850  
43851
43852 /**
43853  * @class Roo.form.NumberField
43854  * @extends Roo.form.TextField
43855  * Numeric text field that provides automatic keystroke filtering and numeric validation.
43856  * @constructor
43857  * Creates a new NumberField
43858  * @param {Object} config Configuration options
43859  */
43860 Roo.form.NumberField = function(config){
43861     Roo.form.NumberField.superclass.constructor.call(this, config);
43862 };
43863
43864 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
43865     /**
43866      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
43867      */
43868     fieldClass: "x-form-field x-form-num-field",
43869     /**
43870      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43871      */
43872     allowDecimals : true,
43873     /**
43874      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43875      */
43876     decimalSeparator : ".",
43877     /**
43878      * @cfg {String} thousandSeparator Character(s) to allow as the thousand separator (defaults to '') - set to ',' for example
43879      */
43880     thousandSeparator : "",
43881     /**
43882      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43883      */
43884     decimalPrecision : 2,
43885     /**
43886      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43887      */
43888     allowNegative : true,
43889     /**
43890      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43891      */
43892     minValue : Number.NEGATIVE_INFINITY,
43893     /**
43894      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43895      */
43896     maxValue : Number.MAX_VALUE,
43897     /**
43898      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43899      */
43900     minText : "The minimum value for this field is {0}",
43901     /**
43902      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43903      */
43904     maxText : "The maximum value for this field is {0}",
43905     /**
43906      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43907      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43908      */
43909     nanText : "{0} is not a valid number",
43910     
43911     hiddenField : false,
43912      
43913     onRender : function(ct, position)
43914     {
43915         Roo.form.TextField.superclass.onRender.call(this, ct, position);
43916     
43917             //this.el.dom.removeAttribute('name'); 
43918         Roo.log("Changing name?");
43919         if (this.thousandSeparator != '') {
43920             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
43921             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
43922                         'before', true);
43923             this.hiddenField.value = this.value ? this.parseValue(this.value) : '';
43924             this.el.on('blur', this.onBlur, this);
43925         }
43926         
43927             // prevent input submission
43928         
43929             
43930             
43931     },
43932      onBlur : function(){
43933         this.beforeBlur();
43934         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43935             this.el.removeClass(this.focusClass);
43936         }
43937         this.hasFocus = false;
43938         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43939             this.validate();
43940         }
43941         var v = this.getValue();
43942         if(String(v) !== String(this.startValue)){
43943             this.setValue( this.parseValue(v));
43944             this.fireEvent('change', this, v, this.startValue);
43945         }
43946         this.fireEvent("blur", this);
43947     },
43948     
43949     // override name, so that it works with hidden field.
43950     getName: function(){
43951         if (this.thousandSeparator != '') {
43952             return this.name;
43953         }
43954         return Roo.form.TextField.superclass.getName.call(this);
43955     },
43956     // private
43957     initEvents : function(){
43958           
43959         var allowed = "0123456789";
43960         if(this.allowDecimals){
43961             allowed += this.decimalSeparator;
43962         }
43963         allowed += this.thousandSeparator;
43964         if(this.allowNegative){
43965             allowed += "-";
43966         }
43967         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43968         var keyPress = function(e){
43969             var k = e.getKey();
43970             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43971                 return;
43972             }
43973             var c = e.getCharCode();
43974             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43975                 e.stopEvent();
43976             }
43977         };
43978         this.el.on("keypress", keyPress, this);
43979     },
43980
43981     // private
43982     validateValue : function(value){
43983         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
43984             return false;
43985         }
43986         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
43987              return true;
43988         }
43989         var num = this.parseValue(value);
43990         if(isNaN(num)){
43991             this.markInvalid(String.format(this.nanText, value));
43992             return false;
43993         }
43994         if(num < this.minValue){
43995             this.markInvalid(String.format(this.minText, this.minValue));
43996             return false;
43997         }
43998         if(num > this.maxValue){
43999             this.markInvalid(String.format(this.maxText, this.maxValue));
44000             return false;
44001         }
44002         return true;
44003     },
44004
44005     getValue : function(){
44006         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
44007     },
44008
44009     // private
44010     parseValue : function(value){
44011         value = parseFloat(String(value).replace(this.decimalSeparator, ".").split(this.thousandSeparator).join(''));
44012         return isNaN(value) ? '' : value;
44013     },
44014
44015     // private
44016     fixPrecision : function(value){
44017         var nan = isNaN(value);
44018         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44019             return nan ? '' : value;
44020         }
44021         return parseFloat(value).toFixed(this.decimalPrecision);
44022     },
44023
44024     setValue : function(v){
44025         v = this.fixPrecision(v);
44026         if(this.thousandSeparator != ''){
44027             v = Roo.util.Format.number(v, this.decimalPrecision, this.thousandSeparator);
44028         } 
44029         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
44030         if (this.hiddenField !== false) {
44031             this.hiddenField.value = v ? this.parseValue(v) : '';
44032         }
44033         
44034
44035     },
44036
44037     // private
44038     decimalPrecisionFcn : function(v){
44039         return Math.floor(v);
44040     },
44041
44042     beforeBlur : function(){
44043         var v = this.parseValue(this.getRawValue());
44044         if(v){
44045             this.setValue(v);
44046         }
44047     }
44048 });/*
44049  * Based on:
44050  * Ext JS Library 1.1.1
44051  * Copyright(c) 2006-2007, Ext JS, LLC.
44052  *
44053  * Originally Released Under LGPL - original licence link has changed is not relivant.
44054  *
44055  * Fork - LGPL
44056  * <script type="text/javascript">
44057  */
44058  
44059 /**
44060  * @class Roo.form.DateField
44061  * @extends Roo.form.TriggerField
44062  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
44063 * @constructor
44064 * Create a new DateField
44065 * @param {Object} config
44066  */
44067 Roo.form.DateField = function(config)
44068 {
44069     Roo.form.DateField.superclass.constructor.call(this, config);
44070     
44071       this.addEvents({
44072          
44073         /**
44074          * @event select
44075          * Fires when a date is selected
44076              * @param {Roo.form.DateField} combo This combo box
44077              * @param {Date} date The date selected
44078              */
44079         'select' : true
44080          
44081     });
44082     
44083     
44084     if(typeof this.minValue == "string") {
44085         this.minValue = this.parseDate(this.minValue);
44086     }
44087     if(typeof this.maxValue == "string") {
44088         this.maxValue = this.parseDate(this.maxValue);
44089     }
44090     this.ddMatch = null;
44091     if(this.disabledDates){
44092         var dd = this.disabledDates;
44093         var re = "(?:";
44094         for(var i = 0; i < dd.length; i++){
44095             re += dd[i];
44096             if(i != dd.length-1) {
44097                 re += "|";
44098             }
44099         }
44100         this.ddMatch = new RegExp(re + ")");
44101     }
44102 };
44103
44104 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
44105     /**
44106      * @cfg {String} format
44107      * The default date format string which can be overriden for localization support.  The format must be
44108      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
44109      */
44110     format : "m/d/y",
44111     /**
44112      * @cfg {String} altFormats
44113      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
44114      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
44115      */
44116     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
44117     /**
44118      * @cfg {Array} disabledDays
44119      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
44120      */
44121     disabledDays : null,
44122     /**
44123      * @cfg {String} disabledDaysText
44124      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
44125      */
44126     disabledDaysText : "Disabled",
44127     /**
44128      * @cfg {Array} disabledDates
44129      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
44130      * expression so they are very powerful. Some examples:
44131      * <ul>
44132      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
44133      * <li>["03/08", "09/16"] would disable those days for every year</li>
44134      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
44135      * <li>["03/../2006"] would disable every day in March 2006</li>
44136      * <li>["^03"] would disable every day in every March</li>
44137      * </ul>
44138      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
44139      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
44140      */
44141     disabledDates : null,
44142     /**
44143      * @cfg {String} disabledDatesText
44144      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
44145      */
44146     disabledDatesText : "Disabled",
44147         
44148         
44149         /**
44150      * @cfg {Date/String} zeroValue
44151      * if the date is less that this number, then the field is rendered as empty
44152      * default is 1800
44153      */
44154         zeroValue : '1800-01-01',
44155         
44156         
44157     /**
44158      * @cfg {Date/String} minValue
44159      * The minimum allowed date. Can be either a Javascript date object or a string date in a
44160      * valid format (defaults to null).
44161      */
44162     minValue : null,
44163     /**
44164      * @cfg {Date/String} maxValue
44165      * The maximum allowed date. Can be either a Javascript date object or a string date in a
44166      * valid format (defaults to null).
44167      */
44168     maxValue : null,
44169     /**
44170      * @cfg {String} minText
44171      * The error text to display when the date in the cell is before minValue (defaults to
44172      * 'The date in this field must be after {minValue}').
44173      */
44174     minText : "The date in this field must be equal to or after {0}",
44175     /**
44176      * @cfg {String} maxText
44177      * The error text to display when the date in the cell is after maxValue (defaults to
44178      * 'The date in this field must be before {maxValue}').
44179      */
44180     maxText : "The date in this field must be equal to or before {0}",
44181     /**
44182      * @cfg {String} invalidText
44183      * The error text to display when the date in the field is invalid (defaults to
44184      * '{value} is not a valid date - it must be in the format {format}').
44185      */
44186     invalidText : "{0} is not a valid date - it must be in the format {1}",
44187     /**
44188      * @cfg {String} triggerClass
44189      * An additional CSS class used to style the trigger button.  The trigger will always get the
44190      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
44191      * which displays a calendar icon).
44192      */
44193     triggerClass : 'x-form-date-trigger',
44194     
44195
44196     /**
44197      * @cfg {Boolean} useIso
44198      * if enabled, then the date field will use a hidden field to store the 
44199      * real value as iso formated date. default (false)
44200      */ 
44201     useIso : false,
44202     /**
44203      * @cfg {String/Object} autoCreate
44204      * A DomHelper element spec, or true for a default element spec (defaults to
44205      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
44206      */ 
44207     // private
44208     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
44209     
44210     // private
44211     hiddenField: false,
44212     
44213     onRender : function(ct, position)
44214     {
44215         Roo.form.DateField.superclass.onRender.call(this, ct, position);
44216         if (this.useIso) {
44217             //this.el.dom.removeAttribute('name'); 
44218             Roo.log("Changing name?");
44219             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
44220             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
44221                     'before', true);
44222             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
44223             // prevent input submission
44224             this.hiddenName = this.name;
44225         }
44226             
44227             
44228     },
44229     
44230     // private
44231     validateValue : function(value)
44232     {
44233         value = this.formatDate(value);
44234         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
44235             Roo.log('super failed');
44236             return false;
44237         }
44238         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
44239              return true;
44240         }
44241         var svalue = value;
44242         value = this.parseDate(value);
44243         if(!value){
44244             Roo.log('parse date failed' + svalue);
44245             this.markInvalid(String.format(this.invalidText, svalue, this.format));
44246             return false;
44247         }
44248         var time = value.getTime();
44249         if(this.minValue && time < this.minValue.getTime()){
44250             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
44251             return false;
44252         }
44253         if(this.maxValue && time > this.maxValue.getTime()){
44254             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
44255             return false;
44256         }
44257         if(this.disabledDays){
44258             var day = value.getDay();
44259             for(var i = 0; i < this.disabledDays.length; i++) {
44260                 if(day === this.disabledDays[i]){
44261                     this.markInvalid(this.disabledDaysText);
44262                     return false;
44263                 }
44264             }
44265         }
44266         var fvalue = this.formatDate(value);
44267         if(this.ddMatch && this.ddMatch.test(fvalue)){
44268             this.markInvalid(String.format(this.disabledDatesText, fvalue));
44269             return false;
44270         }
44271         return true;
44272     },
44273
44274     // private
44275     // Provides logic to override the default TriggerField.validateBlur which just returns true
44276     validateBlur : function(){
44277         return !this.menu || !this.menu.isVisible();
44278     },
44279     
44280     getName: function()
44281     {
44282         // returns hidden if it's set..
44283         if (!this.rendered) {return ''};
44284         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
44285         
44286     },
44287
44288     /**
44289      * Returns the current date value of the date field.
44290      * @return {Date} The date value
44291      */
44292     getValue : function(){
44293         
44294         return  this.hiddenField ?
44295                 this.hiddenField.value :
44296                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
44297     },
44298
44299     /**
44300      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
44301      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
44302      * (the default format used is "m/d/y").
44303      * <br />Usage:
44304      * <pre><code>
44305 //All of these calls set the same date value (May 4, 2006)
44306
44307 //Pass a date object:
44308 var dt = new Date('5/4/06');
44309 dateField.setValue(dt);
44310
44311 //Pass a date string (default format):
44312 dateField.setValue('5/4/06');
44313
44314 //Pass a date string (custom format):
44315 dateField.format = 'Y-m-d';
44316 dateField.setValue('2006-5-4');
44317 </code></pre>
44318      * @param {String/Date} date The date or valid date string
44319      */
44320     setValue : function(date){
44321         if (this.hiddenField) {
44322             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
44323         }
44324         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
44325         // make sure the value field is always stored as a date..
44326         this.value = this.parseDate(date);
44327         
44328         
44329     },
44330
44331     // private
44332     parseDate : function(value){
44333                 
44334                 if (value instanceof Date) {
44335                         if (value < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
44336                                 return  '';
44337                         }
44338                         return value;
44339                 }
44340                 
44341                 
44342         if(!value || value instanceof Date){
44343             return value;
44344         }
44345         var v = Date.parseDate(value, this.format);
44346          if (!v && this.useIso) {
44347             v = Date.parseDate(value, 'Y-m-d');
44348         }
44349         if(!v && this.altFormats){
44350             if(!this.altFormatsArray){
44351                 this.altFormatsArray = this.altFormats.split("|");
44352             }
44353             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
44354                 v = Date.parseDate(value, this.altFormatsArray[i]);
44355             }
44356         }
44357                 if (v < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
44358                         v = '';
44359                 }
44360         return v;
44361     },
44362
44363     // private
44364     formatDate : function(date, fmt){
44365         return (!date || !(date instanceof Date)) ?
44366                date : date.dateFormat(fmt || this.format);
44367     },
44368
44369     // private
44370     menuListeners : {
44371         select: function(m, d){
44372             
44373             this.setValue(d);
44374             this.fireEvent('select', this, d);
44375         },
44376         show : function(){ // retain focus styling
44377             this.onFocus();
44378         },
44379         hide : function(){
44380             this.focus.defer(10, this);
44381             var ml = this.menuListeners;
44382             this.menu.un("select", ml.select,  this);
44383             this.menu.un("show", ml.show,  this);
44384             this.menu.un("hide", ml.hide,  this);
44385         }
44386     },
44387
44388     // private
44389     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
44390     onTriggerClick : function(){
44391         if(this.disabled || this.readOnly){
44392             return;
44393         }
44394         if(this.menu == null){
44395             this.menu = new Roo.menu.DateMenu();
44396         }
44397         Roo.apply(this.menu.picker,  {
44398             showClear: this.allowBlank,
44399             minDate : this.minValue,
44400             maxDate : this.maxValue,
44401             disabledDatesRE : this.ddMatch,
44402             disabledDatesText : this.disabledDatesText,
44403             disabledDays : this.disabledDays,
44404             disabledDaysText : this.disabledDaysText,
44405             format : this.useIso ? 'Y-m-d' : this.format,
44406             minText : String.format(this.minText, this.formatDate(this.minValue)),
44407             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
44408         });
44409         this.menu.on(Roo.apply({}, this.menuListeners, {
44410             scope:this
44411         }));
44412         this.menu.picker.setValue(this.getValue() || new Date());
44413         this.menu.show(this.el, "tl-bl?");
44414     },
44415
44416     beforeBlur : function(){
44417         var v = this.parseDate(this.getRawValue());
44418         if(v){
44419             this.setValue(v);
44420         }
44421     },
44422
44423     /*@
44424      * overide
44425      * 
44426      */
44427     isDirty : function() {
44428         if(this.disabled) {
44429             return false;
44430         }
44431         
44432         if(typeof(this.startValue) === 'undefined'){
44433             return false;
44434         }
44435         
44436         return String(this.getValue()) !== String(this.startValue);
44437         
44438     },
44439     // @overide
44440     cleanLeadingSpace : function(e)
44441     {
44442        return;
44443     }
44444     
44445 });/*
44446  * Based on:
44447  * Ext JS Library 1.1.1
44448  * Copyright(c) 2006-2007, Ext JS, LLC.
44449  *
44450  * Originally Released Under LGPL - original licence link has changed is not relivant.
44451  *
44452  * Fork - LGPL
44453  * <script type="text/javascript">
44454  */
44455  
44456 /**
44457  * @class Roo.form.MonthField
44458  * @extends Roo.form.TriggerField
44459  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
44460 * @constructor
44461 * Create a new MonthField
44462 * @param {Object} config
44463  */
44464 Roo.form.MonthField = function(config){
44465     
44466     Roo.form.MonthField.superclass.constructor.call(this, config);
44467     
44468       this.addEvents({
44469          
44470         /**
44471          * @event select
44472          * Fires when a date is selected
44473              * @param {Roo.form.MonthFieeld} combo This combo box
44474              * @param {Date} date The date selected
44475              */
44476         'select' : true
44477          
44478     });
44479     
44480     
44481     if(typeof this.minValue == "string") {
44482         this.minValue = this.parseDate(this.minValue);
44483     }
44484     if(typeof this.maxValue == "string") {
44485         this.maxValue = this.parseDate(this.maxValue);
44486     }
44487     this.ddMatch = null;
44488     if(this.disabledDates){
44489         var dd = this.disabledDates;
44490         var re = "(?:";
44491         for(var i = 0; i < dd.length; i++){
44492             re += dd[i];
44493             if(i != dd.length-1) {
44494                 re += "|";
44495             }
44496         }
44497         this.ddMatch = new RegExp(re + ")");
44498     }
44499 };
44500
44501 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
44502     /**
44503      * @cfg {String} format
44504      * The default date format string which can be overriden for localization support.  The format must be
44505      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
44506      */
44507     format : "M Y",
44508     /**
44509      * @cfg {String} altFormats
44510      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
44511      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
44512      */
44513     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
44514     /**
44515      * @cfg {Array} disabledDays
44516      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
44517      */
44518     disabledDays : [0,1,2,3,4,5,6],
44519     /**
44520      * @cfg {String} disabledDaysText
44521      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
44522      */
44523     disabledDaysText : "Disabled",
44524     /**
44525      * @cfg {Array} disabledDates
44526      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
44527      * expression so they are very powerful. Some examples:
44528      * <ul>
44529      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
44530      * <li>["03/08", "09/16"] would disable those days for every year</li>
44531      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
44532      * <li>["03/../2006"] would disable every day in March 2006</li>
44533      * <li>["^03"] would disable every day in every March</li>
44534      * </ul>
44535      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
44536      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
44537      */
44538     disabledDates : null,
44539     /**
44540      * @cfg {String} disabledDatesText
44541      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
44542      */
44543     disabledDatesText : "Disabled",
44544     /**
44545      * @cfg {Date/String} minValue
44546      * The minimum allowed date. Can be either a Javascript date object or a string date in a
44547      * valid format (defaults to null).
44548      */
44549     minValue : null,
44550     /**
44551      * @cfg {Date/String} maxValue
44552      * The maximum allowed date. Can be either a Javascript date object or a string date in a
44553      * valid format (defaults to null).
44554      */
44555     maxValue : null,
44556     /**
44557      * @cfg {String} minText
44558      * The error text to display when the date in the cell is before minValue (defaults to
44559      * 'The date in this field must be after {minValue}').
44560      */
44561     minText : "The date in this field must be equal to or after {0}",
44562     /**
44563      * @cfg {String} maxTextf
44564      * The error text to display when the date in the cell is after maxValue (defaults to
44565      * 'The date in this field must be before {maxValue}').
44566      */
44567     maxText : "The date in this field must be equal to or before {0}",
44568     /**
44569      * @cfg {String} invalidText
44570      * The error text to display when the date in the field is invalid (defaults to
44571      * '{value} is not a valid date - it must be in the format {format}').
44572      */
44573     invalidText : "{0} is not a valid date - it must be in the format {1}",
44574     /**
44575      * @cfg {String} triggerClass
44576      * An additional CSS class used to style the trigger button.  The trigger will always get the
44577      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
44578      * which displays a calendar icon).
44579      */
44580     triggerClass : 'x-form-date-trigger',
44581     
44582
44583     /**
44584      * @cfg {Boolean} useIso
44585      * if enabled, then the date field will use a hidden field to store the 
44586      * real value as iso formated date. default (true)
44587      */ 
44588     useIso : true,
44589     /**
44590      * @cfg {String/Object} autoCreate
44591      * A DomHelper element spec, or true for a default element spec (defaults to
44592      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
44593      */ 
44594     // private
44595     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
44596     
44597     // private
44598     hiddenField: false,
44599     
44600     hideMonthPicker : false,
44601     
44602     onRender : function(ct, position)
44603     {
44604         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
44605         if (this.useIso) {
44606             this.el.dom.removeAttribute('name'); 
44607             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
44608                     'before', true);
44609             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
44610             // prevent input submission
44611             this.hiddenName = this.name;
44612         }
44613             
44614             
44615     },
44616     
44617     // private
44618     validateValue : function(value)
44619     {
44620         value = this.formatDate(value);
44621         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
44622             return false;
44623         }
44624         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
44625              return true;
44626         }
44627         var svalue = value;
44628         value = this.parseDate(value);
44629         if(!value){
44630             this.markInvalid(String.format(this.invalidText, svalue, this.format));
44631             return false;
44632         }
44633         var time = value.getTime();
44634         if(this.minValue && time < this.minValue.getTime()){
44635             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
44636             return false;
44637         }
44638         if(this.maxValue && time > this.maxValue.getTime()){
44639             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
44640             return false;
44641         }
44642         /*if(this.disabledDays){
44643             var day = value.getDay();
44644             for(var i = 0; i < this.disabledDays.length; i++) {
44645                 if(day === this.disabledDays[i]){
44646                     this.markInvalid(this.disabledDaysText);
44647                     return false;
44648                 }
44649             }
44650         }
44651         */
44652         var fvalue = this.formatDate(value);
44653         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
44654             this.markInvalid(String.format(this.disabledDatesText, fvalue));
44655             return false;
44656         }
44657         */
44658         return true;
44659     },
44660
44661     // private
44662     // Provides logic to override the default TriggerField.validateBlur which just returns true
44663     validateBlur : function(){
44664         return !this.menu || !this.menu.isVisible();
44665     },
44666
44667     /**
44668      * Returns the current date value of the date field.
44669      * @return {Date} The date value
44670      */
44671     getValue : function(){
44672         
44673         
44674         
44675         return  this.hiddenField ?
44676                 this.hiddenField.value :
44677                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
44678     },
44679
44680     /**
44681      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
44682      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
44683      * (the default format used is "m/d/y").
44684      * <br />Usage:
44685      * <pre><code>
44686 //All of these calls set the same date value (May 4, 2006)
44687
44688 //Pass a date object:
44689 var dt = new Date('5/4/06');
44690 monthField.setValue(dt);
44691
44692 //Pass a date string (default format):
44693 monthField.setValue('5/4/06');
44694
44695 //Pass a date string (custom format):
44696 monthField.format = 'Y-m-d';
44697 monthField.setValue('2006-5-4');
44698 </code></pre>
44699      * @param {String/Date} date The date or valid date string
44700      */
44701     setValue : function(date){
44702         Roo.log('month setValue' + date);
44703         // can only be first of month..
44704         
44705         var val = this.parseDate(date);
44706         
44707         if (this.hiddenField) {
44708             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
44709         }
44710         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
44711         this.value = this.parseDate(date);
44712     },
44713
44714     // private
44715     parseDate : function(value){
44716         if(!value || value instanceof Date){
44717             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
44718             return value;
44719         }
44720         var v = Date.parseDate(value, this.format);
44721         if (!v && this.useIso) {
44722             v = Date.parseDate(value, 'Y-m-d');
44723         }
44724         if (v) {
44725             // 
44726             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
44727         }
44728         
44729         
44730         if(!v && this.altFormats){
44731             if(!this.altFormatsArray){
44732                 this.altFormatsArray = this.altFormats.split("|");
44733             }
44734             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
44735                 v = Date.parseDate(value, this.altFormatsArray[i]);
44736             }
44737         }
44738         return v;
44739     },
44740
44741     // private
44742     formatDate : function(date, fmt){
44743         return (!date || !(date instanceof Date)) ?
44744                date : date.dateFormat(fmt || this.format);
44745     },
44746
44747     // private
44748     menuListeners : {
44749         select: function(m, d){
44750             this.setValue(d);
44751             this.fireEvent('select', this, d);
44752         },
44753         show : function(){ // retain focus styling
44754             this.onFocus();
44755         },
44756         hide : function(){
44757             this.focus.defer(10, this);
44758             var ml = this.menuListeners;
44759             this.menu.un("select", ml.select,  this);
44760             this.menu.un("show", ml.show,  this);
44761             this.menu.un("hide", ml.hide,  this);
44762         }
44763     },
44764     // private
44765     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
44766     onTriggerClick : function(){
44767         if(this.disabled){
44768             return;
44769         }
44770         if(this.menu == null){
44771             this.menu = new Roo.menu.DateMenu();
44772            
44773         }
44774         
44775         Roo.apply(this.menu.picker,  {
44776             
44777             showClear: this.allowBlank,
44778             minDate : this.minValue,
44779             maxDate : this.maxValue,
44780             disabledDatesRE : this.ddMatch,
44781             disabledDatesText : this.disabledDatesText,
44782             
44783             format : this.useIso ? 'Y-m-d' : this.format,
44784             minText : String.format(this.minText, this.formatDate(this.minValue)),
44785             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
44786             
44787         });
44788          this.menu.on(Roo.apply({}, this.menuListeners, {
44789             scope:this
44790         }));
44791        
44792         
44793         var m = this.menu;
44794         var p = m.picker;
44795         
44796         // hide month picker get's called when we called by 'before hide';
44797         
44798         var ignorehide = true;
44799         p.hideMonthPicker  = function(disableAnim){
44800             if (ignorehide) {
44801                 return;
44802             }
44803              if(this.monthPicker){
44804                 Roo.log("hideMonthPicker called");
44805                 if(disableAnim === true){
44806                     this.monthPicker.hide();
44807                 }else{
44808                     this.monthPicker.slideOut('t', {duration:.2});
44809                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
44810                     p.fireEvent("select", this, this.value);
44811                     m.hide();
44812                 }
44813             }
44814         }
44815         
44816         Roo.log('picker set value');
44817         Roo.log(this.getValue());
44818         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
44819         m.show(this.el, 'tl-bl?');
44820         ignorehide  = false;
44821         // this will trigger hideMonthPicker..
44822         
44823         
44824         // hidden the day picker
44825         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
44826         
44827         
44828         
44829       
44830         
44831         p.showMonthPicker.defer(100, p);
44832     
44833         
44834        
44835     },
44836
44837     beforeBlur : function(){
44838         var v = this.parseDate(this.getRawValue());
44839         if(v){
44840             this.setValue(v);
44841         }
44842     }
44843
44844     /** @cfg {Boolean} grow @hide */
44845     /** @cfg {Number} growMin @hide */
44846     /** @cfg {Number} growMax @hide */
44847     /**
44848      * @hide
44849      * @method autoSize
44850      */
44851 });/*
44852  * Based on:
44853  * Ext JS Library 1.1.1
44854  * Copyright(c) 2006-2007, Ext JS, LLC.
44855  *
44856  * Originally Released Under LGPL - original licence link has changed is not relivant.
44857  *
44858  * Fork - LGPL
44859  * <script type="text/javascript">
44860  */
44861  
44862
44863 /**
44864  * @class Roo.form.ComboBox
44865  * @extends Roo.form.TriggerField
44866  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
44867  * @constructor
44868  * Create a new ComboBox.
44869  * @param {Object} config Configuration options
44870  */
44871 Roo.form.ComboBox = function(config){
44872     Roo.form.ComboBox.superclass.constructor.call(this, config);
44873     this.addEvents({
44874         /**
44875          * @event expand
44876          * Fires when the dropdown list is expanded
44877              * @param {Roo.form.ComboBox} combo This combo box
44878              */
44879         'expand' : true,
44880         /**
44881          * @event collapse
44882          * Fires when the dropdown list is collapsed
44883              * @param {Roo.form.ComboBox} combo This combo box
44884              */
44885         'collapse' : true,
44886         /**
44887          * @event beforeselect
44888          * Fires before a list item is selected. Return false to cancel the selection.
44889              * @param {Roo.form.ComboBox} combo This combo box
44890              * @param {Roo.data.Record} record The data record returned from the underlying store
44891              * @param {Number} index The index of the selected item in the dropdown list
44892              */
44893         'beforeselect' : true,
44894         /**
44895          * @event select
44896          * Fires when a list item is selected
44897              * @param {Roo.form.ComboBox} combo This combo box
44898              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
44899              * @param {Number} index The index of the selected item in the dropdown list
44900              */
44901         'select' : true,
44902         /**
44903          * @event beforequery
44904          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
44905          * The event object passed has these properties:
44906              * @param {Roo.form.ComboBox} combo This combo box
44907              * @param {String} query The query
44908              * @param {Boolean} forceAll true to force "all" query
44909              * @param {Boolean} cancel true to cancel the query
44910              * @param {Object} e The query event object
44911              */
44912         'beforequery': true,
44913          /**
44914          * @event add
44915          * Fires when the 'add' icon is pressed (add a listener to enable add button)
44916              * @param {Roo.form.ComboBox} combo This combo box
44917              */
44918         'add' : true,
44919         /**
44920          * @event edit
44921          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
44922              * @param {Roo.form.ComboBox} combo This combo box
44923              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
44924              */
44925         'edit' : true
44926         
44927         
44928     });
44929     if(this.transform){
44930         this.allowDomMove = false;
44931         var s = Roo.getDom(this.transform);
44932         if(!this.hiddenName){
44933             this.hiddenName = s.name;
44934         }
44935         if(!this.store){
44936             this.mode = 'local';
44937             var d = [], opts = s.options;
44938             for(var i = 0, len = opts.length;i < len; i++){
44939                 var o = opts[i];
44940                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
44941                 if(o.selected) {
44942                     this.value = value;
44943                 }
44944                 d.push([value, o.text]);
44945             }
44946             this.store = new Roo.data.SimpleStore({
44947                 'id': 0,
44948                 fields: ['value', 'text'],
44949                 data : d
44950             });
44951             this.valueField = 'value';
44952             this.displayField = 'text';
44953         }
44954         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
44955         if(!this.lazyRender){
44956             this.target = true;
44957             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
44958             s.parentNode.removeChild(s); // remove it
44959             this.render(this.el.parentNode);
44960         }else{
44961             s.parentNode.removeChild(s); // remove it
44962         }
44963
44964     }
44965     if (this.store) {
44966         this.store = Roo.factory(this.store, Roo.data);
44967     }
44968     
44969     this.selectedIndex = -1;
44970     if(this.mode == 'local'){
44971         if(config.queryDelay === undefined){
44972             this.queryDelay = 10;
44973         }
44974         if(config.minChars === undefined){
44975             this.minChars = 0;
44976         }
44977     }
44978 };
44979
44980 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
44981     /**
44982      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
44983      */
44984     /**
44985      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
44986      * rendering into an Roo.Editor, defaults to false)
44987      */
44988     /**
44989      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
44990      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
44991      */
44992     /**
44993      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
44994      */
44995     /**
44996      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
44997      * the dropdown list (defaults to undefined, with no header element)
44998      */
44999
45000      /**
45001      * @cfg {String/Roo.Template} tpl The template to use to render the output
45002      */
45003      
45004     // private
45005     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
45006     /**
45007      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
45008      */
45009     listWidth: undefined,
45010     /**
45011      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
45012      * mode = 'remote' or 'text' if mode = 'local')
45013      */
45014     displayField: undefined,
45015     /**
45016      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
45017      * mode = 'remote' or 'value' if mode = 'local'). 
45018      * Note: use of a valueField requires the user make a selection
45019      * in order for a value to be mapped.
45020      */
45021     valueField: undefined,
45022     
45023     
45024     /**
45025      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
45026      * field's data value (defaults to the underlying DOM element's name)
45027      */
45028     hiddenName: undefined,
45029     /**
45030      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
45031      */
45032     listClass: '',
45033     /**
45034      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
45035      */
45036     selectedClass: 'x-combo-selected',
45037     /**
45038      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
45039      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
45040      * which displays a downward arrow icon).
45041      */
45042     triggerClass : 'x-form-arrow-trigger',
45043     /**
45044      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
45045      */
45046     shadow:'sides',
45047     /**
45048      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
45049      * anchor positions (defaults to 'tl-bl')
45050      */
45051     listAlign: 'tl-bl?',
45052     /**
45053      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
45054      */
45055     maxHeight: 300,
45056     /**
45057      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
45058      * query specified by the allQuery config option (defaults to 'query')
45059      */
45060     triggerAction: 'query',
45061     /**
45062      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
45063      * (defaults to 4, does not apply if editable = false)
45064      */
45065     minChars : 4,
45066     /**
45067      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
45068      * delay (typeAheadDelay) if it matches a known value (defaults to false)
45069      */
45070     typeAhead: false,
45071     /**
45072      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
45073      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
45074      */
45075     queryDelay: 500,
45076     /**
45077      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
45078      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
45079      */
45080     pageSize: 0,
45081     /**
45082      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
45083      * when editable = true (defaults to false)
45084      */
45085     selectOnFocus:false,
45086     /**
45087      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
45088      */
45089     queryParam: 'query',
45090     /**
45091      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
45092      * when mode = 'remote' (defaults to 'Loading...')
45093      */
45094     loadingText: 'Loading...',
45095     /**
45096      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
45097      */
45098     resizable: false,
45099     /**
45100      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
45101      */
45102     handleHeight : 8,
45103     /**
45104      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
45105      * traditional select (defaults to true)
45106      */
45107     editable: true,
45108     /**
45109      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
45110      */
45111     allQuery: '',
45112     /**
45113      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
45114      */
45115     mode: 'remote',
45116     /**
45117      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
45118      * listWidth has a higher value)
45119      */
45120     minListWidth : 70,
45121     /**
45122      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
45123      * allow the user to set arbitrary text into the field (defaults to false)
45124      */
45125     forceSelection:false,
45126     /**
45127      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
45128      * if typeAhead = true (defaults to 250)
45129      */
45130     typeAheadDelay : 250,
45131     /**
45132      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
45133      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
45134      */
45135     valueNotFoundText : undefined,
45136     /**
45137      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
45138      */
45139     blockFocus : false,
45140     
45141     /**
45142      * @cfg {Boolean} disableClear Disable showing of clear button.
45143      */
45144     disableClear : false,
45145     /**
45146      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
45147      */
45148     alwaysQuery : false,
45149     
45150     //private
45151     addicon : false,
45152     editicon: false,
45153     
45154     // element that contains real text value.. (when hidden is used..)
45155      
45156     // private
45157     onRender : function(ct, position)
45158     {
45159         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
45160         
45161                 if(this.hiddenName){
45162             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
45163                     'before', true);
45164             this.hiddenField.value =
45165                 this.hiddenValue !== undefined ? this.hiddenValue :
45166                 this.value !== undefined ? this.value : '';
45167
45168             // prevent input submission
45169             this.el.dom.removeAttribute('name');
45170              
45171              
45172         }
45173         
45174         if(Roo.isGecko){
45175             this.el.dom.setAttribute('autocomplete', 'off');
45176         }
45177
45178         var cls = 'x-combo-list';
45179
45180         this.list = new Roo.Layer({
45181             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
45182         });
45183
45184         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
45185         this.list.setWidth(lw);
45186         this.list.swallowEvent('mousewheel');
45187         this.assetHeight = 0;
45188
45189         if(this.title){
45190             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
45191             this.assetHeight += this.header.getHeight();
45192         }
45193
45194         this.innerList = this.list.createChild({cls:cls+'-inner'});
45195         this.innerList.on('mouseover', this.onViewOver, this);
45196         this.innerList.on('mousemove', this.onViewMove, this);
45197         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
45198         
45199         if(this.allowBlank && !this.pageSize && !this.disableClear){
45200             this.footer = this.list.createChild({cls:cls+'-ft'});
45201             this.pageTb = new Roo.Toolbar(this.footer);
45202            
45203         }
45204         if(this.pageSize){
45205             this.footer = this.list.createChild({cls:cls+'-ft'});
45206             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
45207                     {pageSize: this.pageSize});
45208             
45209         }
45210         
45211         if (this.pageTb && this.allowBlank && !this.disableClear) {
45212             var _this = this;
45213             this.pageTb.add(new Roo.Toolbar.Fill(), {
45214                 cls: 'x-btn-icon x-btn-clear',
45215                 text: '&#160;',
45216                 handler: function()
45217                 {
45218                     _this.collapse();
45219                     _this.clearValue();
45220                     _this.onSelect(false, -1);
45221                 }
45222             });
45223         }
45224         if (this.footer) {
45225             this.assetHeight += this.footer.getHeight();
45226         }
45227         
45228
45229         if(!this.tpl){
45230             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
45231         }
45232
45233         this.view = new Roo.View(this.innerList, this.tpl, {
45234             singleSelect:true,
45235             store: this.store,
45236             selectedClass: this.selectedClass
45237         });
45238
45239         this.view.on('click', this.onViewClick, this);
45240
45241         this.store.on('beforeload', this.onBeforeLoad, this);
45242         this.store.on('load', this.onLoad, this);
45243         this.store.on('loadexception', this.onLoadException, this);
45244
45245         if(this.resizable){
45246             this.resizer = new Roo.Resizable(this.list,  {
45247                pinned:true, handles:'se'
45248             });
45249             this.resizer.on('resize', function(r, w, h){
45250                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
45251                 this.listWidth = w;
45252                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
45253                 this.restrictHeight();
45254             }, this);
45255             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
45256         }
45257         if(!this.editable){
45258             this.editable = true;
45259             this.setEditable(false);
45260         }  
45261         
45262         
45263         if (typeof(this.events.add.listeners) != 'undefined') {
45264             
45265             this.addicon = this.wrap.createChild(
45266                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
45267        
45268             this.addicon.on('click', function(e) {
45269                 this.fireEvent('add', this);
45270             }, this);
45271         }
45272         if (typeof(this.events.edit.listeners) != 'undefined') {
45273             
45274             this.editicon = this.wrap.createChild(
45275                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
45276             if (this.addicon) {
45277                 this.editicon.setStyle('margin-left', '40px');
45278             }
45279             this.editicon.on('click', function(e) {
45280                 
45281                 // we fire even  if inothing is selected..
45282                 this.fireEvent('edit', this, this.lastData );
45283                 
45284             }, this);
45285         }
45286         
45287         
45288         
45289     },
45290
45291     // private
45292     initEvents : function(){
45293         Roo.form.ComboBox.superclass.initEvents.call(this);
45294
45295         this.keyNav = new Roo.KeyNav(this.el, {
45296             "up" : function(e){
45297                 this.inKeyMode = true;
45298                 this.selectPrev();
45299             },
45300
45301             "down" : function(e){
45302                 if(!this.isExpanded()){
45303                     this.onTriggerClick();
45304                 }else{
45305                     this.inKeyMode = true;
45306                     this.selectNext();
45307                 }
45308             },
45309
45310             "enter" : function(e){
45311                 this.onViewClick();
45312                 //return true;
45313             },
45314
45315             "esc" : function(e){
45316                 this.collapse();
45317             },
45318
45319             "tab" : function(e){
45320                 this.onViewClick(false);
45321                 this.fireEvent("specialkey", this, e);
45322                 return true;
45323             },
45324
45325             scope : this,
45326
45327             doRelay : function(foo, bar, hname){
45328                 if(hname == 'down' || this.scope.isExpanded()){
45329                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
45330                 }
45331                 return true;
45332             },
45333
45334             forceKeyDown: true
45335         });
45336         this.queryDelay = Math.max(this.queryDelay || 10,
45337                 this.mode == 'local' ? 10 : 250);
45338         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
45339         if(this.typeAhead){
45340             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
45341         }
45342         if(this.editable !== false){
45343             this.el.on("keyup", this.onKeyUp, this);
45344         }
45345         if(this.forceSelection){
45346             this.on('blur', this.doForce, this);
45347         }
45348     },
45349
45350     onDestroy : function(){
45351         if(this.view){
45352             this.view.setStore(null);
45353             this.view.el.removeAllListeners();
45354             this.view.el.remove();
45355             this.view.purgeListeners();
45356         }
45357         if(this.list){
45358             this.list.destroy();
45359         }
45360         if(this.store){
45361             this.store.un('beforeload', this.onBeforeLoad, this);
45362             this.store.un('load', this.onLoad, this);
45363             this.store.un('loadexception', this.onLoadException, this);
45364         }
45365         Roo.form.ComboBox.superclass.onDestroy.call(this);
45366     },
45367
45368     // private
45369     fireKey : function(e){
45370         if(e.isNavKeyPress() && !this.list.isVisible()){
45371             this.fireEvent("specialkey", this, e);
45372         }
45373     },
45374
45375     // private
45376     onResize: function(w, h){
45377         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
45378         
45379         if(typeof w != 'number'){
45380             // we do not handle it!?!?
45381             return;
45382         }
45383         var tw = this.trigger.getWidth();
45384         tw += this.addicon ? this.addicon.getWidth() : 0;
45385         tw += this.editicon ? this.editicon.getWidth() : 0;
45386         var x = w - tw;
45387         this.el.setWidth( this.adjustWidth('input', x));
45388             
45389         this.trigger.setStyle('left', x+'px');
45390         
45391         if(this.list && this.listWidth === undefined){
45392             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
45393             this.list.setWidth(lw);
45394             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
45395         }
45396         
45397     
45398         
45399     },
45400
45401     /**
45402      * Allow or prevent the user from directly editing the field text.  If false is passed,
45403      * the user will only be able to select from the items defined in the dropdown list.  This method
45404      * is the runtime equivalent of setting the 'editable' config option at config time.
45405      * @param {Boolean} value True to allow the user to directly edit the field text
45406      */
45407     setEditable : function(value){
45408         if(value == this.editable){
45409             return;
45410         }
45411         this.editable = value;
45412         if(!value){
45413             this.el.dom.setAttribute('readOnly', true);
45414             this.el.on('mousedown', this.onTriggerClick,  this);
45415             this.el.addClass('x-combo-noedit');
45416         }else{
45417             this.el.dom.setAttribute('readOnly', false);
45418             this.el.un('mousedown', this.onTriggerClick,  this);
45419             this.el.removeClass('x-combo-noedit');
45420         }
45421     },
45422
45423     // private
45424     onBeforeLoad : function(){
45425         if(!this.hasFocus){
45426             return;
45427         }
45428         this.innerList.update(this.loadingText ?
45429                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
45430         this.restrictHeight();
45431         this.selectedIndex = -1;
45432     },
45433
45434     // private
45435     onLoad : function(){
45436         if(!this.hasFocus){
45437             return;
45438         }
45439         if(this.store.getCount() > 0){
45440             this.expand();
45441             this.restrictHeight();
45442             if(this.lastQuery == this.allQuery){
45443                 if(this.editable){
45444                     this.el.dom.select();
45445                 }
45446                 if(!this.selectByValue(this.value, true)){
45447                     this.select(0, true);
45448                 }
45449             }else{
45450                 this.selectNext();
45451                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
45452                     this.taTask.delay(this.typeAheadDelay);
45453                 }
45454             }
45455         }else{
45456             this.onEmptyResults();
45457         }
45458         //this.el.focus();
45459     },
45460     // private
45461     onLoadException : function()
45462     {
45463         this.collapse();
45464         Roo.log(this.store.reader.jsonData);
45465         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
45466             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
45467         }
45468         
45469         
45470     },
45471     // private
45472     onTypeAhead : function(){
45473         if(this.store.getCount() > 0){
45474             var r = this.store.getAt(0);
45475             var newValue = r.data[this.displayField];
45476             var len = newValue.length;
45477             var selStart = this.getRawValue().length;
45478             if(selStart != len){
45479                 this.setRawValue(newValue);
45480                 this.selectText(selStart, newValue.length);
45481             }
45482         }
45483     },
45484
45485     // private
45486     onSelect : function(record, index){
45487         if(this.fireEvent('beforeselect', this, record, index) !== false){
45488             this.setFromData(index > -1 ? record.data : false);
45489             this.collapse();
45490             this.fireEvent('select', this, record, index);
45491         }
45492     },
45493
45494     /**
45495      * Returns the currently selected field value or empty string if no value is set.
45496      * @return {String} value The selected value
45497      */
45498     getValue : function(){
45499         if(this.valueField){
45500             return typeof this.value != 'undefined' ? this.value : '';
45501         }
45502         return Roo.form.ComboBox.superclass.getValue.call(this);
45503     },
45504
45505     /**
45506      * Clears any text/value currently set in the field
45507      */
45508     clearValue : function(){
45509         if(this.hiddenField){
45510             this.hiddenField.value = '';
45511         }
45512         this.value = '';
45513         this.setRawValue('');
45514         this.lastSelectionText = '';
45515         
45516     },
45517
45518     /**
45519      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
45520      * will be displayed in the field.  If the value does not match the data value of an existing item,
45521      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
45522      * Otherwise the field will be blank (although the value will still be set).
45523      * @param {String} value The value to match
45524      */
45525     setValue : function(v){
45526         var text = v;
45527         if(this.valueField){
45528             var r = this.findRecord(this.valueField, v);
45529             if(r){
45530                 text = r.data[this.displayField];
45531             }else if(this.valueNotFoundText !== undefined){
45532                 text = this.valueNotFoundText;
45533             }
45534         }
45535         this.lastSelectionText = text;
45536         if(this.hiddenField){
45537             this.hiddenField.value = v;
45538         }
45539         Roo.form.ComboBox.superclass.setValue.call(this, text);
45540         this.value = v;
45541     },
45542     /**
45543      * @property {Object} the last set data for the element
45544      */
45545     
45546     lastData : false,
45547     /**
45548      * Sets the value of the field based on a object which is related to the record format for the store.
45549      * @param {Object} value the value to set as. or false on reset?
45550      */
45551     setFromData : function(o){
45552         var dv = ''; // display value
45553         var vv = ''; // value value..
45554         this.lastData = o;
45555         if (this.displayField) {
45556             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
45557         } else {
45558             // this is an error condition!!!
45559             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
45560         }
45561         
45562         if(this.valueField){
45563             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
45564         }
45565         if(this.hiddenField){
45566             this.hiddenField.value = vv;
45567             
45568             this.lastSelectionText = dv;
45569             Roo.form.ComboBox.superclass.setValue.call(this, dv);
45570             this.value = vv;
45571             return;
45572         }
45573         // no hidden field.. - we store the value in 'value', but still display
45574         // display field!!!!
45575         this.lastSelectionText = dv;
45576         Roo.form.ComboBox.superclass.setValue.call(this, dv);
45577         this.value = vv;
45578         
45579         
45580     },
45581     // private
45582     reset : function(){
45583         // overridden so that last data is reset..
45584         this.setValue(this.resetValue);
45585         this.originalValue = this.getValue();
45586         this.clearInvalid();
45587         this.lastData = false;
45588         if (this.view) {
45589             this.view.clearSelections();
45590         }
45591     },
45592     // private
45593     findRecord : function(prop, value){
45594         var record;
45595         if(this.store.getCount() > 0){
45596             this.store.each(function(r){
45597                 if(r.data[prop] == value){
45598                     record = r;
45599                     return false;
45600                 }
45601                 return true;
45602             });
45603         }
45604         return record;
45605     },
45606     
45607     getName: function()
45608     {
45609         // returns hidden if it's set..
45610         if (!this.rendered) {return ''};
45611         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
45612         
45613     },
45614     // private
45615     onViewMove : function(e, t){
45616         this.inKeyMode = false;
45617     },
45618
45619     // private
45620     onViewOver : function(e, t){
45621         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
45622             return;
45623         }
45624         var item = this.view.findItemFromChild(t);
45625         if(item){
45626             var index = this.view.indexOf(item);
45627             this.select(index, false);
45628         }
45629     },
45630
45631     // private
45632     onViewClick : function(doFocus)
45633     {
45634         var index = this.view.getSelectedIndexes()[0];
45635         var r = this.store.getAt(index);
45636         if(r){
45637             this.onSelect(r, index);
45638         }
45639         if(doFocus !== false && !this.blockFocus){
45640             this.el.focus();
45641         }
45642     },
45643
45644     // private
45645     restrictHeight : function(){
45646         this.innerList.dom.style.height = '';
45647         var inner = this.innerList.dom;
45648         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
45649         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
45650         this.list.beginUpdate();
45651         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
45652         this.list.alignTo(this.el, this.listAlign);
45653         this.list.endUpdate();
45654     },
45655
45656     // private
45657     onEmptyResults : function(){
45658         this.collapse();
45659     },
45660
45661     /**
45662      * Returns true if the dropdown list is expanded, else false.
45663      */
45664     isExpanded : function(){
45665         return this.list.isVisible();
45666     },
45667
45668     /**
45669      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
45670      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
45671      * @param {String} value The data value of the item to select
45672      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
45673      * selected item if it is not currently in view (defaults to true)
45674      * @return {Boolean} True if the value matched an item in the list, else false
45675      */
45676     selectByValue : function(v, scrollIntoView){
45677         if(v !== undefined && v !== null){
45678             var r = this.findRecord(this.valueField || this.displayField, v);
45679             if(r){
45680                 this.select(this.store.indexOf(r), scrollIntoView);
45681                 return true;
45682             }
45683         }
45684         return false;
45685     },
45686
45687     /**
45688      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
45689      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
45690      * @param {Number} index The zero-based index of the list item to select
45691      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
45692      * selected item if it is not currently in view (defaults to true)
45693      */
45694     select : function(index, scrollIntoView){
45695         this.selectedIndex = index;
45696         this.view.select(index);
45697         if(scrollIntoView !== false){
45698             var el = this.view.getNode(index);
45699             if(el){
45700                 this.innerList.scrollChildIntoView(el, false);
45701             }
45702         }
45703     },
45704
45705     // private
45706     selectNext : function(){
45707         var ct = this.store.getCount();
45708         if(ct > 0){
45709             if(this.selectedIndex == -1){
45710                 this.select(0);
45711             }else if(this.selectedIndex < ct-1){
45712                 this.select(this.selectedIndex+1);
45713             }
45714         }
45715     },
45716
45717     // private
45718     selectPrev : function(){
45719         var ct = this.store.getCount();
45720         if(ct > 0){
45721             if(this.selectedIndex == -1){
45722                 this.select(0);
45723             }else if(this.selectedIndex != 0){
45724                 this.select(this.selectedIndex-1);
45725             }
45726         }
45727     },
45728
45729     // private
45730     onKeyUp : function(e){
45731         if(this.editable !== false && !e.isSpecialKey()){
45732             this.lastKey = e.getKey();
45733             this.dqTask.delay(this.queryDelay);
45734         }
45735     },
45736
45737     // private
45738     validateBlur : function(){
45739         return !this.list || !this.list.isVisible();   
45740     },
45741
45742     // private
45743     initQuery : function(){
45744         this.doQuery(this.getRawValue());
45745     },
45746
45747     // private
45748     doForce : function(){
45749         if(this.el.dom.value.length > 0){
45750             this.el.dom.value =
45751                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
45752              
45753         }
45754     },
45755
45756     /**
45757      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
45758      * query allowing the query action to be canceled if needed.
45759      * @param {String} query The SQL query to execute
45760      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
45761      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
45762      * saved in the current store (defaults to false)
45763      */
45764     doQuery : function(q, forceAll){
45765         if(q === undefined || q === null){
45766             q = '';
45767         }
45768         var qe = {
45769             query: q,
45770             forceAll: forceAll,
45771             combo: this,
45772             cancel:false
45773         };
45774         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
45775             return false;
45776         }
45777         q = qe.query;
45778         forceAll = qe.forceAll;
45779         if(forceAll === true || (q.length >= this.minChars)){
45780             if(this.lastQuery != q || this.alwaysQuery){
45781                 this.lastQuery = q;
45782                 if(this.mode == 'local'){
45783                     this.selectedIndex = -1;
45784                     if(forceAll){
45785                         this.store.clearFilter();
45786                     }else{
45787                         this.store.filter(this.displayField, q);
45788                     }
45789                     this.onLoad();
45790                 }else{
45791                     this.store.baseParams[this.queryParam] = q;
45792                     this.store.load({
45793                         params: this.getParams(q)
45794                     });
45795                     this.expand();
45796                 }
45797             }else{
45798                 this.selectedIndex = -1;
45799                 this.onLoad();   
45800             }
45801         }
45802     },
45803
45804     // private
45805     getParams : function(q){
45806         var p = {};
45807         //p[this.queryParam] = q;
45808         if(this.pageSize){
45809             p.start = 0;
45810             p.limit = this.pageSize;
45811         }
45812         return p;
45813     },
45814
45815     /**
45816      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
45817      */
45818     collapse : function(){
45819         if(!this.isExpanded()){
45820             return;
45821         }
45822         this.list.hide();
45823         Roo.get(document).un('mousedown', this.collapseIf, this);
45824         Roo.get(document).un('mousewheel', this.collapseIf, this);
45825         if (!this.editable) {
45826             Roo.get(document).un('keydown', this.listKeyPress, this);
45827         }
45828         this.fireEvent('collapse', this);
45829     },
45830
45831     // private
45832     collapseIf : function(e){
45833         if(!e.within(this.wrap) && !e.within(this.list)){
45834             this.collapse();
45835         }
45836     },
45837
45838     /**
45839      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
45840      */
45841     expand : function(){
45842         if(this.isExpanded() || !this.hasFocus){
45843             return;
45844         }
45845         this.list.alignTo(this.el, this.listAlign);
45846         this.list.show();
45847         Roo.get(document).on('mousedown', this.collapseIf, this);
45848         Roo.get(document).on('mousewheel', this.collapseIf, this);
45849         if (!this.editable) {
45850             Roo.get(document).on('keydown', this.listKeyPress, this);
45851         }
45852         
45853         this.fireEvent('expand', this);
45854     },
45855
45856     // private
45857     // Implements the default empty TriggerField.onTriggerClick function
45858     onTriggerClick : function(){
45859         if(this.disabled){
45860             return;
45861         }
45862         if(this.isExpanded()){
45863             this.collapse();
45864             if (!this.blockFocus) {
45865                 this.el.focus();
45866             }
45867             
45868         }else {
45869             this.hasFocus = true;
45870             if(this.triggerAction == 'all') {
45871                 this.doQuery(this.allQuery, true);
45872             } else {
45873                 this.doQuery(this.getRawValue());
45874             }
45875             if (!this.blockFocus) {
45876                 this.el.focus();
45877             }
45878         }
45879     },
45880     listKeyPress : function(e)
45881     {
45882         //Roo.log('listkeypress');
45883         // scroll to first matching element based on key pres..
45884         if (e.isSpecialKey()) {
45885             return false;
45886         }
45887         var k = String.fromCharCode(e.getKey()).toUpperCase();
45888         //Roo.log(k);
45889         var match  = false;
45890         var csel = this.view.getSelectedNodes();
45891         var cselitem = false;
45892         if (csel.length) {
45893             var ix = this.view.indexOf(csel[0]);
45894             cselitem  = this.store.getAt(ix);
45895             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
45896                 cselitem = false;
45897             }
45898             
45899         }
45900         
45901         this.store.each(function(v) { 
45902             if (cselitem) {
45903                 // start at existing selection.
45904                 if (cselitem.id == v.id) {
45905                     cselitem = false;
45906                 }
45907                 return;
45908             }
45909                 
45910             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
45911                 match = this.store.indexOf(v);
45912                 return false;
45913             }
45914         }, this);
45915         
45916         if (match === false) {
45917             return true; // no more action?
45918         }
45919         // scroll to?
45920         this.view.select(match);
45921         var sn = Roo.get(this.view.getSelectedNodes()[0]);
45922         sn.scrollIntoView(sn.dom.parentNode, false);
45923     },
45924         cleanLeadingSpace : function()
45925         {
45926                 // override textfield strip white space (trigers set on blur)
45927         }
45928
45929     /** 
45930     * @cfg {Boolean} grow 
45931     * @hide 
45932     */
45933     /** 
45934     * @cfg {Number} growMin 
45935     * @hide 
45936     */
45937     /** 
45938     * @cfg {Number} growMax 
45939     * @hide 
45940     */
45941     /**
45942      * @hide
45943      * @method autoSize
45944      */
45945 });/*
45946  * Copyright(c) 2010-2012, Roo J Solutions Limited
45947  *
45948  * Licence LGPL
45949  *
45950  */
45951
45952 /**
45953  * @class Roo.form.ComboBoxArray
45954  * @extends Roo.form.TextField
45955  * A facebook style adder... for lists of email / people / countries  etc...
45956  * pick multiple items from a combo box, and shows each one.
45957  *
45958  *  Fred [x]  Brian [x]  [Pick another |v]
45959  *
45960  *
45961  *  For this to work: it needs various extra information
45962  *    - normal combo problay has
45963  *      name, hiddenName
45964  *    + displayField, valueField
45965  *
45966  *    For our purpose...
45967  *
45968  *
45969  *   If we change from 'extends' to wrapping...
45970  *   
45971  *  
45972  *
45973  
45974  
45975  * @constructor
45976  * Create a new ComboBoxArray.
45977  * @param {Object} config Configuration options
45978  */
45979  
45980
45981 Roo.form.ComboBoxArray = function(config)
45982 {
45983     this.addEvents({
45984         /**
45985          * @event beforeremove
45986          * Fires before remove the value from the list
45987              * @param {Roo.form.ComboBoxArray} _self This combo box array
45988              * @param {Roo.form.ComboBoxArray.Item} item removed item
45989              */
45990         'beforeremove' : true,
45991         /**
45992          * @event remove
45993          * Fires when remove the value from the list
45994              * @param {Roo.form.ComboBoxArray} _self This combo box array
45995              * @param {Roo.form.ComboBoxArray.Item} item removed item
45996              */
45997         'remove' : true
45998         
45999         
46000     });
46001     
46002     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
46003     
46004     this.items = new Roo.util.MixedCollection(false);
46005     
46006     // construct the child combo...
46007     
46008     
46009     
46010     
46011    
46012     
46013 }
46014
46015  
46016 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
46017
46018     /**
46019      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
46020      */
46021     
46022     lastData : false,
46023     
46024     // behavies liek a hiddne field
46025     inputType:      'hidden',
46026     /**
46027      * @cfg {Number} width The width of the box that displays the selected element
46028      */ 
46029     width:          300,
46030
46031     
46032     
46033     /**
46034      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
46035      */
46036     name : false,
46037     /**
46038      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
46039      */
46040     hiddenName : false,
46041       /**
46042      * @cfg {String} seperator    The value seperator normally ',' 
46043      */
46044     seperator : ',',
46045     
46046     
46047         // private the array of items that are displayed..
46048     items  : false,
46049     // private - the hidden field el.
46050     hiddenEl : false,
46051     // private - the filed el..
46052     el : false,
46053     
46054     //validateValue : function() { return true; }, // all values are ok!
46055     //onAddClick: function() { },
46056     
46057     onRender : function(ct, position) 
46058     {
46059         
46060         // create the standard hidden element
46061         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
46062         
46063         
46064         // give fake names to child combo;
46065         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
46066         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
46067         
46068         this.combo = Roo.factory(this.combo, Roo.form);
46069         this.combo.onRender(ct, position);
46070         if (typeof(this.combo.width) != 'undefined') {
46071             this.combo.onResize(this.combo.width,0);
46072         }
46073         
46074         this.combo.initEvents();
46075         
46076         // assigned so form know we need to do this..
46077         this.store          = this.combo.store;
46078         this.valueField     = this.combo.valueField;
46079         this.displayField   = this.combo.displayField ;
46080         
46081         
46082         this.combo.wrap.addClass('x-cbarray-grp');
46083         
46084         var cbwrap = this.combo.wrap.createChild(
46085             {tag: 'div', cls: 'x-cbarray-cb'},
46086             this.combo.el.dom
46087         );
46088         
46089              
46090         this.hiddenEl = this.combo.wrap.createChild({
46091             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
46092         });
46093         this.el = this.combo.wrap.createChild({
46094             tag: 'input',  type:'hidden' , name: this.name, value : ''
46095         });
46096          //   this.el.dom.removeAttribute("name");
46097         
46098         
46099         this.outerWrap = this.combo.wrap;
46100         this.wrap = cbwrap;
46101         
46102         this.outerWrap.setWidth(this.width);
46103         this.outerWrap.dom.removeChild(this.el.dom);
46104         
46105         this.wrap.dom.appendChild(this.el.dom);
46106         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
46107         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
46108         
46109         this.combo.trigger.setStyle('position','relative');
46110         this.combo.trigger.setStyle('left', '0px');
46111         this.combo.trigger.setStyle('top', '2px');
46112         
46113         this.combo.el.setStyle('vertical-align', 'text-bottom');
46114         
46115         //this.trigger.setStyle('vertical-align', 'top');
46116         
46117         // this should use the code from combo really... on('add' ....)
46118         if (this.adder) {
46119             
46120         
46121             this.adder = this.outerWrap.createChild(
46122                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
46123             var _t = this;
46124             this.adder.on('click', function(e) {
46125                 _t.fireEvent('adderclick', this, e);
46126             }, _t);
46127         }
46128         //var _t = this;
46129         //this.adder.on('click', this.onAddClick, _t);
46130         
46131         
46132         this.combo.on('select', function(cb, rec, ix) {
46133             this.addItem(rec.data);
46134             
46135             cb.setValue('');
46136             cb.el.dom.value = '';
46137             //cb.lastData = rec.data;
46138             // add to list
46139             
46140         }, this);
46141          
46142         
46143         
46144             
46145     },
46146     
46147     
46148     getName: function()
46149     {
46150         // returns hidden if it's set..
46151         if (!this.rendered) {return ''};
46152         return  this.hiddenName ? this.hiddenName : this.name;
46153         
46154     },
46155     
46156     
46157     onResize: function(w, h){
46158         
46159         return;
46160         // not sure if this is needed..
46161         //this.combo.onResize(w,h);
46162         
46163         if(typeof w != 'number'){
46164             // we do not handle it!?!?
46165             return;
46166         }
46167         var tw = this.combo.trigger.getWidth();
46168         tw += this.addicon ? this.addicon.getWidth() : 0;
46169         tw += this.editicon ? this.editicon.getWidth() : 0;
46170         var x = w - tw;
46171         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
46172             
46173         this.combo.trigger.setStyle('left', '0px');
46174         
46175         if(this.list && this.listWidth === undefined){
46176             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
46177             this.list.setWidth(lw);
46178             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
46179         }
46180         
46181     
46182         
46183     },
46184     
46185     addItem: function(rec)
46186     {
46187         var valueField = this.combo.valueField;
46188         var displayField = this.combo.displayField;
46189         
46190         if (this.items.indexOfKey(rec[valueField]) > -1) {
46191             //console.log("GOT " + rec.data.id);
46192             return;
46193         }
46194         
46195         var x = new Roo.form.ComboBoxArray.Item({
46196             //id : rec[this.idField],
46197             data : rec,
46198             displayField : displayField ,
46199             tipField : displayField ,
46200             cb : this
46201         });
46202         // use the 
46203         this.items.add(rec[valueField],x);
46204         // add it before the element..
46205         this.updateHiddenEl();
46206         x.render(this.outerWrap, this.wrap.dom);
46207         // add the image handler..
46208     },
46209     
46210     updateHiddenEl : function()
46211     {
46212         this.validate();
46213         if (!this.hiddenEl) {
46214             return;
46215         }
46216         var ar = [];
46217         var idField = this.combo.valueField;
46218         
46219         this.items.each(function(f) {
46220             ar.push(f.data[idField]);
46221         });
46222         this.hiddenEl.dom.value = ar.join(this.seperator);
46223         this.validate();
46224     },
46225     
46226     reset : function()
46227     {
46228         this.items.clear();
46229         
46230         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
46231            el.remove();
46232         });
46233         
46234         this.el.dom.value = '';
46235         if (this.hiddenEl) {
46236             this.hiddenEl.dom.value = '';
46237         }
46238         
46239     },
46240     getValue: function()
46241     {
46242         return this.hiddenEl ? this.hiddenEl.dom.value : '';
46243     },
46244     setValue: function(v) // not a valid action - must use addItems..
46245     {
46246         
46247         this.reset();
46248          
46249         if (this.store.isLocal && (typeof(v) == 'string')) {
46250             // then we can use the store to find the values..
46251             // comma seperated at present.. this needs to allow JSON based encoding..
46252             this.hiddenEl.value  = v;
46253             var v_ar = [];
46254             Roo.each(v.split(this.seperator), function(k) {
46255                 Roo.log("CHECK " + this.valueField + ',' + k);
46256                 var li = this.store.query(this.valueField, k);
46257                 if (!li.length) {
46258                     return;
46259                 }
46260                 var add = {};
46261                 add[this.valueField] = k;
46262                 add[this.displayField] = li.item(0).data[this.displayField];
46263                 
46264                 this.addItem(add);
46265             }, this) 
46266              
46267         }
46268         if (typeof(v) == 'object' ) {
46269             // then let's assume it's an array of objects..
46270             Roo.each(v, function(l) {
46271                 var add = l;
46272                 if (typeof(l) == 'string') {
46273                     add = {};
46274                     add[this.valueField] = l;
46275                     add[this.displayField] = l
46276                 }
46277                 this.addItem(add);
46278             }, this);
46279              
46280         }
46281         
46282         
46283     },
46284     setFromData: function(v)
46285     {
46286         // this recieves an object, if setValues is called.
46287         this.reset();
46288         this.el.dom.value = v[this.displayField];
46289         this.hiddenEl.dom.value = v[this.valueField];
46290         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
46291             return;
46292         }
46293         var kv = v[this.valueField];
46294         var dv = v[this.displayField];
46295         kv = typeof(kv) != 'string' ? '' : kv;
46296         dv = typeof(dv) != 'string' ? '' : dv;
46297         
46298         
46299         var keys = kv.split(this.seperator);
46300         var display = dv.split(this.seperator);
46301         for (var i = 0 ; i < keys.length; i++) {
46302             add = {};
46303             add[this.valueField] = keys[i];
46304             add[this.displayField] = display[i];
46305             this.addItem(add);
46306         }
46307       
46308         
46309     },
46310     
46311     /**
46312      * Validates the combox array value
46313      * @return {Boolean} True if the value is valid, else false
46314      */
46315     validate : function(){
46316         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
46317             this.clearInvalid();
46318             return true;
46319         }
46320         return false;
46321     },
46322     
46323     validateValue : function(value){
46324         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
46325         
46326     },
46327     
46328     /*@
46329      * overide
46330      * 
46331      */
46332     isDirty : function() {
46333         if(this.disabled) {
46334             return false;
46335         }
46336         
46337         try {
46338             var d = Roo.decode(String(this.originalValue));
46339         } catch (e) {
46340             return String(this.getValue()) !== String(this.originalValue);
46341         }
46342         
46343         var originalValue = [];
46344         
46345         for (var i = 0; i < d.length; i++){
46346             originalValue.push(d[i][this.valueField]);
46347         }
46348         
46349         return String(this.getValue()) !== String(originalValue.join(this.seperator));
46350         
46351     }
46352     
46353 });
46354
46355
46356
46357 /**
46358  * @class Roo.form.ComboBoxArray.Item
46359  * @extends Roo.BoxComponent
46360  * A selected item in the list
46361  *  Fred [x]  Brian [x]  [Pick another |v]
46362  * 
46363  * @constructor
46364  * Create a new item.
46365  * @param {Object} config Configuration options
46366  */
46367  
46368 Roo.form.ComboBoxArray.Item = function(config) {
46369     config.id = Roo.id();
46370     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
46371 }
46372
46373 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
46374     data : {},
46375     cb: false,
46376     displayField : false,
46377     tipField : false,
46378      
46379     
46380     defaultAutoCreate : {
46381         tag: 'div',
46382         cls: 'x-cbarray-item',
46383         cn : [ 
46384             { tag: 'div' },
46385             {
46386                 tag: 'img',
46387                 width:16,
46388                 height : 16,
46389                 src : Roo.BLANK_IMAGE_URL ,
46390                 align: 'center'
46391             }
46392         ]
46393         
46394     },
46395     
46396  
46397     onRender : function(ct, position)
46398     {
46399         Roo.form.Field.superclass.onRender.call(this, ct, position);
46400         
46401         if(!this.el){
46402             var cfg = this.getAutoCreate();
46403             this.el = ct.createChild(cfg, position);
46404         }
46405         
46406         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
46407         
46408         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
46409             this.cb.renderer(this.data) :
46410             String.format('{0}',this.data[this.displayField]);
46411         
46412             
46413         this.el.child('div').dom.setAttribute('qtip',
46414                         String.format('{0}',this.data[this.tipField])
46415         );
46416         
46417         this.el.child('img').on('click', this.remove, this);
46418         
46419     },
46420    
46421     remove : function()
46422     {
46423         if(this.cb.disabled){
46424             return;
46425         }
46426         
46427         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
46428             this.cb.items.remove(this);
46429             this.el.child('img').un('click', this.remove, this);
46430             this.el.remove();
46431             this.cb.updateHiddenEl();
46432
46433             this.cb.fireEvent('remove', this.cb, this);
46434         }
46435         
46436     }
46437 });/*
46438  * RooJS Library 1.1.1
46439  * Copyright(c) 2008-2011  Alan Knowles
46440  *
46441  * License - LGPL
46442  */
46443  
46444
46445 /**
46446  * @class Roo.form.ComboNested
46447  * @extends Roo.form.ComboBox
46448  * A combobox for that allows selection of nested items in a list,
46449  * eg.
46450  *
46451  *  Book
46452  *    -> red
46453  *    -> green
46454  *  Table
46455  *    -> square
46456  *      ->red
46457  *      ->green
46458  *    -> rectangle
46459  *      ->green
46460  *      
46461  * 
46462  * @constructor
46463  * Create a new ComboNested
46464  * @param {Object} config Configuration options
46465  */
46466 Roo.form.ComboNested = function(config){
46467     Roo.form.ComboCheck.superclass.constructor.call(this, config);
46468     // should verify some data...
46469     // like
46470     // hiddenName = required..
46471     // displayField = required
46472     // valudField == required
46473     var req= [ 'hiddenName', 'displayField', 'valueField' ];
46474     var _t = this;
46475     Roo.each(req, function(e) {
46476         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
46477             throw "Roo.form.ComboNested : missing value for: " + e;
46478         }
46479     });
46480      
46481     
46482 };
46483
46484 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
46485    
46486     /*
46487      * @config {Number} max Number of columns to show
46488      */
46489     
46490     maxColumns : 3,
46491    
46492     list : null, // the outermost div..
46493     innerLists : null, // the
46494     views : null,
46495     stores : null,
46496     // private
46497     loadingChildren : false,
46498     
46499     onRender : function(ct, position)
46500     {
46501         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
46502         
46503         if(this.hiddenName){
46504             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
46505                     'before', true);
46506             this.hiddenField.value =
46507                 this.hiddenValue !== undefined ? this.hiddenValue :
46508                 this.value !== undefined ? this.value : '';
46509
46510             // prevent input submission
46511             this.el.dom.removeAttribute('name');
46512              
46513              
46514         }
46515         
46516         if(Roo.isGecko){
46517             this.el.dom.setAttribute('autocomplete', 'off');
46518         }
46519
46520         var cls = 'x-combo-list';
46521
46522         this.list = new Roo.Layer({
46523             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
46524         });
46525
46526         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
46527         this.list.setWidth(lw);
46528         this.list.swallowEvent('mousewheel');
46529         this.assetHeight = 0;
46530
46531         if(this.title){
46532             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
46533             this.assetHeight += this.header.getHeight();
46534         }
46535         this.innerLists = [];
46536         this.views = [];
46537         this.stores = [];
46538         for (var i =0 ; i < this.maxColumns; i++) {
46539             this.onRenderList( cls, i);
46540         }
46541         
46542         // always needs footer, as we are going to have an 'OK' button.
46543         this.footer = this.list.createChild({cls:cls+'-ft'});
46544         this.pageTb = new Roo.Toolbar(this.footer);  
46545         var _this = this;
46546         this.pageTb.add(  {
46547             
46548             text: 'Done',
46549             handler: function()
46550             {
46551                 _this.collapse();
46552             }
46553         });
46554         
46555         if ( this.allowBlank && !this.disableClear) {
46556             
46557             this.pageTb.add(new Roo.Toolbar.Fill(), {
46558                 cls: 'x-btn-icon x-btn-clear',
46559                 text: '&#160;',
46560                 handler: function()
46561                 {
46562                     _this.collapse();
46563                     _this.clearValue();
46564                     _this.onSelect(false, -1);
46565                 }
46566             });
46567         }
46568         if (this.footer) {
46569             this.assetHeight += this.footer.getHeight();
46570         }
46571         
46572     },
46573     onRenderList : function (  cls, i)
46574     {
46575         
46576         var lw = Math.floor(
46577                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
46578         );
46579         
46580         this.list.setWidth(lw); // default to '1'
46581
46582         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
46583         //il.on('mouseover', this.onViewOver, this, { list:  i });
46584         //il.on('mousemove', this.onViewMove, this, { list:  i });
46585         il.setWidth(lw);
46586         il.setStyle({ 'overflow-x' : 'hidden'});
46587
46588         if(!this.tpl){
46589             this.tpl = new Roo.Template({
46590                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
46591                 isEmpty: function (value, allValues) {
46592                     //Roo.log(value);
46593                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
46594                     return dl ? 'has-children' : 'no-children'
46595                 }
46596             });
46597         }
46598         
46599         var store  = this.store;
46600         if (i > 0) {
46601             store  = new Roo.data.SimpleStore({
46602                 //fields : this.store.reader.meta.fields,
46603                 reader : this.store.reader,
46604                 data : [ ]
46605             });
46606         }
46607         this.stores[i]  = store;
46608                   
46609         var view = this.views[i] = new Roo.View(
46610             il,
46611             this.tpl,
46612             {
46613                 singleSelect:true,
46614                 store: store,
46615                 selectedClass: this.selectedClass
46616             }
46617         );
46618         view.getEl().setWidth(lw);
46619         view.getEl().setStyle({
46620             position: i < 1 ? 'relative' : 'absolute',
46621             top: 0,
46622             left: (i * lw ) + 'px',
46623             display : i > 0 ? 'none' : 'block'
46624         });
46625         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
46626         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
46627         //view.on('click', this.onViewClick, this, { list : i });
46628
46629         store.on('beforeload', this.onBeforeLoad, this);
46630         store.on('load',  this.onLoad, this, { list  : i});
46631         store.on('loadexception', this.onLoadException, this);
46632
46633         // hide the other vies..
46634         
46635         
46636         
46637     },
46638       
46639     restrictHeight : function()
46640     {
46641         var mh = 0;
46642         Roo.each(this.innerLists, function(il,i) {
46643             var el = this.views[i].getEl();
46644             el.dom.style.height = '';
46645             var inner = el.dom;
46646             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
46647             // only adjust heights on other ones..
46648             mh = Math.max(h, mh);
46649             if (i < 1) {
46650                 
46651                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
46652                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
46653                
46654             }
46655             
46656             
46657         }, this);
46658         
46659         this.list.beginUpdate();
46660         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
46661         this.list.alignTo(this.el, this.listAlign);
46662         this.list.endUpdate();
46663         
46664     },
46665      
46666     
46667     // -- store handlers..
46668     // private
46669     onBeforeLoad : function()
46670     {
46671         if(!this.hasFocus){
46672             return;
46673         }
46674         this.innerLists[0].update(this.loadingText ?
46675                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
46676         this.restrictHeight();
46677         this.selectedIndex = -1;
46678     },
46679     // private
46680     onLoad : function(a,b,c,d)
46681     {
46682         if (!this.loadingChildren) {
46683             // then we are loading the top level. - hide the children
46684             for (var i = 1;i < this.views.length; i++) {
46685                 this.views[i].getEl().setStyle({ display : 'none' });
46686             }
46687             var lw = Math.floor(
46688                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
46689             );
46690         
46691              this.list.setWidth(lw); // default to '1'
46692
46693             
46694         }
46695         if(!this.hasFocus){
46696             return;
46697         }
46698         
46699         if(this.store.getCount() > 0) {
46700             this.expand();
46701             this.restrictHeight();   
46702         } else {
46703             this.onEmptyResults();
46704         }
46705         
46706         if (!this.loadingChildren) {
46707             this.selectActive();
46708         }
46709         /*
46710         this.stores[1].loadData([]);
46711         this.stores[2].loadData([]);
46712         this.views
46713         */    
46714     
46715         //this.el.focus();
46716     },
46717     
46718     
46719     // private
46720     onLoadException : function()
46721     {
46722         this.collapse();
46723         Roo.log(this.store.reader.jsonData);
46724         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
46725             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
46726         }
46727         
46728         
46729     },
46730     // no cleaning of leading spaces on blur here.
46731     cleanLeadingSpace : function(e) { },
46732     
46733
46734     onSelectChange : function (view, sels, opts )
46735     {
46736         var ix = view.getSelectedIndexes();
46737          
46738         if (opts.list > this.maxColumns - 2) {
46739             if (view.store.getCount()<  1) {
46740                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
46741
46742             } else  {
46743                 if (ix.length) {
46744                     // used to clear ?? but if we are loading unselected 
46745                     this.setFromData(view.store.getAt(ix[0]).data);
46746                 }
46747                 
46748             }
46749             
46750             return;
46751         }
46752         
46753         if (!ix.length) {
46754             // this get's fired when trigger opens..
46755            // this.setFromData({});
46756             var str = this.stores[opts.list+1];
46757             str.data.clear(); // removeall wihtout the fire events..
46758             return;
46759         }
46760         
46761         var rec = view.store.getAt(ix[0]);
46762          
46763         this.setFromData(rec.data);
46764         this.fireEvent('select', this, rec, ix[0]);
46765         
46766         var lw = Math.floor(
46767              (
46768                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
46769              ) / this.maxColumns
46770         );
46771         this.loadingChildren = true;
46772         this.stores[opts.list+1].loadDataFromChildren( rec );
46773         this.loadingChildren = false;
46774         var dl = this.stores[opts.list+1]. getTotalCount();
46775         
46776         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
46777         
46778         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
46779         for (var i = opts.list+2; i < this.views.length;i++) {
46780             this.views[i].getEl().setStyle({ display : 'none' });
46781         }
46782         
46783         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
46784         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
46785         
46786         if (this.isLoading) {
46787            // this.selectActive(opts.list);
46788         }
46789          
46790     },
46791     
46792     
46793     
46794     
46795     onDoubleClick : function()
46796     {
46797         this.collapse(); //??
46798     },
46799     
46800      
46801     
46802     
46803     
46804     // private
46805     recordToStack : function(store, prop, value, stack)
46806     {
46807         var cstore = new Roo.data.SimpleStore({
46808             //fields : this.store.reader.meta.fields, // we need array reader.. for
46809             reader : this.store.reader,
46810             data : [ ]
46811         });
46812         var _this = this;
46813         var record  = false;
46814         var srec = false;
46815         if(store.getCount() < 1){
46816             return false;
46817         }
46818         store.each(function(r){
46819             if(r.data[prop] == value){
46820                 record = r;
46821             srec = r;
46822                 return false;
46823             }
46824             if (r.data.cn && r.data.cn.length) {
46825                 cstore.loadDataFromChildren( r);
46826                 var cret = _this.recordToStack(cstore, prop, value, stack);
46827                 if (cret !== false) {
46828                     record = cret;
46829                     srec = r;
46830                     return false;
46831                 }
46832             }
46833              
46834             return true;
46835         });
46836         if (record == false) {
46837             return false
46838         }
46839         stack.unshift(srec);
46840         return record;
46841     },
46842     
46843     /*
46844      * find the stack of stores that match our value.
46845      *
46846      * 
46847      */
46848     
46849     selectActive : function ()
46850     {
46851         // if store is not loaded, then we will need to wait for that to happen first.
46852         var stack = [];
46853         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
46854         for (var i = 0; i < stack.length; i++ ) {
46855             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
46856         }
46857         
46858     }
46859         
46860          
46861     
46862     
46863     
46864     
46865 });/*
46866  * Based on:
46867  * Ext JS Library 1.1.1
46868  * Copyright(c) 2006-2007, Ext JS, LLC.
46869  *
46870  * Originally Released Under LGPL - original licence link has changed is not relivant.
46871  *
46872  * Fork - LGPL
46873  * <script type="text/javascript">
46874  */
46875 /**
46876  * @class Roo.form.Checkbox
46877  * @extends Roo.form.Field
46878  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
46879  * @constructor
46880  * Creates a new Checkbox
46881  * @param {Object} config Configuration options
46882  */
46883 Roo.form.Checkbox = function(config){
46884     Roo.form.Checkbox.superclass.constructor.call(this, config);
46885     this.addEvents({
46886         /**
46887          * @event check
46888          * Fires when the checkbox is checked or unchecked.
46889              * @param {Roo.form.Checkbox} this This checkbox
46890              * @param {Boolean} checked The new checked value
46891              */
46892         check : true
46893     });
46894 };
46895
46896 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
46897     /**
46898      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
46899      */
46900     focusClass : undefined,
46901     /**
46902      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
46903      */
46904     fieldClass: "x-form-field",
46905     /**
46906      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
46907      */
46908     checked: false,
46909     /**
46910      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46911      * {tag: "input", type: "checkbox", autocomplete: "off"})
46912      */
46913     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
46914     /**
46915      * @cfg {String} boxLabel The text that appears beside the checkbox
46916      */
46917     boxLabel : "",
46918     /**
46919      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
46920      */  
46921     inputValue : '1',
46922     /**
46923      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
46924      */
46925      valueOff: '0', // value when not checked..
46926
46927     actionMode : 'viewEl', 
46928     //
46929     // private
46930     itemCls : 'x-menu-check-item x-form-item',
46931     groupClass : 'x-menu-group-item',
46932     inputType : 'hidden',
46933     
46934     
46935     inSetChecked: false, // check that we are not calling self...
46936     
46937     inputElement: false, // real input element?
46938     basedOn: false, // ????
46939     
46940     isFormField: true, // not sure where this is needed!!!!
46941
46942     onResize : function(){
46943         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
46944         if(!this.boxLabel){
46945             this.el.alignTo(this.wrap, 'c-c');
46946         }
46947     },
46948
46949     initEvents : function(){
46950         Roo.form.Checkbox.superclass.initEvents.call(this);
46951         this.el.on("click", this.onClick,  this);
46952         this.el.on("change", this.onClick,  this);
46953     },
46954
46955
46956     getResizeEl : function(){
46957         return this.wrap;
46958     },
46959
46960     getPositionEl : function(){
46961         return this.wrap;
46962     },
46963
46964     // private
46965     onRender : function(ct, position){
46966         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
46967         /*
46968         if(this.inputValue !== undefined){
46969             this.el.dom.value = this.inputValue;
46970         }
46971         */
46972         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
46973         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
46974         var viewEl = this.wrap.createChild({ 
46975             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
46976         this.viewEl = viewEl;   
46977         this.wrap.on('click', this.onClick,  this); 
46978         
46979         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
46980         this.el.on('propertychange', this.setFromHidden,  this);  //ie
46981         
46982         
46983         
46984         if(this.boxLabel){
46985             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
46986         //    viewEl.on('click', this.onClick,  this); 
46987         }
46988         //if(this.checked){
46989             this.setChecked(this.checked);
46990         //}else{
46991             //this.checked = this.el.dom;
46992         //}
46993
46994     },
46995
46996     // private
46997     initValue : Roo.emptyFn,
46998
46999     /**
47000      * Returns the checked state of the checkbox.
47001      * @return {Boolean} True if checked, else false
47002      */
47003     getValue : function(){
47004         if(this.el){
47005             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
47006         }
47007         return this.valueOff;
47008         
47009     },
47010
47011         // private
47012     onClick : function(){ 
47013         if (this.disabled) {
47014             return;
47015         }
47016         this.setChecked(!this.checked);
47017
47018         //if(this.el.dom.checked != this.checked){
47019         //    this.setValue(this.el.dom.checked);
47020        // }
47021     },
47022
47023     /**
47024      * Sets the checked state of the checkbox.
47025      * On is always based on a string comparison between inputValue and the param.
47026      * @param {Boolean/String} value - the value to set 
47027      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
47028      */
47029     setValue : function(v,suppressEvent){
47030         
47031         
47032         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
47033         //if(this.el && this.el.dom){
47034         //    this.el.dom.checked = this.checked;
47035         //    this.el.dom.defaultChecked = this.checked;
47036         //}
47037         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
47038         //this.fireEvent("check", this, this.checked);
47039     },
47040     // private..
47041     setChecked : function(state,suppressEvent)
47042     {
47043         if (this.inSetChecked) {
47044             this.checked = state;
47045             return;
47046         }
47047         
47048     
47049         if(this.wrap){
47050             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
47051         }
47052         this.checked = state;
47053         if(suppressEvent !== true){
47054             this.fireEvent('check', this, state);
47055         }
47056         this.inSetChecked = true;
47057                  
47058                 this.el.dom.value = state ? this.inputValue : this.valueOff;
47059                  
47060         this.inSetChecked = false;
47061         
47062     },
47063     // handle setting of hidden value by some other method!!?!?
47064     setFromHidden: function()
47065     {
47066         if(!this.el){
47067             return;
47068         }
47069         //console.log("SET FROM HIDDEN");
47070         //alert('setFrom hidden');
47071         this.setValue(this.el.dom.value);
47072     },
47073     
47074     onDestroy : function()
47075     {
47076         if(this.viewEl){
47077             Roo.get(this.viewEl).remove();
47078         }
47079          
47080         Roo.form.Checkbox.superclass.onDestroy.call(this);
47081     },
47082     
47083     setBoxLabel : function(str)
47084     {
47085         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
47086     }
47087
47088 });/*
47089  * Based on:
47090  * Ext JS Library 1.1.1
47091  * Copyright(c) 2006-2007, Ext JS, LLC.
47092  *
47093  * Originally Released Under LGPL - original licence link has changed is not relivant.
47094  *
47095  * Fork - LGPL
47096  * <script type="text/javascript">
47097  */
47098  
47099 /**
47100  * @class Roo.form.Radio
47101  * @extends Roo.form.Checkbox
47102  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
47103  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
47104  * @constructor
47105  * Creates a new Radio
47106  * @param {Object} config Configuration options
47107  */
47108 Roo.form.Radio = function(){
47109     Roo.form.Radio.superclass.constructor.apply(this, arguments);
47110 };
47111 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
47112     inputType: 'radio',
47113
47114     /**
47115      * If this radio is part of a group, it will return the selected value
47116      * @return {String}
47117      */
47118     getGroupValue : function(){
47119         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
47120     },
47121     
47122     
47123     onRender : function(ct, position){
47124         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
47125         
47126         if(this.inputValue !== undefined){
47127             this.el.dom.value = this.inputValue;
47128         }
47129          
47130         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
47131         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
47132         //var viewEl = this.wrap.createChild({ 
47133         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
47134         //this.viewEl = viewEl;   
47135         //this.wrap.on('click', this.onClick,  this); 
47136         
47137         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
47138         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
47139         
47140         
47141         
47142         if(this.boxLabel){
47143             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
47144         //    viewEl.on('click', this.onClick,  this); 
47145         }
47146          if(this.checked){
47147             this.el.dom.checked =   'checked' ;
47148         }
47149          
47150     },
47151     /**
47152      * Sets the checked state of the checkbox.
47153      * On is always based on a string comparison between inputValue and the param.
47154      * @param {Boolean/String} value - the value to set 
47155      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
47156      */
47157     setValue : function(v,suppressEvent){
47158         
47159         
47160         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
47161         //if(this.el && this.el.dom){
47162         //    this.el.dom.checked = this.checked;
47163         //    this.el.dom.defaultChecked = this.checked;
47164         //}
47165         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
47166         
47167         this.el.dom.form[this.name].value = v;
47168      
47169         //this.fireEvent("check", this, this.checked);
47170     },
47171     // private..
47172     setChecked : function(state,suppressEvent)
47173     {
47174          
47175         if(this.wrap){
47176             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
47177         }
47178         this.checked = state;
47179         if(suppressEvent !== true){
47180             this.fireEvent('check', this, state);
47181         }
47182                  
47183                   
47184        
47185         
47186     },
47187     reset : function(){
47188         // this.setValue(this.resetValue);
47189         //this.originalValue = this.getValue();
47190         this.clearInvalid();
47191     } 
47192     
47193 });Roo.rtf = {}; // namespace
47194 Roo.rtf.Hex = function(hex)
47195 {
47196     this.hexstr = hex;
47197 };
47198 Roo.rtf.Paragraph = function(opts)
47199 {
47200     this.content = []; ///??? is that used?
47201 };Roo.rtf.Span = function(opts)
47202 {
47203     this.value = opts.value;
47204 };
47205
47206 Roo.rtf.Group = function(parent)
47207 {
47208     // we dont want to acutally store parent - it will make debug a nightmare..
47209     this.content = [];
47210     this.cn  = [];
47211      
47212        
47213     
47214 };
47215
47216 Roo.rtf.Group.prototype = {
47217     ignorable : false,
47218     content: false,
47219     cn: false,
47220     addContent : function(node) {
47221         // could set styles...
47222         this.content.push(node);
47223     },
47224     addChild : function(cn)
47225     {
47226         this.cn.push(cn);
47227     },
47228     // only for images really...
47229     toDataURL : function()
47230     {
47231         var mimetype = false;
47232         switch(true) {
47233             case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0: 
47234                 mimetype = "image/png";
47235                 break;
47236              case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
47237                 mimetype = "image/jpeg";
47238                 break;
47239             default :
47240                 return 'about:blank'; // ?? error?
47241         }
47242         
47243         
47244         var hexstring = this.content[this.content.length-1].value;
47245         
47246         return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
47247             return String.fromCharCode(parseInt(a, 16));
47248         }).join(""));
47249     }
47250     
47251 };
47252 // this looks like it's normally the {rtf{ .... }}
47253 Roo.rtf.Document = function()
47254 {
47255     // we dont want to acutally store parent - it will make debug a nightmare..
47256     this.rtlch  = [];
47257     this.content = [];
47258     this.cn = [];
47259     
47260 };
47261 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, { 
47262     addChild : function(cn)
47263     {
47264         this.cn.push(cn);
47265         switch(cn.type) {
47266             case 'rtlch': // most content seems to be inside this??
47267             case 'listtext':
47268             case 'shpinst':
47269                 this.rtlch.push(cn);
47270                 return;
47271             default:
47272                 this[cn.type] = cn;
47273         }
47274         
47275     },
47276     
47277     getElementsByType : function(type)
47278     {
47279         var ret =  [];
47280         this._getElementsByType(type, ret, this.cn, 'rtf');
47281         return ret;
47282     },
47283     _getElementsByType : function (type, ret, search_array, path)
47284     {
47285         search_array.forEach(function(n,i) {
47286             if (n.type == type) {
47287                 n.path = path + '/' + n.type + ':' + i;
47288                 ret.push(n);
47289             }
47290             if (n.cn.length > 0) {
47291                 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
47292             }
47293         },this);
47294     }
47295     
47296 });
47297  
47298 Roo.rtf.Ctrl = function(opts)
47299 {
47300     this.value = opts.value;
47301     this.param = opts.param;
47302 };
47303 /**
47304  *
47305  *
47306  * based on this https://github.com/iarna/rtf-parser
47307  * it's really only designed to extract pict from pasted RTF 
47308  *
47309  * usage:
47310  *
47311  *  var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
47312  *  
47313  *
47314  */
47315
47316  
47317
47318
47319
47320 Roo.rtf.Parser = function(text) {
47321     //super({objectMode: true})
47322     this.text = '';
47323     this.parserState = this.parseText;
47324     
47325     // these are for interpeter...
47326     this.doc = {};
47327     ///this.parserState = this.parseTop
47328     this.groupStack = [];
47329     this.hexStore = [];
47330     this.doc = false;
47331     
47332     this.groups = []; // where we put the return.
47333     
47334     for (var ii = 0; ii < text.length; ++ii) {
47335         ++this.cpos;
47336         
47337         if (text[ii] === '\n') {
47338             ++this.row;
47339             this.col = 1;
47340         } else {
47341             ++this.col;
47342         }
47343         this.parserState(text[ii]);
47344     }
47345     
47346     
47347     
47348 };
47349 Roo.rtf.Parser.prototype = {
47350     text : '', // string being parsed..
47351     controlWord : '',
47352     controlWordParam :  '',
47353     hexChar : '',
47354     doc : false,
47355     group: false,
47356     groupStack : false,
47357     hexStore : false,
47358     
47359     
47360     cpos : 0, 
47361     row : 1, // reportin?
47362     col : 1, //
47363
47364      
47365     push : function (el)
47366     {
47367         var m = 'cmd'+ el.type;
47368         if (typeof(this[m]) == 'undefined') {
47369             Roo.log('invalid cmd:' + el.type);
47370             return;
47371         }
47372         this[m](el);
47373         //Roo.log(el);
47374     },
47375     flushHexStore : function()
47376     {
47377         if (this.hexStore.length < 1) {
47378             return;
47379         }
47380         var hexstr = this.hexStore.map(
47381             function(cmd) {
47382                 return cmd.value;
47383         }).join('');
47384         
47385         this.group.addContent( new Roo.rtf.Hex( hexstr ));
47386               
47387             
47388         this.hexStore.splice(0)
47389         
47390     },
47391     
47392     cmdgroupstart : function()
47393     {
47394         this.flushHexStore();
47395         if (this.group) {
47396             this.groupStack.push(this.group);
47397         }
47398          // parent..
47399         if (this.doc === false) {
47400             this.group = this.doc = new Roo.rtf.Document();
47401             return;
47402             
47403         }
47404         this.group = new Roo.rtf.Group(this.group);
47405     },
47406     cmdignorable : function()
47407     {
47408         this.flushHexStore();
47409         this.group.ignorable = true;
47410     },
47411     cmdendparagraph : function()
47412     {
47413         this.flushHexStore();
47414         this.group.addContent(new Roo.rtf.Paragraph());
47415     },
47416     cmdgroupend : function ()
47417     {
47418         this.flushHexStore();
47419         var endingGroup = this.group;
47420         
47421         
47422         this.group = this.groupStack.pop();
47423         if (this.group) {
47424             this.group.addChild(endingGroup);
47425         }
47426         
47427         
47428         
47429         var doc = this.group || this.doc;
47430         //if (endingGroup instanceof FontTable) {
47431         //  doc.fonts = endingGroup.table
47432         //} else if (endingGroup instanceof ColorTable) {
47433         //  doc.colors = endingGroup.table
47434         //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
47435         if (endingGroup.ignorable === false) {
47436             //code
47437             this.groups.push(endingGroup);
47438            // Roo.log( endingGroup );
47439         }
47440             //Roo.each(endingGroup.content, function(item)) {
47441             //    doc.addContent(item);
47442             //}
47443             //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
47444         //}
47445     },
47446     cmdtext : function (cmd)
47447     {
47448         this.flushHexStore();
47449         if (!this.group) { // an RTF fragment, missing the {\rtf1 header
47450             //this.group = this.doc
47451             return;  // we really don't care about stray text...
47452         }
47453         this.group.addContent(new Roo.rtf.Span(cmd));
47454     },
47455     cmdcontrolword : function (cmd)
47456     {
47457         this.flushHexStore();
47458         if (!this.group.type) {
47459             this.group.type = cmd.value;
47460             return;
47461         }
47462         this.group.addContent(new Roo.rtf.Ctrl(cmd));
47463         // we actually don't care about ctrl words...
47464         return ;
47465         /*
47466         var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
47467         if (this[method]) {
47468             this[method](cmd.param)
47469         } else {
47470             if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
47471         }
47472         */
47473     },
47474     cmdhexchar : function(cmd) {
47475         this.hexStore.push(cmd);
47476     },
47477     cmderror : function(cmd) {
47478         throw cmd.value;
47479     },
47480     
47481     /*
47482       _flush (done) {
47483         if (this.text !== '\u0000') this.emitText()
47484         done()
47485       }
47486       */
47487       
47488       
47489     parseText : function(c)
47490     {
47491         if (c === '\\') {
47492             this.parserState = this.parseEscapes;
47493         } else if (c === '{') {
47494             this.emitStartGroup();
47495         } else if (c === '}') {
47496             this.emitEndGroup();
47497         } else if (c === '\x0A' || c === '\x0D') {
47498             // cr/lf are noise chars
47499         } else {
47500             this.text += c;
47501         }
47502     },
47503     
47504     parseEscapes: function (c)
47505     {
47506         if (c === '\\' || c === '{' || c === '}') {
47507             this.text += c;
47508             this.parserState = this.parseText;
47509         } else {
47510             this.parserState = this.parseControlSymbol;
47511             this.parseControlSymbol(c);
47512         }
47513     },
47514     parseControlSymbol: function(c)
47515     {
47516         if (c === '~') {
47517             this.text += '\u00a0'; // nbsp
47518             this.parserState = this.parseText
47519         } else if (c === '-') {
47520              this.text += '\u00ad'; // soft hyphen
47521         } else if (c === '_') {
47522             this.text += '\u2011'; // non-breaking hyphen
47523         } else if (c === '*') {
47524             this.emitIgnorable();
47525             this.parserState = this.parseText;
47526         } else if (c === "'") {
47527             this.parserState = this.parseHexChar;
47528         } else if (c === '|') { // formula cacter
47529             this.emitFormula();
47530             this.parserState = this.parseText;
47531         } else if (c === ':') { // subentry in an index entry
47532             this.emitIndexSubEntry();
47533             this.parserState = this.parseText;
47534         } else if (c === '\x0a') {
47535             this.emitEndParagraph();
47536             this.parserState = this.parseText;
47537         } else if (c === '\x0d') {
47538             this.emitEndParagraph();
47539             this.parserState = this.parseText;
47540         } else {
47541             this.parserState = this.parseControlWord;
47542             this.parseControlWord(c);
47543         }
47544     },
47545     parseHexChar: function (c)
47546     {
47547         if (/^[A-Fa-f0-9]$/.test(c)) {
47548             this.hexChar += c;
47549             if (this.hexChar.length >= 2) {
47550               this.emitHexChar();
47551               this.parserState = this.parseText;
47552             }
47553             return;
47554         }
47555         this.emitError("Invalid character \"" + c + "\" in hex literal.");
47556         this.parserState = this.parseText;
47557         
47558     },
47559     parseControlWord : function(c)
47560     {
47561         if (c === ' ') {
47562             this.emitControlWord();
47563             this.parserState = this.parseText;
47564         } else if (/^[-\d]$/.test(c)) {
47565             this.parserState = this.parseControlWordParam;
47566             this.controlWordParam += c;
47567         } else if (/^[A-Za-z]$/.test(c)) {
47568           this.controlWord += c;
47569         } else {
47570           this.emitControlWord();
47571           this.parserState = this.parseText;
47572           this.parseText(c);
47573         }
47574     },
47575     parseControlWordParam : function (c) {
47576         if (/^\d$/.test(c)) {
47577           this.controlWordParam += c;
47578         } else if (c === ' ') {
47579           this.emitControlWord();
47580           this.parserState = this.parseText;
47581         } else {
47582           this.emitControlWord();
47583           this.parserState = this.parseText;
47584           this.parseText(c);
47585         }
47586     },
47587     
47588     
47589     
47590     
47591     emitText : function () {
47592         if (this.text === '') {
47593             return;
47594         }
47595         this.push({
47596             type: 'text',
47597             value: this.text,
47598             pos: this.cpos,
47599             row: this.row,
47600             col: this.col
47601         });
47602         this.text = ''
47603     },
47604     emitControlWord : function ()
47605     {
47606         this.emitText();
47607         if (this.controlWord === '') {
47608             // do we want to track this - it seems just to cause problems.
47609             //this.emitError('empty control word');
47610         } else {
47611             this.push({
47612                   type: 'controlword',
47613                   value: this.controlWord,
47614                   param: this.controlWordParam !== '' && Number(this.controlWordParam),
47615                   pos: this.cpos,
47616                   row: this.row,
47617                   col: this.col
47618             });
47619         }
47620         this.controlWord = '';
47621         this.controlWordParam = '';
47622     },
47623     emitStartGroup : function ()
47624     {
47625         this.emitText();
47626         this.push({
47627             type: 'groupstart',
47628             pos: this.cpos,
47629             row: this.row,
47630             col: this.col
47631         });
47632     },
47633     emitEndGroup : function ()
47634     {
47635         this.emitText();
47636         this.push({
47637             type: 'groupend',
47638             pos: this.cpos,
47639             row: this.row,
47640             col: this.col
47641         });
47642     },
47643     emitIgnorable : function ()
47644     {
47645         this.emitText();
47646         this.push({
47647             type: 'ignorable',
47648             pos: this.cpos,
47649             row: this.row,
47650             col: this.col
47651         });
47652     },
47653     emitHexChar : function ()
47654     {
47655         this.emitText();
47656         this.push({
47657             type: 'hexchar',
47658             value: this.hexChar,
47659             pos: this.cpos,
47660             row: this.row,
47661             col: this.col
47662         });
47663         this.hexChar = ''
47664     },
47665     emitError : function (message)
47666     {
47667       this.emitText();
47668       this.push({
47669             type: 'error',
47670             value: message,
47671             row: this.row,
47672             col: this.col,
47673             char: this.cpos //,
47674             //stack: new Error().stack
47675         });
47676     },
47677     emitEndParagraph : function () {
47678         this.emitText();
47679         this.push({
47680             type: 'endparagraph',
47681             pos: this.cpos,
47682             row: this.row,
47683             col: this.col
47684         });
47685     }
47686      
47687 } ;
47688 Roo.htmleditor = {};
47689  
47690 /**
47691  * @class Roo.htmleditor.Filter
47692  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
47693  * @cfg {DomElement} node The node to iterate and filter
47694  * @cfg {boolean|String|Array} tag Tags to replace 
47695  * @constructor
47696  * Create a new Filter.
47697  * @param {Object} config Configuration options
47698  */
47699
47700
47701
47702 Roo.htmleditor.Filter = function(cfg) {
47703     Roo.apply(this.cfg);
47704     // this does not actually call walk as it's really just a abstract class
47705 }
47706
47707
47708 Roo.htmleditor.Filter.prototype = {
47709     
47710     node: false,
47711     
47712     tag: false,
47713
47714     // overrride to do replace comments.
47715     replaceComment : false,
47716     
47717     // overrride to do replace or do stuff with tags..
47718     replaceTag : false,
47719     
47720     walk : function(dom)
47721     {
47722         Roo.each( Array.from(dom.childNodes), function( e ) {
47723             switch(true) {
47724                 
47725                 case e.nodeType == 8 &&  this.replaceComment  !== false: // comment
47726                     this.replaceComment(e);
47727                     return;
47728                 
47729                 case e.nodeType != 1: //not a node.
47730                     return;
47731                 
47732                 case this.tag === true: // everything
47733                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1:
47734                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":":
47735                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
47736                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
47737                     if (this.replaceTag && false === this.replaceTag(e)) {
47738                         return;
47739                     }
47740                     if (e.hasChildNodes()) {
47741                         this.walk(e);
47742                     }
47743                     return;
47744                 
47745                 default:    // tags .. that do not match.
47746                     if (e.hasChildNodes()) {
47747                         this.walk(e);
47748                     }
47749             }
47750             
47751         }, this);
47752         
47753     },
47754     
47755     
47756     removeNodeKeepChildren : function( node)
47757     {
47758     
47759         ar = Array.from(node.childNodes);
47760         for (var i = 0; i < ar.length; i++) {
47761          
47762             node.removeChild(ar[i]);
47763             // what if we need to walk these???
47764             node.parentNode.insertBefore(ar[i], node);
47765            
47766         }
47767         node.parentNode.removeChild(node);
47768     },
47769
47770     searchTag : function(dom)
47771     {
47772         if(this.tag === false) {
47773             return;
47774         }
47775
47776         var els = dom.getElementsByTagName(this.tag);
47777
47778         Roo.each(Array.from(els), function(e){
47779             if(e.parentNode == null) {
47780                 return;
47781             }
47782             if(this.replaceTag) {
47783                 this.replaceTag(e);
47784             }
47785         }, this);
47786     }
47787 }; 
47788
47789 /**
47790  * @class Roo.htmleditor.FilterAttributes
47791  * clean attributes and  styles including http:// etc.. in attribute
47792  * @constructor
47793 * Run a new Attribute Filter
47794 * @param {Object} config Configuration options
47795  */
47796 Roo.htmleditor.FilterAttributes = function(cfg)
47797 {
47798     Roo.apply(this, cfg);
47799     this.attrib_black = this.attrib_black || [];
47800     this.attrib_white = this.attrib_white || [];
47801
47802     this.attrib_clean = this.attrib_clean || [];
47803     this.style_white = this.style_white || [];
47804     this.style_black = this.style_black || [];
47805     this.walk(cfg.node);
47806 }
47807
47808 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
47809 {
47810     tag: true, // all tags
47811     
47812     attrib_black : false, // array
47813     attrib_clean : false,
47814     attrib_white : false,
47815
47816     style_white : false,
47817     style_black : false,
47818      
47819      
47820     replaceTag : function(node)
47821     {
47822         if (!node.attributes || !node.attributes.length) {
47823             return true;
47824         }
47825         
47826         for (var i = node.attributes.length-1; i > -1 ; i--) {
47827             var a = node.attributes[i];
47828             //console.log(a);
47829             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
47830                 node.removeAttribute(a.name);
47831                 continue;
47832             }
47833             
47834             
47835             
47836             if (a.name.toLowerCase().substr(0,2)=='on')  {
47837                 node.removeAttribute(a.name);
47838                 continue;
47839             }
47840             
47841             
47842             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
47843                 node.removeAttribute(a.name);
47844                 continue;
47845             }
47846             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
47847                 this.cleanAttr(node,a.name,a.value); // fixme..
47848                 continue;
47849             }
47850             if (a.name == 'style') {
47851                 this.cleanStyle(node,a.name,a.value);
47852                 continue;
47853             }
47854             /// clean up MS crap..
47855             // tecnically this should be a list of valid class'es..
47856             
47857             
47858             if (a.name == 'class') {
47859                 if (a.value.match(/^Mso/)) {
47860                     node.removeAttribute('class');
47861                 }
47862                 
47863                 if (a.value.match(/^body$/)) {
47864                     node.removeAttribute('class');
47865                 }
47866                 continue;
47867             }
47868             
47869             
47870             // style cleanup!?
47871             // class cleanup?
47872             
47873         }
47874         return true; // clean children
47875     },
47876         
47877     cleanAttr: function(node, n,v)
47878     {
47879         
47880         if (v.match(/^\./) || v.match(/^\//)) {
47881             return;
47882         }
47883         if (v.match(/^(http|https):\/\//)
47884             || v.match(/^mailto:/) 
47885             || v.match(/^ftp:/)
47886             || v.match(/^data:/)
47887             ) {
47888             return;
47889         }
47890         if (v.match(/^#/)) {
47891             return;
47892         }
47893         if (v.match(/^\{/)) { // allow template editing.
47894             return;
47895         }
47896 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
47897         node.removeAttribute(n);
47898         
47899     },
47900     cleanStyle : function(node,  n,v)
47901     {
47902         if (v.match(/expression/)) { //XSS?? should we even bother..
47903             node.removeAttribute(n);
47904             return;
47905         }
47906         
47907         var parts = v.split(/;/);
47908         var clean = [];
47909         
47910         Roo.each(parts, function(p) {
47911             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
47912             if (!p.length) {
47913                 return true;
47914             }
47915             var l = p.split(':').shift().replace(/\s+/g,'');
47916             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
47917             
47918             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
47919                 return true;
47920             }
47921             //Roo.log()
47922             // only allow 'c whitelisted system attributes'
47923             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
47924                 return true;
47925             }
47926             
47927             
47928             clean.push(p);
47929             return true;
47930         },this);
47931         if (clean.length) { 
47932             node.setAttribute(n, clean.join(';'));
47933         } else {
47934             node.removeAttribute(n);
47935         }
47936         
47937     }
47938         
47939         
47940         
47941     
47942 });/**
47943  * @class Roo.htmleditor.FilterBlack
47944  * remove blacklisted elements.
47945  * @constructor
47946  * Run a new Blacklisted Filter
47947  * @param {Object} config Configuration options
47948  */
47949
47950 Roo.htmleditor.FilterBlack = function(cfg)
47951 {
47952     Roo.apply(this, cfg);
47953     this.walk(cfg.node);
47954 }
47955
47956 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
47957 {
47958     tag : true, // all elements.
47959    
47960     replaceTag : function(n)
47961     {
47962         n.parentNode.removeChild(n);
47963     }
47964 });
47965 /**
47966  * @class Roo.htmleditor.FilterComment
47967  * remove comments.
47968  * @constructor
47969 * Run a new Comments Filter
47970 * @param {Object} config Configuration options
47971  */
47972 Roo.htmleditor.FilterComment = function(cfg)
47973 {
47974     this.walk(cfg.node);
47975 }
47976
47977 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
47978 {
47979   
47980     replaceComment : function(n)
47981     {
47982         n.parentNode.removeChild(n);
47983     }
47984 });/**
47985  * @class Roo.htmleditor.FilterEmpty
47986  * filter empty elements
47987  * @constructor
47988  * Run a new Empty Filter
47989  * @param {Object} config Configuration options
47990  */
47991
47992 Roo.htmleditor.FilterEmpty = function(cfg)
47993 {
47994     // no need to apply config.
47995     this.walk(cfg.node);
47996 }
47997
47998 Roo.extend(Roo.htmleditor.FilterEmpty, Roo.htmleditor.FilterBlack,
47999 {
48000      
48001     tag : true,
48002      
48003  
48004     replaceTag : function(node)
48005     {
48006         // start from leaf node
48007         if(node.hasChildNodes()) {
48008             this.walk(node);
48009         }
48010
48011         // only filter empty leaf element with certain tags
48012         if(
48013             ['B', 'I', 'U', 'S'].indexOf(node.tagName) < 0
48014             ||
48015             node.attributes && node.attributes.length > 0
48016             ||
48017             node.hasChildNodes()
48018         ) {
48019             return false; // don't walk
48020         }
48021
48022         Roo.htmleditor.FilterBlack.prototype.replaceTag.call(this, node);
48023         return false; // don't walk
48024      
48025     }
48026     
48027 });/**
48028  * @class Roo.htmleditor.FilterKeepChildren
48029  * remove tags but keep children
48030  * @constructor
48031  * Run a new Keep Children Filter
48032  * @param {Object} config Configuration options
48033  */
48034
48035 Roo.htmleditor.FilterKeepChildren = function(cfg)
48036 {
48037     Roo.apply(this, cfg);
48038     if (this.tag === false) {
48039         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
48040     }
48041     // hacky?
48042     if ((typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)) {
48043         this.cleanNamespace = true;
48044     }
48045         
48046     this.walk(cfg.node);
48047 }
48048
48049 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
48050 {
48051     cleanNamespace : false, // should really be an option, rather than using ':' inside of this tag.
48052   
48053     replaceTag : function(node)
48054     {
48055         // walk children...
48056         //Roo.log(node.tagName);
48057         var ar = Array.from(node.childNodes);
48058         //remove first..
48059         
48060         for (var i = 0; i < ar.length; i++) {
48061             var e = ar[i];
48062             if (e.nodeType == 1) {
48063                 if (
48064                     (typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1)
48065                     || // array and it matches
48066                     (typeof(this.tag) == 'string' && this.tag == e.tagName)
48067                     ||
48068                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)
48069                     ||
48070                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":")
48071                 ) {
48072                     this.replaceTag(ar[i]); // child is blacklisted as well...
48073                     continue;
48074                 }
48075             }
48076         }  
48077         ar = Array.from(node.childNodes);
48078         for (var i = 0; i < ar.length; i++) {
48079          
48080             node.removeChild(ar[i]);
48081             // what if we need to walk these???
48082             node.parentNode.insertBefore(ar[i], node);
48083             if (this.tag !== false) {
48084                 this.walk(ar[i]);
48085                 
48086             }
48087         }
48088         //Roo.log("REMOVE:" + node.tagName);
48089         node.parentNode.removeChild(node);
48090         return false; // don't walk children
48091         
48092         
48093     }
48094 });/**
48095  * @class Roo.htmleditor.FilterParagraph
48096  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
48097  * like on 'push' to remove the <p> tags and replace them with line breaks.
48098  * @constructor
48099  * Run a new Paragraph Filter
48100  * @param {Object} config Configuration options
48101  */
48102
48103 Roo.htmleditor.FilterParagraph = function(cfg)
48104 {
48105     // no need to apply config.
48106     this.searchTag(cfg.node);
48107 }
48108
48109 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
48110 {
48111     
48112      
48113     tag : 'P',
48114     
48115      
48116     replaceTag : function(node)
48117     {
48118         
48119         if (node.childNodes.length == 1 &&
48120             node.childNodes[0].nodeType == 3 &&
48121             node.childNodes[0].textContent.trim().length < 1
48122             ) {
48123             // remove and replace with '<BR>';
48124             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
48125             return false; // no need to walk..
48126         }
48127
48128         var ar = Array.from(node.childNodes);
48129         for (var i = 0; i < ar.length; i++) {
48130             node.removeChild(ar[i]);
48131             // what if we need to walk these???
48132             node.parentNode.insertBefore(ar[i], node);
48133         }
48134         // now what about this?
48135         // <p> &nbsp; </p>
48136         
48137         // double BR.
48138         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
48139         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
48140         node.parentNode.removeChild(node);
48141         
48142         return false;
48143
48144     }
48145     
48146 });/**
48147  * @class Roo.htmleditor.FilterHashLink
48148  * remove hash link
48149  * @constructor
48150  * Run a new Hash Link Filter
48151  * @param {Object} config Configuration options
48152  */
48153
48154  Roo.htmleditor.FilterHashLink = function(cfg)
48155  {
48156      // no need to apply config.
48157     //  this.walk(cfg.node);
48158     this.searchTag(cfg.node);
48159  }
48160  
48161  Roo.extend(Roo.htmleditor.FilterHashLink, Roo.htmleditor.Filter,
48162  {
48163       
48164      tag : 'A',
48165      
48166       
48167      replaceTag : function(node)
48168      {
48169          for(var i = 0; i < node.attributes.length; i ++) {
48170              var a = node.attributes[i];
48171
48172              if(a.name.toLowerCase() == 'href' && a.value.startsWith('#')) {
48173                  this.removeNodeKeepChildren(node);
48174              }
48175          }
48176          
48177          return false;
48178  
48179      }
48180      
48181  });/**
48182  * @class Roo.htmleditor.FilterSpan
48183  * filter span's with no attributes out..
48184  * @constructor
48185  * Run a new Span Filter
48186  * @param {Object} config Configuration options
48187  */
48188
48189 Roo.htmleditor.FilterSpan = function(cfg)
48190 {
48191     // no need to apply config.
48192     this.searchTag(cfg.node);
48193 }
48194
48195 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
48196 {
48197      
48198     tag : 'SPAN',
48199      
48200  
48201     replaceTag : function(node)
48202     {
48203         if (node.attributes && node.attributes.length > 0) {
48204             return true; // walk if there are any.
48205         }
48206         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
48207         return false;
48208      
48209     }
48210     
48211 });/**
48212  * @class Roo.htmleditor.FilterTableWidth
48213   try and remove table width data - as that frequently messes up other stuff.
48214  * 
48215  *      was cleanTableWidths.
48216  *
48217  * Quite often pasting from word etc.. results in tables with column and widths.
48218  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
48219  *
48220  * @constructor
48221  * Run a new Table Filter
48222  * @param {Object} config Configuration options
48223  */
48224
48225 Roo.htmleditor.FilterTableWidth = function(cfg)
48226 {
48227     // no need to apply config.
48228     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
48229     this.walk(cfg.node);
48230 }
48231
48232 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
48233 {
48234      
48235      
48236     
48237     replaceTag: function(node) {
48238         
48239         
48240       
48241         if (node.hasAttribute('width')) {
48242             node.removeAttribute('width');
48243         }
48244         
48245          
48246         if (node.hasAttribute("style")) {
48247             // pretty basic...
48248             
48249             var styles = node.getAttribute("style").split(";");
48250             var nstyle = [];
48251             Roo.each(styles, function(s) {
48252                 if (!s.match(/:/)) {
48253                     return;
48254                 }
48255                 var kv = s.split(":");
48256                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
48257                     return;
48258                 }
48259                 // what ever is left... we allow.
48260                 nstyle.push(s);
48261             });
48262             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
48263             if (!nstyle.length) {
48264                 node.removeAttribute('style');
48265             }
48266         }
48267         
48268         return true; // continue doing children..
48269     }
48270 });/**
48271  * @class Roo.htmleditor.FilterWord
48272  * try and clean up all the mess that Word generates.
48273  * 
48274  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
48275  
48276  * @constructor
48277  * Run a new Span Filter
48278  * @param {Object} config Configuration options
48279  */
48280
48281 Roo.htmleditor.FilterWord = function(cfg)
48282 {
48283     // no need to apply config.
48284     this.replaceDocBullets(cfg.node);
48285     
48286     this.replaceAname(cfg.node);
48287     // this is disabled as the removal is done by other filters;
48288    // this.walk(cfg.node);
48289     this.replaceImageTable(cfg.node);
48290     
48291 }
48292
48293 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
48294 {
48295     tag: true,
48296      
48297     
48298     /**
48299      * Clean up MS wordisms...
48300      */
48301     replaceTag : function(node)
48302     {
48303          
48304         // no idea what this does - span with text, replaceds with just text.
48305         if(
48306                 node.nodeName == 'SPAN' &&
48307                 !node.hasAttributes() &&
48308                 node.childNodes.length == 1 &&
48309                 node.firstChild.nodeName == "#text"  
48310         ) {
48311             var textNode = node.firstChild;
48312             node.removeChild(textNode);
48313             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
48314                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
48315             }
48316             node.parentNode.insertBefore(textNode, node);
48317             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
48318                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
48319             }
48320             
48321             node.parentNode.removeChild(node);
48322             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
48323         }
48324         
48325    
48326         
48327         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
48328             node.parentNode.removeChild(node);
48329             return false; // dont do chidlren
48330         }
48331         //Roo.log(node.tagName);
48332         // remove - but keep children..
48333         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
48334             //Roo.log('-- removed');
48335             while (node.childNodes.length) {
48336                 var cn = node.childNodes[0];
48337                 node.removeChild(cn);
48338                 node.parentNode.insertBefore(cn, node);
48339                 // move node to parent - and clean it..
48340                 if (cn.nodeType == 1) {
48341                     this.replaceTag(cn);
48342                 }
48343                 
48344             }
48345             node.parentNode.removeChild(node);
48346             /// no need to iterate chidlren = it's got none..
48347             //this.iterateChildren(node, this.cleanWord);
48348             return false; // no need to iterate children.
48349         }
48350         // clean styles
48351         if (node.className.length) {
48352             
48353             var cn = node.className.split(/\W+/);
48354             var cna = [];
48355             Roo.each(cn, function(cls) {
48356                 if (cls.match(/Mso[a-zA-Z]+/)) {
48357                     return;
48358                 }
48359                 cna.push(cls);
48360             });
48361             node.className = cna.length ? cna.join(' ') : '';
48362             if (!cna.length) {
48363                 node.removeAttribute("class");
48364             }
48365         }
48366         
48367         if (node.hasAttribute("lang")) {
48368             node.removeAttribute("lang");
48369         }
48370         
48371         if (node.hasAttribute("style")) {
48372             
48373             var styles = node.getAttribute("style").split(";");
48374             var nstyle = [];
48375             Roo.each(styles, function(s) {
48376                 if (!s.match(/:/)) {
48377                     return;
48378                 }
48379                 var kv = s.split(":");
48380                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
48381                     return;
48382                 }
48383                 // what ever is left... we allow.
48384                 nstyle.push(s);
48385             });
48386             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
48387             if (!nstyle.length) {
48388                 node.removeAttribute('style');
48389             }
48390         }
48391         return true; // do children
48392         
48393         
48394         
48395     },
48396     
48397     styleToObject: function(node)
48398     {
48399         var styles = (node.getAttribute("style") || '').split(";");
48400         var ret = {};
48401         Roo.each(styles, function(s) {
48402             if (!s.match(/:/)) {
48403                 return;
48404             }
48405             var kv = s.split(":");
48406              
48407             // what ever is left... we allow.
48408             ret[kv[0].trim()] = kv[1];
48409         });
48410         return ret;
48411     },
48412     
48413     
48414     replaceAname : function (doc)
48415     {
48416         // replace all the a/name without..
48417         var aa = Array.from(doc.getElementsByTagName('a'));
48418         for (var i = 0; i  < aa.length; i++) {
48419             var a = aa[i];
48420             if (a.hasAttribute("name")) {
48421                 a.removeAttribute("name");
48422             }
48423             if (a.hasAttribute("href")) {
48424                 continue;
48425             }
48426             // reparent children.
48427             this.removeNodeKeepChildren(a);
48428             
48429         }
48430         
48431         
48432         
48433     },
48434
48435     
48436     
48437     replaceDocBullets : function(doc)
48438     {
48439         // this is a bit odd - but it appears some indents use ql-indent-1
48440          //Roo.log(doc.innerHTML);
48441         
48442         var listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpFirst'));
48443         for( var i = 0; i < listpara.length; i ++) {
48444             listpara[i].className = "MsoListParagraph";
48445         }
48446         
48447         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpMiddle'));
48448         for( var i = 0; i < listpara.length; i ++) {
48449             listpara[i].className = "MsoListParagraph";
48450         }
48451         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpLast'));
48452         for( var i = 0; i < listpara.length; i ++) {
48453             listpara[i].className = "MsoListParagraph";
48454         }
48455         listpara =  Array.from(doc.getElementsByClassName('ql-indent-1'));
48456         for( var i = 0; i < listpara.length; i ++) {
48457             listpara[i].className = "MsoListParagraph";
48458         }
48459         
48460         // this is a bit hacky - we had one word document where h2 had a miso-list attribute.
48461         var htwo =  Array.from(doc.getElementsByTagName('h2'));
48462         for( var i = 0; i < htwo.length; i ++) {
48463             if (htwo[i].hasAttribute('style') && htwo[i].getAttribute('style').match(/mso-list:/)) {
48464                 htwo[i].className = "MsoListParagraph";
48465             }
48466         }
48467         listpara =  Array.from(doc.getElementsByClassName('MsoNormal'));
48468         for( var i = 0; i < listpara.length; i ++) {
48469             if (listpara[i].hasAttribute('style') && listpara[i].getAttribute('style').match(/mso-list:/)) {
48470                 listpara[i].className = "MsoListParagraph";
48471             } else {
48472                 listpara[i].className = "MsoNormalx";
48473             }
48474         }
48475        
48476         listpara = doc.getElementsByClassName('MsoListParagraph');
48477         // Roo.log(doc.innerHTML);
48478         
48479         
48480         
48481         while(listpara.length) {
48482             
48483             this.replaceDocBullet(listpara.item(0));
48484         }
48485       
48486     },
48487     
48488      
48489     
48490     replaceDocBullet : function(p)
48491     {
48492         // gather all the siblings.
48493         var ns = p,
48494             parent = p.parentNode,
48495             doc = parent.ownerDocument,
48496             items = [];
48497          
48498         //Roo.log("Parsing: " + p.innerText)    ;
48499         var listtype = 'ul';   
48500         while (ns) {
48501             if (ns.nodeType != 1) {
48502                 ns = ns.nextSibling;
48503                 continue;
48504             }
48505             if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
48506                 //Roo.log("Missing para r q1indent - got:" + ns.className);
48507                 break;
48508             }
48509             var spans = ns.getElementsByTagName('span');
48510             
48511             if (ns.hasAttribute('style') && ns.getAttribute('style').match(/mso-list/)) {
48512                 items.push(ns);
48513                 ns = ns.nextSibling;
48514                 has_list = true;
48515                 if (!spans.length) {
48516                     continue;
48517                 }
48518                 var ff = '';
48519                 var se = spans[0];
48520                 for (var i = 0; i < spans.length;i++) {
48521                     se = spans[i];
48522                     if (se.hasAttribute('style')  && se.hasAttribute('style') && se.style.fontFamily != '') {
48523                         ff = se.style.fontFamily;
48524                         break;
48525                     }
48526                 }
48527                  
48528                     
48529                 //Roo.log("got font family: " + ff);
48530                 if (typeof(ff) != 'undefined' && !ff.match(/(Symbol|Wingdings)/) && "·o".indexOf(se.innerText.trim()) < 0) {
48531                     listtype = 'ol';
48532                 }
48533                 
48534                 continue;
48535             }
48536             //Roo.log("no mso-list?");
48537             
48538             var spans = ns.getElementsByTagName('span');
48539             if (!spans.length) {
48540                 break;
48541             }
48542             var has_list  = false;
48543             for(var i = 0; i < spans.length; i++) {
48544                 if (spans[i].hasAttribute('style') && spans[i].getAttribute('style').match(/mso-list/)) {
48545                     has_list = true;
48546                     break;
48547                 }
48548             }
48549             if (!has_list) {
48550                 break;
48551             }
48552             items.push(ns);
48553             ns = ns.nextSibling;
48554             
48555             
48556         }
48557         if (!items.length) {
48558             ns.className = "";
48559             return;
48560         }
48561         
48562         var ul = parent.ownerDocument.createElement(listtype); // what about number lists...
48563         parent.insertBefore(ul, p);
48564         var lvl = 0;
48565         var stack = [ ul ];
48566         var last_li = false;
48567         
48568         var margin_to_depth = {};
48569         max_margins = -1;
48570         
48571         items.forEach(function(n, ipos) {
48572             //Roo.log("got innertHMLT=" + n.innerHTML);
48573             
48574             var spans = n.getElementsByTagName('span');
48575             if (!spans.length) {
48576                 //Roo.log("No spans found");
48577                  
48578                 parent.removeChild(n);
48579                 
48580                 
48581                 return; // skip it...
48582             }
48583            
48584                 
48585             var num = 1;
48586             var style = {};
48587             for(var i = 0; i < spans.length; i++) {
48588             
48589                 style = this.styleToObject(spans[i]);
48590                 if (typeof(style['mso-list']) == 'undefined') {
48591                     continue;
48592                 }
48593                 if (listtype == 'ol') {
48594                    num = spans[i].innerText.replace(/[^0-9]+]/g,'')  * 1;
48595                 }
48596                 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
48597                 break;
48598             }
48599             //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
48600             style = this.styleToObject(n); // mo-list is from the parent node.
48601             if (typeof(style['mso-list']) == 'undefined') {
48602                 //Roo.log("parent is missing level");
48603                   
48604                 parent.removeChild(n);
48605                  
48606                 return;
48607             }
48608             
48609             var margin = style['margin-left'];
48610             if (typeof(margin_to_depth[margin]) == 'undefined') {
48611                 max_margins++;
48612                 margin_to_depth[margin] = max_margins;
48613             }
48614             nlvl = margin_to_depth[margin] ;
48615              
48616             if (nlvl > lvl) {
48617                 //new indent
48618                 var nul = doc.createElement(listtype); // what about number lists...
48619                 if (!last_li) {
48620                     last_li = doc.createElement('li');
48621                     stack[lvl].appendChild(last_li);
48622                 }
48623                 last_li.appendChild(nul);
48624                 stack[nlvl] = nul;
48625                 
48626             }
48627             lvl = nlvl;
48628             
48629             // not starting at 1..
48630             if (!stack[nlvl].hasAttribute("start") && listtype == "ol") {
48631                 stack[nlvl].setAttribute("start", num);
48632             }
48633             
48634             var nli = stack[nlvl].appendChild(doc.createElement('li'));
48635             last_li = nli;
48636             nli.innerHTML = n.innerHTML;
48637             //Roo.log("innerHTML = " + n.innerHTML);
48638             parent.removeChild(n);
48639             
48640              
48641              
48642             
48643         },this);
48644         
48645         
48646         
48647         
48648     },
48649     
48650     replaceImageTable : function(doc)
48651     {
48652          /*
48653           <table cellpadding=0 cellspacing=0 align=left>
48654   <tr>
48655    <td width=423 height=0></td>
48656   </tr>
48657   <tr>
48658    <td></td>
48659    <td><img width=601 height=401
48660    src="file:///C:/Users/Alan/AppData/Local/Temp/msohtmlclip1/01/clip_image002.jpg"
48661    v:shapes="Picture_x0020_2"></td>
48662   </tr>
48663  </table>
48664  */
48665         var imgs = Array.from(doc.getElementsByTagName('img'));
48666         Roo.each(imgs, function(img) {
48667             var td = img.parentNode;
48668             if (td.nodeName !=  'TD') {
48669                 return;
48670             }
48671             var tr = td.parentNode;
48672             if (tr.nodeName !=  'TR') {
48673                 return;
48674             }
48675             var tbody = tr.parentNode;
48676             if (tbody.nodeName !=  'TBODY') {
48677                 return;
48678             }
48679             var table = tbody.parentNode;
48680             if (table.nodeName !=  'TABLE') {
48681                 return;
48682             }
48683             // first row..
48684             
48685             if (table.getElementsByTagName('tr').length != 2) {
48686                 return;
48687             }
48688             if (table.getElementsByTagName('td').length != 3) {
48689                 return;
48690             }
48691             if (table.innerText.trim() != '') {
48692                 return;
48693             }
48694             var p = table.parentNode;
48695             img.parentNode.removeChild(img);
48696             p.insertBefore(img, table);
48697             p.removeChild(table);
48698             
48699             
48700             
48701         });
48702         
48703       
48704     }
48705     
48706 });
48707 /**
48708  * @class Roo.htmleditor.FilterStyleToTag
48709  * part of the word stuff... - certain 'styles' should be converted to tags.
48710  * eg.
48711  *   font-weight: bold -> bold
48712  *   ?? super / subscrit etc..
48713  * 
48714  * @constructor
48715 * Run a new style to tag filter.
48716 * @param {Object} config Configuration options
48717  */
48718 Roo.htmleditor.FilterStyleToTag = function(cfg)
48719 {
48720     
48721     this.tags = {
48722         B  : [ 'fontWeight' , 'bold', 'font-weight'],
48723         I :  [ 'fontStyle' , 'italic', 'font-style'],
48724         //pre :  [ 'font-style' , 'italic'],
48725         // h1.. h6 ?? font-size?
48726         SUP : [ 'verticalAlign' , 'super', 'vertical-align'],
48727         SUB : [ 'verticalAlign' , 'sub', 'vertical-align']
48728         
48729         
48730     };
48731     
48732     Roo.apply(this, cfg);
48733      
48734     
48735     this.walk(cfg.node);
48736     
48737     
48738     
48739 }
48740
48741
48742 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
48743 {
48744     tag: true, // all tags
48745     
48746     tags : false,
48747     
48748     
48749     replaceTag : function(node)
48750     {
48751         
48752         
48753         if (node.getAttribute("style") === null) {
48754             return true;
48755         }
48756         var inject = [];
48757         for (var k in this.tags) {
48758             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
48759                 inject.push(k);
48760                 node.style.removeProperty(this.tags[k][2]);
48761             }
48762         }
48763         if (!inject.length) {
48764             return true; 
48765         }
48766         var cn = Array.from(node.childNodes);
48767         var nn = node;
48768         Roo.each(inject, function(t) {
48769             var nc = node.ownerDocument.createElement(t);
48770             nn.appendChild(nc);
48771             nn = nc;
48772         });
48773         for(var i = 0;i < cn.length;i++) {
48774             node.removeChild(cn[i]);
48775             nn.appendChild(cn[i]);
48776         }
48777         return true /// iterate thru
48778     }
48779     
48780 })/**
48781  * @class Roo.htmleditor.FilterLongBr
48782  * BR/BR/BR - keep a maximum of 2...
48783  * @constructor
48784  * Run a new Long BR Filter
48785  * @param {Object} config Configuration options
48786  */
48787
48788 Roo.htmleditor.FilterLongBr = function(cfg)
48789 {
48790     // no need to apply config.
48791     this.searchTag(cfg.node);
48792 }
48793
48794 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
48795 {
48796     
48797      
48798     tag : 'BR',
48799     
48800      
48801     replaceTag : function(node)
48802     {
48803         
48804         var ps = node.nextSibling;
48805         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
48806             ps = ps.nextSibling;
48807         }
48808         
48809         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
48810             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
48811             return false;
48812         }
48813         
48814         if (!ps || ps.nodeType != 1) {
48815             return false;
48816         }
48817         
48818         if (!ps || ps.tagName != 'BR') {
48819            
48820             return false;
48821         }
48822         
48823         
48824         
48825         if (!node.previousSibling) {
48826             return false;
48827         }
48828         var ps = node.previousSibling;
48829         
48830         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
48831             ps = ps.previousSibling;
48832         }
48833         if (!ps || ps.nodeType != 1) {
48834             return false;
48835         }
48836         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
48837         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
48838             return false;
48839         }
48840         
48841         node.parentNode.removeChild(node); // remove me...
48842         
48843         return false; // no need to do children
48844
48845     }
48846     
48847 }); 
48848
48849 /**
48850  * @class Roo.htmleditor.FilterBlock
48851  * removes id / data-block and contenteditable that are associated with blocks
48852  * usage should be done on a cloned copy of the dom
48853  * @constructor
48854 * Run a new Attribute Filter { node : xxxx }}
48855 * @param {Object} config Configuration options
48856  */
48857 Roo.htmleditor.FilterBlock = function(cfg)
48858 {
48859     Roo.apply(this, cfg);
48860     var qa = cfg.node.querySelectorAll;
48861     this.removeAttributes('data-block');
48862     this.removeAttributes('contenteditable');
48863     this.removeAttributes('id');
48864     
48865 }
48866
48867 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
48868 {
48869     node: true, // all tags
48870      
48871      
48872     removeAttributes : function(attr)
48873     {
48874         var ar = this.node.querySelectorAll('*[' + attr + ']');
48875         for (var i =0;i<ar.length;i++) {
48876             ar[i].removeAttribute(attr);
48877         }
48878     }
48879         
48880         
48881         
48882     
48883 });
48884 /***
48885  * This is based loosely on tinymce 
48886  * @class Roo.htmleditor.TidySerializer
48887  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
48888  * @constructor
48889  * @method Serializer
48890  * @param {Object} settings Name/value settings object.
48891  */
48892
48893
48894 Roo.htmleditor.TidySerializer = function(settings)
48895 {
48896     Roo.apply(this, settings);
48897     
48898     this.writer = new Roo.htmleditor.TidyWriter(settings);
48899     
48900     
48901
48902 };
48903 Roo.htmleditor.TidySerializer.prototype = {
48904     
48905     /**
48906      * @param {boolean} inner do the inner of the node.
48907      */
48908     inner : false,
48909     
48910     writer : false,
48911     
48912     /**
48913     * Serializes the specified node into a string.
48914     *
48915     * @example
48916     * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
48917     * @method serialize
48918     * @param {DomElement} node Node instance to serialize.
48919     * @return {String} String with HTML based on DOM tree.
48920     */
48921     serialize : function(node) {
48922         
48923         // = settings.validate;
48924         var writer = this.writer;
48925         var self  = this;
48926         this.handlers = {
48927             // #text
48928             3: function(node) {
48929                 
48930                 writer.text(node.nodeValue, node);
48931             },
48932             // #comment
48933             8: function(node) {
48934                 writer.comment(node.nodeValue);
48935             },
48936             // Processing instruction
48937             7: function(node) {
48938                 writer.pi(node.name, node.nodeValue);
48939             },
48940             // Doctype
48941             10: function(node) {
48942                 writer.doctype(node.nodeValue);
48943             },
48944             // CDATA
48945             4: function(node) {
48946                 writer.cdata(node.nodeValue);
48947             },
48948             // Document fragment
48949             11: function(node) {
48950                 node = node.firstChild;
48951                 if (!node) {
48952                     return;
48953                 }
48954                 while(node) {
48955                     self.walk(node);
48956                     node = node.nextSibling
48957                 }
48958             }
48959         };
48960         writer.reset();
48961         1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
48962         return writer.getContent();
48963     },
48964
48965     walk: function(node)
48966     {
48967         var attrName, attrValue, sortedAttrs, i, l, elementRule,
48968             handler = this.handlers[node.nodeType];
48969             
48970         if (handler) {
48971             handler(node);
48972             return;
48973         }
48974     
48975         var name = node.nodeName;
48976         var isEmpty = node.childNodes.length < 1;
48977       
48978         var writer = this.writer;
48979         var attrs = node.attributes;
48980         // Sort attributes
48981         
48982         writer.start(node.nodeName, attrs, isEmpty, node);
48983         if (isEmpty) {
48984             return;
48985         }
48986         node = node.firstChild;
48987         if (!node) {
48988             writer.end(name);
48989             return;
48990         }
48991         while (node) {
48992             this.walk(node);
48993             node = node.nextSibling;
48994         }
48995         writer.end(name);
48996         
48997     
48998     }
48999     // Serialize element and treat all non elements as fragments
49000    
49001 }; 
49002
49003 /***
49004  * This is based loosely on tinymce 
49005  * @class Roo.htmleditor.TidyWriter
49006  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
49007  *
49008  * Known issues?
49009  * - not tested much with 'PRE' formated elements.
49010  * 
49011  *
49012  *
49013  */
49014
49015 Roo.htmleditor.TidyWriter = function(settings)
49016 {
49017     
49018     // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
49019     Roo.apply(this, settings);
49020     this.html = [];
49021     this.state = [];
49022      
49023     this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
49024   
49025 }
49026 Roo.htmleditor.TidyWriter.prototype = {
49027
49028  
49029     state : false,
49030     
49031     indent :  '  ',
49032     
49033     // part of state...
49034     indentstr : '',
49035     in_pre: false,
49036     in_inline : false,
49037     last_inline : false,
49038     encode : false,
49039      
49040     
49041             /**
49042     * Writes the a start element such as <p id="a">.
49043     *
49044     * @method start
49045     * @param {String} name Name of the element.
49046     * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
49047     * @param {Boolean} empty Optional empty state if the tag should end like <br />.
49048     */
49049     start: function(name, attrs, empty, node)
49050     {
49051         var i, l, attr, value;
49052         
49053         // there are some situations where adding line break && indentation will not work. will not work.
49054         // <span / b / i ... formating?
49055         
49056         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
49057         var in_pre    = this.in_pre    || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
49058         
49059         var is_short   = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
49060         
49061         var add_lb = name == 'BR' ? false : in_inline;
49062         
49063         if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
49064             i_inline = false;
49065         }
49066
49067         var indentstr =  this.indentstr;
49068         
49069         // e_inline = elements that can be inline, but still allow \n before and after?
49070         // only 'BR' ??? any others?
49071         
49072         // ADD LINE BEFORE tage
49073         if (!this.in_pre) {
49074             if (in_inline) {
49075                 //code
49076                 if (name == 'BR') {
49077                     this.addLine();
49078                 } else if (this.lastElementEndsWS()) {
49079                     this.addLine();
49080                 } else{
49081                     // otherwise - no new line. (and dont indent.)
49082                     indentstr = '';
49083                 }
49084                 
49085             } else {
49086                 this.addLine();
49087             }
49088         } else {
49089             indentstr = '';
49090         }
49091         
49092         this.html.push(indentstr + '<', name.toLowerCase());
49093         
49094         if (attrs) {
49095             for (i = 0, l = attrs.length; i < l; i++) {
49096                 attr = attrs[i];
49097                 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
49098             }
49099         }
49100      
49101         if (empty) {
49102             if (is_short) {
49103                 this.html[this.html.length] = '/>';
49104             } else {
49105                 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
49106             }
49107             var e_inline = name == 'BR' ? false : this.in_inline;
49108             
49109             if (!e_inline && !this.in_pre) {
49110                 this.addLine();
49111             }
49112             return;
49113         
49114         }
49115         // not empty..
49116         this.html[this.html.length] = '>';
49117         
49118         // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
49119         /*
49120         if (!in_inline && !in_pre) {
49121             var cn = node.firstChild;
49122             while(cn) {
49123                 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
49124                     in_inline = true
49125                     break;
49126                 }
49127                 cn = cn.nextSibling;
49128             }
49129              
49130         }
49131         */
49132         
49133         
49134         this.pushState({
49135             indentstr : in_pre   ? '' : (this.indentstr + this.indent),
49136             in_pre : in_pre,
49137             in_inline :  in_inline
49138         });
49139         // add a line after if we are not in a
49140         
49141         if (!in_inline && !in_pre) {
49142             this.addLine();
49143         }
49144         
49145             
49146          
49147         
49148     },
49149     
49150     lastElementEndsWS : function()
49151     {
49152         var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
49153         if (value === false) {
49154             return true;
49155         }
49156         return value.match(/\s+$/);
49157         
49158     },
49159     
49160     /**
49161      * Writes the a end element such as </p>.
49162      *
49163      * @method end
49164      * @param {String} name Name of the element.
49165      */
49166     end: function(name) {
49167         var value;
49168         this.popState();
49169         var indentstr = '';
49170         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
49171         
49172         if (!this.in_pre && !in_inline) {
49173             this.addLine();
49174             indentstr  = this.indentstr;
49175         }
49176         this.html.push(indentstr + '</', name.toLowerCase(), '>');
49177         this.last_inline = in_inline;
49178         
49179         // pop the indent state..
49180     },
49181     /**
49182      * Writes a text node.
49183      *
49184      * In pre - we should not mess with the contents.
49185      * 
49186      *
49187      * @method text
49188      * @param {String} text String to write out.
49189      * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
49190      */
49191     text: function(in_text, node)
49192     {
49193         // if not in whitespace critical
49194         if (in_text.length < 1) {
49195             return;
49196         }
49197         var text = new XMLSerializer().serializeToString(document.createTextNode(in_text)); // escape it properly?
49198         
49199         if (this.in_pre) {
49200             this.html[this.html.length] =  text;
49201             return;   
49202         }
49203         
49204         if (this.in_inline) {
49205             text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
49206             if (text != ' ') {
49207                 text = text.replace(/\s+/,' ');  // all white space to single white space
49208                 
49209                     
49210                 // if next tag is '<BR>', then we can trim right..
49211                 if (node.nextSibling &&
49212                     node.nextSibling.nodeType == 1 &&
49213                     node.nextSibling.nodeName == 'BR' )
49214                 {
49215                     text = text.replace(/\s+$/g,'');
49216                 }
49217                 // if previous tag was a BR, we can also trim..
49218                 if (node.previousSibling &&
49219                     node.previousSibling.nodeType == 1 &&
49220                     node.previousSibling.nodeName == 'BR' )
49221                 {
49222                     text = this.indentstr +  text.replace(/^\s+/g,'');
49223                 }
49224                 if (text.match(/\n/)) {
49225                     text = text.replace(
49226                         /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
49227                     );
49228                     // remoeve the last whitespace / line break.
49229                     text = text.replace(/\n\s+$/,'');
49230                 }
49231                 // repace long lines
49232                 
49233             }
49234              
49235             this.html[this.html.length] =  text;
49236             return;   
49237         }
49238         // see if previous element was a inline element.
49239         var indentstr = this.indentstr;
49240    
49241         text = text.replace(/\s+/g," "); // all whitespace into single white space.
49242         
49243         // should trim left?
49244         if (node.previousSibling &&
49245             node.previousSibling.nodeType == 1 &&
49246             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
49247         {
49248             indentstr = '';
49249             
49250         } else {
49251             this.addLine();
49252             text = text.replace(/^\s+/,''); // trim left
49253           
49254         }
49255         // should trim right?
49256         if (node.nextSibling &&
49257             node.nextSibling.nodeType == 1 &&
49258             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
49259         {
49260           // noop
49261             
49262         }  else {
49263             text = text.replace(/\s+$/,''); // trim right
49264         }
49265          
49266               
49267         
49268         
49269         
49270         if (text.length < 1) {
49271             return;
49272         }
49273         if (!text.match(/\n/)) {
49274             this.html.push(indentstr + text);
49275             return;
49276         }
49277         
49278         text = this.indentstr + text.replace(
49279             /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
49280         );
49281         // remoeve the last whitespace / line break.
49282         text = text.replace(/\s+$/,''); 
49283         
49284         this.html.push(text);
49285         
49286         // split and indent..
49287         
49288         
49289     },
49290     /**
49291      * Writes a cdata node such as <![CDATA[data]]>.
49292      *
49293      * @method cdata
49294      * @param {String} text String to write out inside the cdata.
49295      */
49296     cdata: function(text) {
49297         this.html.push('<![CDATA[', text, ']]>');
49298     },
49299     /**
49300     * Writes a comment node such as <!-- Comment -->.
49301     *
49302     * @method cdata
49303     * @param {String} text String to write out inside the comment.
49304     */
49305    comment: function(text) {
49306        this.html.push('<!--', text, '-->');
49307    },
49308     /**
49309      * Writes a PI node such as <?xml attr="value" ?>.
49310      *
49311      * @method pi
49312      * @param {String} name Name of the pi.
49313      * @param {String} text String to write out inside the pi.
49314      */
49315     pi: function(name, text) {
49316         text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
49317         this.indent != '' && this.html.push('\n');
49318     },
49319     /**
49320      * Writes a doctype node such as <!DOCTYPE data>.
49321      *
49322      * @method doctype
49323      * @param {String} text String to write out inside the doctype.
49324      */
49325     doctype: function(text) {
49326         this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
49327     },
49328     /**
49329      * Resets the internal buffer if one wants to reuse the writer.
49330      *
49331      * @method reset
49332      */
49333     reset: function() {
49334         this.html.length = 0;
49335         this.state = [];
49336         this.pushState({
49337             indentstr : '',
49338             in_pre : false, 
49339             in_inline : false
49340         })
49341     },
49342     /**
49343      * Returns the contents that got serialized.
49344      *
49345      * @method getContent
49346      * @return {String} HTML contents that got written down.
49347      */
49348     getContent: function() {
49349         return this.html.join('').replace(/\n$/, '');
49350     },
49351     
49352     pushState : function(cfg)
49353     {
49354         this.state.push(cfg);
49355         Roo.apply(this, cfg);
49356     },
49357     
49358     popState : function()
49359     {
49360         if (this.state.length < 1) {
49361             return; // nothing to push
49362         }
49363         var cfg = {
49364             in_pre: false,
49365             indentstr : ''
49366         };
49367         this.state.pop();
49368         if (this.state.length > 0) {
49369             cfg = this.state[this.state.length-1]; 
49370         }
49371         Roo.apply(this, cfg);
49372     },
49373     
49374     addLine: function()
49375     {
49376         if (this.html.length < 1) {
49377             return;
49378         }
49379         
49380         
49381         var value = this.html[this.html.length - 1];
49382         if (value.length > 0 && '\n' !== value) {
49383             this.html.push('\n');
49384         }
49385     }
49386     
49387     
49388 //'pre script noscript style textarea video audio iframe object code'
49389 // shortended... 'area base basefont br col frame hr img input isindex link  meta param embed source wbr track');
49390 // inline 
49391 };
49392
49393 Roo.htmleditor.TidyWriter.inline_elements = [
49394         'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
49395         'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
49396 ];
49397 Roo.htmleditor.TidyWriter.shortend_elements = [
49398     'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
49399     'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
49400 ];
49401
49402 Roo.htmleditor.TidyWriter.whitespace_elements = [
49403     'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
49404 ];/***
49405  * This is based loosely on tinymce 
49406  * @class Roo.htmleditor.TidyEntities
49407  * @static
49408  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
49409  *
49410  * Not 100% sure this is actually used or needed.
49411  */
49412
49413 Roo.htmleditor.TidyEntities = {
49414     
49415     /**
49416      * initialize data..
49417      */
49418     init : function (){
49419      
49420         this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
49421        
49422     },
49423
49424
49425     buildEntitiesLookup: function(items, radix) {
49426         var i, chr, entity, lookup = {};
49427         if (!items) {
49428             return {};
49429         }
49430         items = typeof(items) == 'string' ? items.split(',') : items;
49431         radix = radix || 10;
49432         // Build entities lookup table
49433         for (i = 0; i < items.length; i += 2) {
49434             chr = String.fromCharCode(parseInt(items[i], radix));
49435             // Only add non base entities
49436             if (!this.baseEntities[chr]) {
49437                 entity = '&' + items[i + 1] + ';';
49438                 lookup[chr] = entity;
49439                 lookup[entity] = chr;
49440             }
49441         }
49442         return lookup;
49443         
49444     },
49445     
49446     asciiMap : {
49447             128: '€',
49448             130: '‚',
49449             131: 'ƒ',
49450             132: '„',
49451             133: '…',
49452             134: '†',
49453             135: '‡',
49454             136: 'ˆ',
49455             137: '‰',
49456             138: 'Š',
49457             139: '‹',
49458             140: 'Œ',
49459             142: 'Ž',
49460             145: '‘',
49461             146: '’',
49462             147: '“',
49463             148: '”',
49464             149: '•',
49465             150: '–',
49466             151: '—',
49467             152: '˜',
49468             153: '™',
49469             154: 'š',
49470             155: '›',
49471             156: 'œ',
49472             158: 'ž',
49473             159: 'Ÿ'
49474     },
49475     // Raw entities
49476     baseEntities : {
49477         '"': '&quot;',
49478         // Needs to be escaped since the YUI compressor would otherwise break the code
49479         '\'': '&#39;',
49480         '<': '&lt;',
49481         '>': '&gt;',
49482         '&': '&amp;',
49483         '`': '&#96;'
49484     },
49485     // Reverse lookup table for raw entities
49486     reverseEntities : {
49487         '&lt;': '<',
49488         '&gt;': '>',
49489         '&amp;': '&',
49490         '&quot;': '"',
49491         '&apos;': '\''
49492     },
49493     
49494     attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
49495     textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
49496     rawCharsRegExp : /[<>&\"\']/g,
49497     entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
49498     namedEntities  : false,
49499     namedEntitiesData : [ 
49500         '50',
49501         'nbsp',
49502         '51',
49503         'iexcl',
49504         '52',
49505         'cent',
49506         '53',
49507         'pound',
49508         '54',
49509         'curren',
49510         '55',
49511         'yen',
49512         '56',
49513         'brvbar',
49514         '57',
49515         'sect',
49516         '58',
49517         'uml',
49518         '59',
49519         'copy',
49520         '5a',
49521         'ordf',
49522         '5b',
49523         'laquo',
49524         '5c',
49525         'not',
49526         '5d',
49527         'shy',
49528         '5e',
49529         'reg',
49530         '5f',
49531         'macr',
49532         '5g',
49533         'deg',
49534         '5h',
49535         'plusmn',
49536         '5i',
49537         'sup2',
49538         '5j',
49539         'sup3',
49540         '5k',
49541         'acute',
49542         '5l',
49543         'micro',
49544         '5m',
49545         'para',
49546         '5n',
49547         'middot',
49548         '5o',
49549         'cedil',
49550         '5p',
49551         'sup1',
49552         '5q',
49553         'ordm',
49554         '5r',
49555         'raquo',
49556         '5s',
49557         'frac14',
49558         '5t',
49559         'frac12',
49560         '5u',
49561         'frac34',
49562         '5v',
49563         'iquest',
49564         '60',
49565         'Agrave',
49566         '61',
49567         'Aacute',
49568         '62',
49569         'Acirc',
49570         '63',
49571         'Atilde',
49572         '64',
49573         'Auml',
49574         '65',
49575         'Aring',
49576         '66',
49577         'AElig',
49578         '67',
49579         'Ccedil',
49580         '68',
49581         'Egrave',
49582         '69',
49583         'Eacute',
49584         '6a',
49585         'Ecirc',
49586         '6b',
49587         'Euml',
49588         '6c',
49589         'Igrave',
49590         '6d',
49591         'Iacute',
49592         '6e',
49593         'Icirc',
49594         '6f',
49595         'Iuml',
49596         '6g',
49597         'ETH',
49598         '6h',
49599         'Ntilde',
49600         '6i',
49601         'Ograve',
49602         '6j',
49603         'Oacute',
49604         '6k',
49605         'Ocirc',
49606         '6l',
49607         'Otilde',
49608         '6m',
49609         'Ouml',
49610         '6n',
49611         'times',
49612         '6o',
49613         'Oslash',
49614         '6p',
49615         'Ugrave',
49616         '6q',
49617         'Uacute',
49618         '6r',
49619         'Ucirc',
49620         '6s',
49621         'Uuml',
49622         '6t',
49623         'Yacute',
49624         '6u',
49625         'THORN',
49626         '6v',
49627         'szlig',
49628         '70',
49629         'agrave',
49630         '71',
49631         'aacute',
49632         '72',
49633         'acirc',
49634         '73',
49635         'atilde',
49636         '74',
49637         'auml',
49638         '75',
49639         'aring',
49640         '76',
49641         'aelig',
49642         '77',
49643         'ccedil',
49644         '78',
49645         'egrave',
49646         '79',
49647         'eacute',
49648         '7a',
49649         'ecirc',
49650         '7b',
49651         'euml',
49652         '7c',
49653         'igrave',
49654         '7d',
49655         'iacute',
49656         '7e',
49657         'icirc',
49658         '7f',
49659         'iuml',
49660         '7g',
49661         'eth',
49662         '7h',
49663         'ntilde',
49664         '7i',
49665         'ograve',
49666         '7j',
49667         'oacute',
49668         '7k',
49669         'ocirc',
49670         '7l',
49671         'otilde',
49672         '7m',
49673         'ouml',
49674         '7n',
49675         'divide',
49676         '7o',
49677         'oslash',
49678         '7p',
49679         'ugrave',
49680         '7q',
49681         'uacute',
49682         '7r',
49683         'ucirc',
49684         '7s',
49685         'uuml',
49686         '7t',
49687         'yacute',
49688         '7u',
49689         'thorn',
49690         '7v',
49691         'yuml',
49692         'ci',
49693         'fnof',
49694         'sh',
49695         'Alpha',
49696         'si',
49697         'Beta',
49698         'sj',
49699         'Gamma',
49700         'sk',
49701         'Delta',
49702         'sl',
49703         'Epsilon',
49704         'sm',
49705         'Zeta',
49706         'sn',
49707         'Eta',
49708         'so',
49709         'Theta',
49710         'sp',
49711         'Iota',
49712         'sq',
49713         'Kappa',
49714         'sr',
49715         'Lambda',
49716         'ss',
49717         'Mu',
49718         'st',
49719         'Nu',
49720         'su',
49721         'Xi',
49722         'sv',
49723         'Omicron',
49724         't0',
49725         'Pi',
49726         't1',
49727         'Rho',
49728         't3',
49729         'Sigma',
49730         't4',
49731         'Tau',
49732         't5',
49733         'Upsilon',
49734         't6',
49735         'Phi',
49736         't7',
49737         'Chi',
49738         't8',
49739         'Psi',
49740         't9',
49741         'Omega',
49742         'th',
49743         'alpha',
49744         'ti',
49745         'beta',
49746         'tj',
49747         'gamma',
49748         'tk',
49749         'delta',
49750         'tl',
49751         'epsilon',
49752         'tm',
49753         'zeta',
49754         'tn',
49755         'eta',
49756         'to',
49757         'theta',
49758         'tp',
49759         'iota',
49760         'tq',
49761         'kappa',
49762         'tr',
49763         'lambda',
49764         'ts',
49765         'mu',
49766         'tt',
49767         'nu',
49768         'tu',
49769         'xi',
49770         'tv',
49771         'omicron',
49772         'u0',
49773         'pi',
49774         'u1',
49775         'rho',
49776         'u2',
49777         'sigmaf',
49778         'u3',
49779         'sigma',
49780         'u4',
49781         'tau',
49782         'u5',
49783         'upsilon',
49784         'u6',
49785         'phi',
49786         'u7',
49787         'chi',
49788         'u8',
49789         'psi',
49790         'u9',
49791         'omega',
49792         'uh',
49793         'thetasym',
49794         'ui',
49795         'upsih',
49796         'um',
49797         'piv',
49798         '812',
49799         'bull',
49800         '816',
49801         'hellip',
49802         '81i',
49803         'prime',
49804         '81j',
49805         'Prime',
49806         '81u',
49807         'oline',
49808         '824',
49809         'frasl',
49810         '88o',
49811         'weierp',
49812         '88h',
49813         'image',
49814         '88s',
49815         'real',
49816         '892',
49817         'trade',
49818         '89l',
49819         'alefsym',
49820         '8cg',
49821         'larr',
49822         '8ch',
49823         'uarr',
49824         '8ci',
49825         'rarr',
49826         '8cj',
49827         'darr',
49828         '8ck',
49829         'harr',
49830         '8dl',
49831         'crarr',
49832         '8eg',
49833         'lArr',
49834         '8eh',
49835         'uArr',
49836         '8ei',
49837         'rArr',
49838         '8ej',
49839         'dArr',
49840         '8ek',
49841         'hArr',
49842         '8g0',
49843         'forall',
49844         '8g2',
49845         'part',
49846         '8g3',
49847         'exist',
49848         '8g5',
49849         'empty',
49850         '8g7',
49851         'nabla',
49852         '8g8',
49853         'isin',
49854         '8g9',
49855         'notin',
49856         '8gb',
49857         'ni',
49858         '8gf',
49859         'prod',
49860         '8gh',
49861         'sum',
49862         '8gi',
49863         'minus',
49864         '8gn',
49865         'lowast',
49866         '8gq',
49867         'radic',
49868         '8gt',
49869         'prop',
49870         '8gu',
49871         'infin',
49872         '8h0',
49873         'ang',
49874         '8h7',
49875         'and',
49876         '8h8',
49877         'or',
49878         '8h9',
49879         'cap',
49880         '8ha',
49881         'cup',
49882         '8hb',
49883         'int',
49884         '8hk',
49885         'there4',
49886         '8hs',
49887         'sim',
49888         '8i5',
49889         'cong',
49890         '8i8',
49891         'asymp',
49892         '8j0',
49893         'ne',
49894         '8j1',
49895         'equiv',
49896         '8j4',
49897         'le',
49898         '8j5',
49899         'ge',
49900         '8k2',
49901         'sub',
49902         '8k3',
49903         'sup',
49904         '8k4',
49905         'nsub',
49906         '8k6',
49907         'sube',
49908         '8k7',
49909         'supe',
49910         '8kl',
49911         'oplus',
49912         '8kn',
49913         'otimes',
49914         '8l5',
49915         'perp',
49916         '8m5',
49917         'sdot',
49918         '8o8',
49919         'lceil',
49920         '8o9',
49921         'rceil',
49922         '8oa',
49923         'lfloor',
49924         '8ob',
49925         'rfloor',
49926         '8p9',
49927         'lang',
49928         '8pa',
49929         'rang',
49930         '9ea',
49931         'loz',
49932         '9j0',
49933         'spades',
49934         '9j3',
49935         'clubs',
49936         '9j5',
49937         'hearts',
49938         '9j6',
49939         'diams',
49940         'ai',
49941         'OElig',
49942         'aj',
49943         'oelig',
49944         'b0',
49945         'Scaron',
49946         'b1',
49947         'scaron',
49948         'bo',
49949         'Yuml',
49950         'm6',
49951         'circ',
49952         'ms',
49953         'tilde',
49954         '802',
49955         'ensp',
49956         '803',
49957         'emsp',
49958         '809',
49959         'thinsp',
49960         '80c',
49961         'zwnj',
49962         '80d',
49963         'zwj',
49964         '80e',
49965         'lrm',
49966         '80f',
49967         'rlm',
49968         '80j',
49969         'ndash',
49970         '80k',
49971         'mdash',
49972         '80o',
49973         'lsquo',
49974         '80p',
49975         'rsquo',
49976         '80q',
49977         'sbquo',
49978         '80s',
49979         'ldquo',
49980         '80t',
49981         'rdquo',
49982         '80u',
49983         'bdquo',
49984         '810',
49985         'dagger',
49986         '811',
49987         'Dagger',
49988         '81g',
49989         'permil',
49990         '81p',
49991         'lsaquo',
49992         '81q',
49993         'rsaquo',
49994         '85c',
49995         'euro'
49996     ],
49997
49998          
49999     /**
50000      * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
50001      *
50002      * @method encodeRaw
50003      * @param {String} text Text to encode.
50004      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
50005      * @return {String} Entity encoded text.
50006      */
50007     encodeRaw: function(text, attr)
50008     {
50009         var t = this;
50010         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
50011             return t.baseEntities[chr] || chr;
50012         });
50013     },
50014     /**
50015      * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
50016      * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
50017      * and is exposed as the DOMUtils.encode function.
50018      *
50019      * @method encodeAllRaw
50020      * @param {String} text Text to encode.
50021      * @return {String} Entity encoded text.
50022      */
50023     encodeAllRaw: function(text) {
50024         var t = this;
50025         return ('' + text).replace(this.rawCharsRegExp, function(chr) {
50026             return t.baseEntities[chr] || chr;
50027         });
50028     },
50029     /**
50030      * Encodes the specified string using numeric entities. The core entities will be
50031      * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
50032      *
50033      * @method encodeNumeric
50034      * @param {String} text Text to encode.
50035      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
50036      * @return {String} Entity encoded text.
50037      */
50038     encodeNumeric: function(text, attr) {
50039         var t = this;
50040         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
50041             // Multi byte sequence convert it to a single entity
50042             if (chr.length > 1) {
50043                 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
50044             }
50045             return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
50046         });
50047     },
50048     /**
50049      * Encodes the specified string using named entities. The core entities will be encoded
50050      * as named ones but all non lower ascii characters will be encoded into named entities.
50051      *
50052      * @method encodeNamed
50053      * @param {String} text Text to encode.
50054      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
50055      * @param {Object} entities Optional parameter with entities to use.
50056      * @return {String} Entity encoded text.
50057      */
50058     encodeNamed: function(text, attr, entities) {
50059         var t = this;
50060         entities = entities || this.namedEntities;
50061         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
50062             return t.baseEntities[chr] || entities[chr] || chr;
50063         });
50064     },
50065     /**
50066      * Returns an encode function based on the name(s) and it's optional entities.
50067      *
50068      * @method getEncodeFunc
50069      * @param {String} name Comma separated list of encoders for example named,numeric.
50070      * @param {String} entities Optional parameter with entities to use instead of the built in set.
50071      * @return {function} Encode function to be used.
50072      */
50073     getEncodeFunc: function(name, entities) {
50074         entities = this.buildEntitiesLookup(entities) || this.namedEntities;
50075         var t = this;
50076         function encodeNamedAndNumeric(text, attr) {
50077             return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
50078                 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
50079             });
50080         }
50081
50082         function encodeCustomNamed(text, attr) {
50083             return t.encodeNamed(text, attr, entities);
50084         }
50085         // Replace + with , to be compatible with previous TinyMCE versions
50086         name = this.makeMap(name.replace(/\+/g, ','));
50087         // Named and numeric encoder
50088         if (name.named && name.numeric) {
50089             return this.encodeNamedAndNumeric;
50090         }
50091         // Named encoder
50092         if (name.named) {
50093             // Custom names
50094             if (entities) {
50095                 return encodeCustomNamed;
50096             }
50097             return this.encodeNamed;
50098         }
50099         // Numeric
50100         if (name.numeric) {
50101             return this.encodeNumeric;
50102         }
50103         // Raw encoder
50104         return this.encodeRaw;
50105     },
50106     /**
50107      * Decodes the specified string, this will replace entities with raw UTF characters.
50108      *
50109      * @method decode
50110      * @param {String} text Text to entity decode.
50111      * @return {String} Entity decoded string.
50112      */
50113     decode: function(text)
50114     {
50115         var  t = this;
50116         return text.replace(this.entityRegExp, function(all, numeric) {
50117             if (numeric) {
50118                 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
50119                 // Support upper UTF
50120                 if (numeric > 65535) {
50121                     numeric -= 65536;
50122                     return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
50123                 }
50124                 return t.asciiMap[numeric] || String.fromCharCode(numeric);
50125             }
50126             return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
50127         });
50128     },
50129     nativeDecode : function (text) {
50130         return text;
50131     },
50132     makeMap : function (items, delim, map) {
50133                 var i;
50134                 items = items || [];
50135                 delim = delim || ',';
50136                 if (typeof items == "string") {
50137                         items = items.split(delim);
50138                 }
50139                 map = map || {};
50140                 i = items.length;
50141                 while (i--) {
50142                         map[items[i]] = {};
50143                 }
50144                 return map;
50145         }
50146 };
50147     
50148     
50149     
50150 Roo.htmleditor.TidyEntities.init();
50151 /**
50152  * @class Roo.htmleditor.KeyEnter
50153  * Handle Enter press..
50154  * @cfg {Roo.HtmlEditorCore} core the editor.
50155  * @constructor
50156  * Create a new Filter.
50157  * @param {Object} config Configuration options
50158  */
50159
50160
50161
50162
50163
50164 Roo.htmleditor.KeyEnter = function(cfg) {
50165     Roo.apply(this, cfg);
50166     // this does not actually call walk as it's really just a abstract class
50167  
50168     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
50169 }
50170
50171 //Roo.htmleditor.KeyEnter.i = 0;
50172
50173
50174 Roo.htmleditor.KeyEnter.prototype = {
50175     
50176     core : false,
50177     
50178     keypress : function(e)
50179     {
50180         if (e.charCode != 13 && e.charCode != 10) {
50181             Roo.log([e.charCode,e]);
50182             return true;
50183         }
50184         e.preventDefault();
50185         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
50186         var doc = this.core.doc;
50187           //add a new line
50188        
50189     
50190         var sel = this.core.getSelection();
50191         var range = sel.getRangeAt(0);
50192         var n = range.commonAncestorContainer;
50193         var pc = range.closest([ 'ol', 'ul']);
50194         var pli = range.closest('li');
50195         if (!pc || e.ctrlKey) {
50196             // on it list, or ctrl pressed.
50197             if (!e.ctrlKey) {
50198                 sel.insertNode('br', 'after'); 
50199             } else {
50200                 // only do this if we have ctrl key..
50201                 var br = doc.createElement('br');
50202                 br.className = 'clear';
50203                 br.setAttribute('style', 'clear: both');
50204                 sel.insertNode(br, 'after'); 
50205             }
50206             
50207          
50208             this.core.undoManager.addEvent();
50209             this.core.fireEditorEvent(e);
50210             return false;
50211         }
50212         
50213         // deal with <li> insetion
50214         if (pli.innerText.trim() == '' &&
50215             pli.previousSibling &&
50216             pli.previousSibling.nodeName == 'LI' &&
50217             pli.previousSibling.innerText.trim() ==  '') {
50218             pli.parentNode.removeChild(pli.previousSibling);
50219             sel.cursorAfter(pc);
50220             this.core.undoManager.addEvent();
50221             this.core.fireEditorEvent(e);
50222             return false;
50223         }
50224     
50225         var li = doc.createElement('LI');
50226         li.innerHTML = '&nbsp;';
50227         if (!pli || !pli.firstSibling) {
50228             pc.appendChild(li);
50229         } else {
50230             pli.parentNode.insertBefore(li, pli.firstSibling);
50231         }
50232         sel.cursorText (li.firstChild);
50233       
50234         this.core.undoManager.addEvent();
50235         this.core.fireEditorEvent(e);
50236
50237         return false;
50238         
50239     
50240         
50241         
50242          
50243     }
50244 };
50245      
50246 /**
50247  * @class Roo.htmleditor.Block
50248  * Base class for html editor blocks - do not use it directly .. extend it..
50249  * @cfg {DomElement} node The node to apply stuff to.
50250  * @cfg {String} friendly_name the name that appears in the context bar about this block
50251  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
50252  
50253  * @constructor
50254  * Create a new Filter.
50255  * @param {Object} config Configuration options
50256  */
50257
50258 Roo.htmleditor.Block  = function(cfg)
50259 {
50260     // do nothing .. should not be called really.
50261 }
50262 /**
50263  * factory method to get the block from an element (using cache if necessary)
50264  * @static
50265  * @param {HtmlElement} the dom element
50266  */
50267 Roo.htmleditor.Block.factory = function(node)
50268 {
50269     var cc = Roo.htmleditor.Block.cache;
50270     var id = Roo.get(node).id;
50271     if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
50272         Roo.htmleditor.Block.cache[id].readElement(node);
50273         return Roo.htmleditor.Block.cache[id];
50274     }
50275     var db  = node.getAttribute('data-block');
50276     if (!db) {
50277         db = node.nodeName.toLowerCase().toUpperCaseFirst();
50278     }
50279     var cls = Roo.htmleditor['Block' + db];
50280     if (typeof(cls) == 'undefined') {
50281         //Roo.log(node.getAttribute('data-block'));
50282         Roo.log("OOps missing block : " + 'Block' + db);
50283         return false;
50284     }
50285     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
50286     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
50287 };
50288
50289 /**
50290  * initalize all Elements from content that are 'blockable'
50291  * @static
50292  * @param the body element
50293  */
50294 Roo.htmleditor.Block.initAll = function(body, type)
50295 {
50296     if (typeof(type) == 'undefined') {
50297         var ia = Roo.htmleditor.Block.initAll;
50298         ia(body,'table');
50299         ia(body,'td');
50300         ia(body,'figure');
50301         return;
50302     }
50303     Roo.each(Roo.get(body).query(type), function(e) {
50304         Roo.htmleditor.Block.factory(e);    
50305     },this);
50306 };
50307 // question goes here... do we need to clear out this cache sometimes?
50308 // or show we make it relivant to the htmleditor.
50309 Roo.htmleditor.Block.cache = {};
50310
50311 Roo.htmleditor.Block.prototype = {
50312     
50313     node : false,
50314     
50315      // used by context menu
50316     friendly_name : 'Based Block',
50317     
50318     // text for button to delete this element
50319     deleteTitle : false,
50320     
50321     context : false,
50322     /**
50323      * Update a node with values from this object
50324      * @param {DomElement} node
50325      */
50326     updateElement : function(node)
50327     {
50328         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
50329     },
50330      /**
50331      * convert to plain HTML for calling insertAtCursor..
50332      */
50333     toHTML : function()
50334     {
50335         return Roo.DomHelper.markup(this.toObject());
50336     },
50337     /**
50338      * used by readEleemnt to extract data from a node
50339      * may need improving as it's pretty basic
50340      
50341      * @param {DomElement} node
50342      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
50343      * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
50344      * @param {String} style the style property - eg. text-align
50345      */
50346     getVal : function(node, tag, attr, style)
50347     {
50348         var n = node;
50349         if (tag !== true && n.tagName != tag.toUpperCase()) {
50350             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
50351             // but kiss for now.
50352             n = node.getElementsByTagName(tag).item(0);
50353         }
50354         if (!n) {
50355             return '';
50356         }
50357         if (attr === false) {
50358             return n;
50359         }
50360         if (attr == 'html') {
50361             return n.innerHTML;
50362         }
50363         if (attr == 'style') {
50364             return n.style[style]; 
50365         }
50366         
50367         return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
50368             
50369     },
50370     /**
50371      * create a DomHelper friendly object - for use with 
50372      * Roo.DomHelper.markup / overwrite / etc..
50373      * (override this)
50374      */
50375     toObject : function()
50376     {
50377         return {};
50378     },
50379       /**
50380      * Read a node that has a 'data-block' property - and extract the values from it.
50381      * @param {DomElement} node - the node
50382      */
50383     readElement : function(node)
50384     {
50385         
50386     } 
50387     
50388     
50389 };
50390
50391  
50392
50393 /**
50394  * @class Roo.htmleditor.BlockFigure
50395  * Block that has an image and a figcaption
50396  * @cfg {String} image_src the url for the image
50397  * @cfg {String} align (left|right) alignment for the block default left
50398  * @cfg {String} caption the text to appear below  (and in the alt tag)
50399  * @cfg {String} caption_display (block|none) display or not the caption
50400  * @cfg {String|number} image_width the width of the image number or %?
50401  * @cfg {String|number} image_height the height of the image number or %?
50402  * 
50403  * @constructor
50404  * Create a new Filter.
50405  * @param {Object} config Configuration options
50406  */
50407
50408 Roo.htmleditor.BlockFigure = function(cfg)
50409 {
50410     if (cfg.node) {
50411         this.readElement(cfg.node);
50412         this.updateElement(cfg.node);
50413     }
50414     Roo.apply(this, cfg);
50415 }
50416 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
50417  
50418     
50419     // setable values.
50420     image_src: '',
50421     align: 'center',
50422     caption : '',
50423     caption_display : 'block',
50424     width : '100%',
50425     cls : '',
50426     href: '',
50427     video_url : '',
50428     
50429     // margin: '2%', not used
50430     
50431     text_align: 'left', //   (left|right) alignment for the text caption default left. - not used at present
50432
50433     
50434     // used by context menu
50435     friendly_name : 'Image with caption',
50436     deleteTitle : "Delete Image and Caption",
50437     
50438     contextMenu : function(toolbar)
50439     {
50440         
50441         var block = function() {
50442             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
50443         };
50444         
50445         
50446         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
50447         
50448         var syncValue = toolbar.editorcore.syncValue;
50449         
50450         var fields = {};
50451         
50452         return [
50453              {
50454                 xtype : 'TextItem',
50455                 text : "Source: ",
50456                 xns : rooui.Toolbar  //Boostrap?
50457             },
50458             {
50459                 xtype : 'Button',
50460                 text: 'Change Image URL',
50461                  
50462                 listeners : {
50463                     click: function (btn, state)
50464                     {
50465                         var b = block();
50466                         
50467                         Roo.MessageBox.show({
50468                             title : "Image Source URL",
50469                             msg : "Enter the url for the image",
50470                             buttons: Roo.MessageBox.OKCANCEL,
50471                             fn: function(btn, val){
50472                                 if (btn != 'ok') {
50473                                     return;
50474                                 }
50475                                 b.image_src = val;
50476                                 b.updateElement();
50477                                 syncValue();
50478                                 toolbar.editorcore.onEditorEvent();
50479                             },
50480                             minWidth:250,
50481                             prompt:true,
50482                             //multiline: multiline,
50483                             modal : true,
50484                             value : b.image_src
50485                         });
50486                     }
50487                 },
50488                 xns : rooui.Toolbar
50489             },
50490          
50491             {
50492                 xtype : 'Button',
50493                 text: 'Change Link URL',
50494                  
50495                 listeners : {
50496                     click: function (btn, state)
50497                     {
50498                         var b = block();
50499                         
50500                         Roo.MessageBox.show({
50501                             title : "Link URL",
50502                             msg : "Enter the url for the link - leave blank to have no link",
50503                             buttons: Roo.MessageBox.OKCANCEL,
50504                             fn: function(btn, val){
50505                                 if (btn != 'ok') {
50506                                     return;
50507                                 }
50508                                 b.href = val;
50509                                 b.updateElement();
50510                                 syncValue();
50511                                 toolbar.editorcore.onEditorEvent();
50512                             },
50513                             minWidth:250,
50514                             prompt:true,
50515                             //multiline: multiline,
50516                             modal : true,
50517                             value : b.href
50518                         });
50519                     }
50520                 },
50521                 xns : rooui.Toolbar
50522             },
50523             {
50524                 xtype : 'Button',
50525                 text: 'Show Video URL',
50526                  
50527                 listeners : {
50528                     click: function (btn, state)
50529                     {
50530                         Roo.MessageBox.alert("Video URL",
50531                             block().video_url == '' ? 'This image is not linked ot a video' :
50532                                 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
50533                     }
50534                 },
50535                 xns : rooui.Toolbar
50536             },
50537             
50538             
50539             {
50540                 xtype : 'TextItem',
50541                 text : "Width: ",
50542                 xns : rooui.Toolbar  //Boostrap?
50543             },
50544             {
50545                 xtype : 'ComboBox',
50546                 allowBlank : false,
50547                 displayField : 'val',
50548                 editable : true,
50549                 listWidth : 100,
50550                 triggerAction : 'all',
50551                 typeAhead : true,
50552                 valueField : 'val',
50553                 width : 70,
50554                 name : 'width',
50555                 listeners : {
50556                     select : function (combo, r, index)
50557                     {
50558                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
50559                         var b = block();
50560                         b.width = r.get('val');
50561                         b.updateElement();
50562                         syncValue();
50563                         toolbar.editorcore.onEditorEvent();
50564                     }
50565                 },
50566                 xns : rooui.form,
50567                 store : {
50568                     xtype : 'SimpleStore',
50569                     data : [
50570                         ['100%'],
50571                         ['80%'],
50572                         ['50%'],
50573                         ['20%'],
50574                         ['10%']
50575                     ],
50576                     fields : [ 'val'],
50577                     xns : Roo.data
50578                 }
50579             },
50580             {
50581                 xtype : 'TextItem',
50582                 text : "Align: ",
50583                 xns : rooui.Toolbar  //Boostrap?
50584             },
50585             {
50586                 xtype : 'ComboBox',
50587                 allowBlank : false,
50588                 displayField : 'val',
50589                 editable : true,
50590                 listWidth : 100,
50591                 triggerAction : 'all',
50592                 typeAhead : true,
50593                 valueField : 'val',
50594                 width : 70,
50595                 name : 'align',
50596                 listeners : {
50597                     select : function (combo, r, index)
50598                     {
50599                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
50600                         var b = block();
50601                         b.align = r.get('val');
50602                         b.updateElement();
50603                         syncValue();
50604                         toolbar.editorcore.onEditorEvent();
50605                     }
50606                 },
50607                 xns : rooui.form,
50608                 store : {
50609                     xtype : 'SimpleStore',
50610                     data : [
50611                         ['left'],
50612                         ['right'],
50613                         ['center']
50614                     ],
50615                     fields : [ 'val'],
50616                     xns : Roo.data
50617                 }
50618             },
50619             
50620               
50621             {
50622                 xtype : 'Button',
50623                 text: 'Hide Caption',
50624                 name : 'caption_display',
50625                 pressed : false,
50626                 enableToggle : true,
50627                 setValue : function(v) {
50628                     // this trigger toggle.
50629                      
50630                     this.setText(v ? "Hide Caption" : "Show Caption");
50631                     this.setPressed(v != 'block');
50632                 },
50633                 listeners : {
50634                     toggle: function (btn, state)
50635                     {
50636                         var b  = block();
50637                         b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
50638                         this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
50639                         b.updateElement();
50640                         syncValue();
50641                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
50642                         toolbar.editorcore.onEditorEvent();
50643                     }
50644                 },
50645                 xns : rooui.Toolbar
50646             }
50647         ];
50648         
50649     },
50650     /**
50651      * create a DomHelper friendly object - for use with
50652      * Roo.DomHelper.markup / overwrite / etc..
50653      */
50654     toObject : function()
50655     {
50656         var d = document.createElement('div');
50657         d.innerHTML = this.caption;
50658         
50659         var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0; 
50660         
50661         var iw = this.align == 'center' ? this.width : '100%';
50662         var img =   {
50663             tag : 'img',
50664             contenteditable : 'false',
50665             src : this.image_src,
50666             alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
50667             style: {
50668                 width : iw,
50669                 maxWidth : iw + ' !important', // this is not getting rendered?
50670                 margin : m  
50671                 
50672             },
50673             width: this.align == 'center' ?  this.width : '100%' 
50674
50675         };
50676         
50677         /*
50678         '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
50679                     '<a href="{2}">' + 
50680                         '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' + 
50681                     '</a>' + 
50682                 '</div>',
50683         */
50684                 
50685         if (this.href.length > 0) {
50686             img = {
50687                 tag : 'a',
50688                 href: this.href,
50689                 contenteditable : 'true',
50690                 cn : [
50691                     img
50692                 ]
50693             };
50694         }
50695         
50696         
50697         if (this.video_url.length > 0) {
50698             img = {
50699                 tag : 'div',
50700                 cls : this.cls,
50701                 frameborder : 0,
50702                 allowfullscreen : true,
50703                 width : 420,  // these are for video tricks - that we replace the outer
50704                 height : 315,
50705                 src : this.video_url,
50706                 cn : [
50707                     img
50708                 ]
50709             };
50710         }
50711
50712
50713   
50714         var ret =   {
50715             tag: 'figure',
50716             'data-block' : 'Figure',
50717             'data-width' : this.width,
50718             'data-caption' : this.caption, 
50719             'data-caption-display' : this.caption_display,
50720             contenteditable : 'false',
50721             
50722             style : {
50723                 display: 'block',
50724                 float :  this.align ,
50725                 maxWidth :  this.align == 'center' ? '100% !important' : (this.width + ' !important'),
50726                 width : this.align == 'center' ? '100%' : this.width,
50727                 margin:  '0px',
50728                 padding: this.align == 'center' ? '0' : '0 10px' ,
50729                 textAlign : this.align   // seems to work for email..
50730                 
50731             },
50732             
50733             align : this.align,
50734             cn : [
50735                 img
50736             ]
50737         };
50738
50739         // show figcaption only if caption_display is 'block'
50740         if(this.caption_display == 'block') {
50741             ret['cn'].push({
50742                 tag: 'figcaption',
50743                 style : {
50744                     textAlign : 'left',
50745                     fontSize : '16px',
50746                     lineHeight : '24px',
50747                     display : this.caption_display,
50748                     maxWidth : (this.align == 'center' ?  this.width : '100%' ) + ' !important',
50749                     margin: m,
50750                     width: this.align == 'center' ?  this.width : '100%' 
50751                 
50752                      
50753                 },
50754                 cls : this.cls.length > 0 ? (this.cls  + '-thumbnail' ) : '',
50755                 cn : [
50756                     {
50757                         tag: 'div',
50758                         style  : {
50759                             marginTop : '16px',
50760                             textAlign : 'start'
50761                         },
50762                         align: 'left',
50763                         cn : [
50764                             {
50765                                 // we can not rely on yahoo syndication to use CSS elements - so have to use  '<i>' to encase stuff.
50766                                 tag : 'i',
50767                                 contenteditable : Roo.htmleditor.BlockFigure.caption_edit,
50768                                 html : this.caption.length ? this.caption : "Caption" // fake caption
50769                             }
50770                             
50771                         ]
50772                     }
50773                     
50774                 ]
50775                 
50776             });
50777         }
50778         return ret;
50779          
50780     },
50781     
50782     readElement : function(node)
50783     {
50784         // this should not really come from the link...
50785         this.video_url = this.getVal(node, 'div', 'src');
50786         this.cls = this.getVal(node, 'div', 'class');
50787         this.href = this.getVal(node, 'a', 'href');
50788         
50789         
50790         this.image_src = this.getVal(node, 'img', 'src');
50791          
50792         this.align = this.getVal(node, 'figure', 'align');
50793
50794         // caption display is stored in figure
50795         this.caption_display = this.getVal(node, true, 'data-caption-display');
50796
50797         // backward compatible
50798         // it was stored in figcaption
50799         if(this.caption_display == '') {
50800             this.caption_display = this.getVal(node, 'figcaption', 'data-display');
50801         }
50802
50803         // read caption from figcaption
50804         var figcaption = this.getVal(node, 'figcaption', false);
50805
50806         if (figcaption !== '') {
50807             this.caption = this.getVal(figcaption, 'i', 'html');
50808         }
50809                 
50810
50811         // read caption from data-caption in figure if no caption from figcaption
50812         var dc = this.getVal(node, true, 'data-caption');
50813
50814         if(this.caption_display == 'none' && dc && dc.length){
50815             this.caption = dc;
50816         }
50817
50818         //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
50819         this.width = this.getVal(node, true, 'data-width');
50820         //this.margin = this.getVal(node, 'figure', 'style', 'margin');
50821         
50822     },
50823     removeNode : function()
50824     {
50825         return this.node;
50826     }
50827     
50828   
50829    
50830      
50831     
50832     
50833     
50834     
50835 });
50836
50837 Roo.apply(Roo.htmleditor.BlockFigure, {
50838     caption_edit : true
50839 });
50840
50841  
50842
50843 /**
50844  * @class Roo.htmleditor.BlockTable
50845  * Block that manages a table
50846  * 
50847  * @constructor
50848  * Create a new Filter.
50849  * @param {Object} config Configuration options
50850  */
50851
50852 Roo.htmleditor.BlockTable = function(cfg)
50853 {
50854     if (cfg.node) {
50855         this.readElement(cfg.node);
50856         this.updateElement(cfg.node);
50857     }
50858     Roo.apply(this, cfg);
50859     if (!cfg.node) {
50860         this.rows = [];
50861         for(var r = 0; r < this.no_row; r++) {
50862             this.rows[r] = [];
50863             for(var c = 0; c < this.no_col; c++) {
50864                 this.rows[r][c] = this.emptyCell();
50865             }
50866         }
50867     }
50868     
50869     
50870 }
50871 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
50872  
50873     rows : false,
50874     no_col : 1,
50875     no_row : 1,
50876     
50877     
50878     width: '100%',
50879     
50880     // used by context menu
50881     friendly_name : 'Table',
50882     deleteTitle : 'Delete Table',
50883     // context menu is drawn once..
50884     
50885     contextMenu : function(toolbar)
50886     {
50887         
50888         var block = function() {
50889             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
50890         };
50891         
50892         
50893         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
50894         
50895         var syncValue = toolbar.editorcore.syncValue;
50896         
50897         var fields = {};
50898         
50899         return [
50900             {
50901                 xtype : 'TextItem',
50902                 text : "Width: ",
50903                 xns : rooui.Toolbar  //Boostrap?
50904             },
50905             {
50906                 xtype : 'ComboBox',
50907                 allowBlank : false,
50908                 displayField : 'val',
50909                 editable : true,
50910                 listWidth : 100,
50911                 triggerAction : 'all',
50912                 typeAhead : true,
50913                 valueField : 'val',
50914                 width : 100,
50915                 name : 'width',
50916                 listeners : {
50917                     select : function (combo, r, index)
50918                     {
50919                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
50920                         var b = block();
50921                         b.width = r.get('val');
50922                         b.updateElement();
50923                         syncValue();
50924                         toolbar.editorcore.onEditorEvent();
50925                     }
50926                 },
50927                 xns : rooui.form,
50928                 store : {
50929                     xtype : 'SimpleStore',
50930                     data : [
50931                         ['100%'],
50932                         ['auto']
50933                     ],
50934                     fields : [ 'val'],
50935                     xns : Roo.data
50936                 }
50937             },
50938             // -------- Cols
50939             
50940             {
50941                 xtype : 'TextItem',
50942                 text : "Columns: ",
50943                 xns : rooui.Toolbar  //Boostrap?
50944             },
50945          
50946             {
50947                 xtype : 'Button',
50948                 text: '-',
50949                 listeners : {
50950                     click : function (_self, e)
50951                     {
50952                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
50953                         block().removeColumn();
50954                         syncValue();
50955                         toolbar.editorcore.onEditorEvent();
50956                     }
50957                 },
50958                 xns : rooui.Toolbar
50959             },
50960             {
50961                 xtype : 'Button',
50962                 text: '+',
50963                 listeners : {
50964                     click : function (_self, e)
50965                     {
50966                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
50967                         block().addColumn();
50968                         syncValue();
50969                         toolbar.editorcore.onEditorEvent();
50970                     }
50971                 },
50972                 xns : rooui.Toolbar
50973             },
50974             // -------- ROWS
50975             {
50976                 xtype : 'TextItem',
50977                 text : "Rows: ",
50978                 xns : rooui.Toolbar  //Boostrap?
50979             },
50980          
50981             {
50982                 xtype : 'Button',
50983                 text: '-',
50984                 listeners : {
50985                     click : function (_self, e)
50986                     {
50987                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
50988                         block().removeRow();
50989                         syncValue();
50990                         toolbar.editorcore.onEditorEvent();
50991                     }
50992                 },
50993                 xns : rooui.Toolbar
50994             },
50995             {
50996                 xtype : 'Button',
50997                 text: '+',
50998                 listeners : {
50999                     click : function (_self, e)
51000                     {
51001                         block().addRow();
51002                         syncValue();
51003                         toolbar.editorcore.onEditorEvent();
51004                     }
51005                 },
51006                 xns : rooui.Toolbar
51007             },
51008             // -------- ROWS
51009             {
51010                 xtype : 'Button',
51011                 text: 'Reset Column Widths',
51012                 listeners : {
51013                     
51014                     click : function (_self, e)
51015                     {
51016                         block().resetWidths();
51017                         syncValue();
51018                         toolbar.editorcore.onEditorEvent();
51019                     }
51020                 },
51021                 xns : rooui.Toolbar
51022             } 
51023             
51024             
51025             
51026         ];
51027         
51028     },
51029     
51030     
51031   /**
51032      * create a DomHelper friendly object - for use with
51033      * Roo.DomHelper.markup / overwrite / etc..
51034      * ?? should it be called with option to hide all editing features?
51035      */
51036     toObject : function()
51037     {
51038         
51039         var ret = {
51040             tag : 'table',
51041             contenteditable : 'false', // this stops cell selection from picking the table.
51042             'data-block' : 'Table',
51043             style : {
51044                 width:  this.width,
51045                 border : 'solid 1px #000', // ??? hard coded?
51046                 'border-collapse' : 'collapse' 
51047             },
51048             cn : [
51049                 { tag : 'tbody' , cn : [] }
51050             ]
51051         };
51052         
51053         // do we have a head = not really 
51054         var ncols = 0;
51055         Roo.each(this.rows, function( row ) {
51056             var tr = {
51057                 tag: 'tr',
51058                 style : {
51059                     margin: '6px',
51060                     border : 'solid 1px #000',
51061                     textAlign : 'left' 
51062                 },
51063                 cn : [ ]
51064             };
51065             
51066             ret.cn[0].cn.push(tr);
51067             // does the row have any properties? ?? height?
51068             var nc = 0;
51069             Roo.each(row, function( cell ) {
51070                 
51071                 var td = {
51072                     tag : 'td',
51073                     contenteditable :  'true',
51074                     'data-block' : 'Td',
51075                     html : cell.html,
51076                     style : cell.style
51077                 };
51078                 if (cell.colspan > 1) {
51079                     td.colspan = cell.colspan ;
51080                     nc += cell.colspan;
51081                 } else {
51082                     nc++;
51083                 }
51084                 if (cell.rowspan > 1) {
51085                     td.rowspan = cell.rowspan ;
51086                 }
51087                 
51088                 
51089                 // widths ?
51090                 tr.cn.push(td);
51091                     
51092                 
51093             }, this);
51094             ncols = Math.max(nc, ncols);
51095             
51096             
51097         }, this);
51098         // add the header row..
51099         
51100         ncols++;
51101          
51102         
51103         return ret;
51104          
51105     },
51106     
51107     readElement : function(node)
51108     {
51109         node  = node ? node : this.node ;
51110         this.width = this.getVal(node, true, 'style', 'width') || '100%';
51111         
51112         this.rows = [];
51113         this.no_row = 0;
51114         var trs = Array.from(node.rows);
51115         trs.forEach(function(tr) {
51116             var row =  [];
51117             this.rows.push(row);
51118             
51119             this.no_row++;
51120             var no_column = 0;
51121             Array.from(tr.cells).forEach(function(td) {
51122                 
51123                 var add = {
51124                     colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
51125                     rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
51126                     style : td.hasAttribute('style') ? td.getAttribute('style') : '',
51127                     html : td.innerHTML
51128                 };
51129                 no_column += add.colspan;
51130                      
51131                 
51132                 row.push(add);
51133                 
51134                 
51135             },this);
51136             this.no_col = Math.max(this.no_col, no_column);
51137             
51138             
51139         },this);
51140         
51141         
51142     },
51143     normalizeRows: function()
51144     {
51145         var ret= [];
51146         var rid = -1;
51147         this.rows.forEach(function(row) {
51148             rid++;
51149             ret[rid] = [];
51150             row = this.normalizeRow(row);
51151             var cid = 0;
51152             row.forEach(function(c) {
51153                 while (typeof(ret[rid][cid]) != 'undefined') {
51154                     cid++;
51155                 }
51156                 if (typeof(ret[rid]) == 'undefined') {
51157                     ret[rid] = [];
51158                 }
51159                 ret[rid][cid] = c;
51160                 c.row = rid;
51161                 c.col = cid;
51162                 if (c.rowspan < 2) {
51163                     return;
51164                 }
51165                 
51166                 for(var i = 1 ;i < c.rowspan; i++) {
51167                     if (typeof(ret[rid+i]) == 'undefined') {
51168                         ret[rid+i] = [];
51169                     }
51170                     ret[rid+i][cid] = c;
51171                 }
51172             });
51173         }, this);
51174         return ret;
51175     
51176     },
51177     
51178     normalizeRow: function(row)
51179     {
51180         var ret= [];
51181         row.forEach(function(c) {
51182             if (c.colspan < 2) {
51183                 ret.push(c);
51184                 return;
51185             }
51186             for(var i =0 ;i < c.colspan; i++) {
51187                 ret.push(c);
51188             }
51189         });
51190         return ret;
51191     
51192     },
51193     
51194     deleteColumn : function(sel)
51195     {
51196         if (!sel || sel.type != 'col') {
51197             return;
51198         }
51199         if (this.no_col < 2) {
51200             return;
51201         }
51202         
51203         this.rows.forEach(function(row) {
51204             var cols = this.normalizeRow(row);
51205             var col = cols[sel.col];
51206             if (col.colspan > 1) {
51207                 col.colspan --;
51208             } else {
51209                 row.remove(col);
51210             }
51211             
51212         }, this);
51213         this.no_col--;
51214         
51215     },
51216     removeColumn : function()
51217     {
51218         this.deleteColumn({
51219             type: 'col',
51220             col : this.no_col-1
51221         });
51222         this.updateElement();
51223     },
51224     
51225      
51226     addColumn : function()
51227     {
51228         
51229         this.rows.forEach(function(row) {
51230             row.push(this.emptyCell());
51231            
51232         }, this);
51233         this.updateElement();
51234     },
51235     
51236     deleteRow : function(sel)
51237     {
51238         if (!sel || sel.type != 'row') {
51239             return;
51240         }
51241         
51242         if (this.no_row < 2) {
51243             return;
51244         }
51245         
51246         var rows = this.normalizeRows();
51247         
51248         
51249         rows[sel.row].forEach(function(col) {
51250             if (col.rowspan > 1) {
51251                 col.rowspan--;
51252             } else {
51253                 col.remove = 1; // flage it as removed.
51254             }
51255             
51256         }, this);
51257         var newrows = [];
51258         this.rows.forEach(function(row) {
51259             newrow = [];
51260             row.forEach(function(c) {
51261                 if (typeof(c.remove) == 'undefined') {
51262                     newrow.push(c);
51263                 }
51264                 
51265             });
51266             if (newrow.length > 0) {
51267                 newrows.push(row);
51268             }
51269         });
51270         this.rows =  newrows;
51271         
51272         
51273         
51274         this.no_row--;
51275         this.updateElement();
51276         
51277     },
51278     removeRow : function()
51279     {
51280         this.deleteRow({
51281             type: 'row',
51282             row : this.no_row-1
51283         });
51284         
51285     },
51286     
51287      
51288     addRow : function()
51289     {
51290         
51291         var row = [];
51292         for (var i = 0; i < this.no_col; i++ ) {
51293             
51294             row.push(this.emptyCell());
51295            
51296         }
51297         this.rows.push(row);
51298         this.updateElement();
51299         
51300     },
51301      
51302     // the default cell object... at present...
51303     emptyCell : function() {
51304         return (new Roo.htmleditor.BlockTd({})).toObject();
51305         
51306      
51307     },
51308     
51309     removeNode : function()
51310     {
51311         return this.node;
51312     },
51313     
51314     
51315     
51316     resetWidths : function()
51317     {
51318         Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
51319             var nn = Roo.htmleditor.Block.factory(n);
51320             nn.width = '';
51321             nn.updateElement(n);
51322         });
51323     }
51324     
51325     
51326     
51327     
51328 })
51329
51330 /**
51331  *
51332  * editing a TD?
51333  *
51334  * since selections really work on the table cell, then editing really should work from there
51335  *
51336  * The original plan was to support merging etc... - but that may not be needed yet..
51337  *
51338  * So this simple version will support:
51339  *   add/remove cols
51340  *   adjust the width +/-
51341  *   reset the width...
51342  *   
51343  *
51344  */
51345
51346
51347  
51348
51349 /**
51350  * @class Roo.htmleditor.BlockTable
51351  * Block that manages a table
51352  * 
51353  * @constructor
51354  * Create a new Filter.
51355  * @param {Object} config Configuration options
51356  */
51357
51358 Roo.htmleditor.BlockTd = function(cfg)
51359 {
51360     if (cfg.node) {
51361         this.readElement(cfg.node);
51362         this.updateElement(cfg.node);
51363     }
51364     Roo.apply(this, cfg);
51365      
51366     
51367     
51368 }
51369 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
51370  
51371     node : false,
51372     
51373     width: '',
51374     textAlign : 'left',
51375     valign : 'top',
51376     
51377     colspan : 1,
51378     rowspan : 1,
51379     
51380     
51381     // used by context menu
51382     friendly_name : 'Table Cell',
51383     deleteTitle : false, // use our customer delete
51384     
51385     // context menu is drawn once..
51386     
51387     contextMenu : function(toolbar)
51388     {
51389         
51390         var cell = function() {
51391             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
51392         };
51393         
51394         var table = function() {
51395             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
51396         };
51397         
51398         var lr = false;
51399         var saveSel = function()
51400         {
51401             lr = toolbar.editorcore.getSelection().getRangeAt(0);
51402         }
51403         var restoreSel = function()
51404         {
51405             if (lr) {
51406                 (function() {
51407                     toolbar.editorcore.focus();
51408                     var cr = toolbar.editorcore.getSelection();
51409                     cr.removeAllRanges();
51410                     cr.addRange(lr);
51411                     toolbar.editorcore.onEditorEvent();
51412                 }).defer(10, this);
51413                 
51414                 
51415             }
51416         }
51417         
51418         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
51419         
51420         var syncValue = toolbar.editorcore.syncValue;
51421         
51422         var fields = {};
51423         
51424         return [
51425             {
51426                 xtype : 'Button',
51427                 text : 'Edit Table',
51428                 listeners : {
51429                     click : function() {
51430                         var t = toolbar.tb.selectedNode.closest('table');
51431                         toolbar.editorcore.selectNode(t);
51432                         toolbar.editorcore.onEditorEvent();                        
51433                     }
51434                 }
51435                 
51436             },
51437               
51438            
51439              
51440             {
51441                 xtype : 'TextItem',
51442                 text : "Column Width: ",
51443                  xns : rooui.Toolbar 
51444                
51445             },
51446             {
51447                 xtype : 'Button',
51448                 text: '-',
51449                 listeners : {
51450                     click : function (_self, e)
51451                     {
51452                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
51453                         cell().shrinkColumn();
51454                         syncValue();
51455                          toolbar.editorcore.onEditorEvent();
51456                     }
51457                 },
51458                 xns : rooui.Toolbar
51459             },
51460             {
51461                 xtype : 'Button',
51462                 text: '+',
51463                 listeners : {
51464                     click : function (_self, e)
51465                     {
51466                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
51467                         cell().growColumn();
51468                         syncValue();
51469                         toolbar.editorcore.onEditorEvent();
51470                     }
51471                 },
51472                 xns : rooui.Toolbar
51473             },
51474             
51475             {
51476                 xtype : 'TextItem',
51477                 text : "Vertical Align: ",
51478                 xns : rooui.Toolbar  //Boostrap?
51479             },
51480             {
51481                 xtype : 'ComboBox',
51482                 allowBlank : false,
51483                 displayField : 'val',
51484                 editable : true,
51485                 listWidth : 100,
51486                 triggerAction : 'all',
51487                 typeAhead : true,
51488                 valueField : 'val',
51489                 width : 100,
51490                 name : 'valign',
51491                 listeners : {
51492                     select : function (combo, r, index)
51493                     {
51494                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
51495                         var b = cell();
51496                         b.valign = r.get('val');
51497                         b.updateElement();
51498                         syncValue();
51499                         toolbar.editorcore.onEditorEvent();
51500                     }
51501                 },
51502                 xns : rooui.form,
51503                 store : {
51504                     xtype : 'SimpleStore',
51505                     data : [
51506                         ['top'],
51507                         ['middle'],
51508                         ['bottom'] // there are afew more... 
51509                     ],
51510                     fields : [ 'val'],
51511                     xns : Roo.data
51512                 }
51513             },
51514             
51515             {
51516                 xtype : 'TextItem',
51517                 text : "Merge Cells: ",
51518                  xns : rooui.Toolbar 
51519                
51520             },
51521             
51522             
51523             {
51524                 xtype : 'Button',
51525                 text: 'Right',
51526                 listeners : {
51527                     click : function (_self, e)
51528                     {
51529                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
51530                         cell().mergeRight();
51531                         //block().growColumn();
51532                         syncValue();
51533                         toolbar.editorcore.onEditorEvent();
51534                     }
51535                 },
51536                 xns : rooui.Toolbar
51537             },
51538              
51539             {
51540                 xtype : 'Button',
51541                 text: 'Below',
51542                 listeners : {
51543                     click : function (_self, e)
51544                     {
51545                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
51546                         cell().mergeBelow();
51547                         //block().growColumn();
51548                         syncValue();
51549                         toolbar.editorcore.onEditorEvent();
51550                     }
51551                 },
51552                 xns : rooui.Toolbar
51553             },
51554             {
51555                 xtype : 'TextItem',
51556                 text : "| ",
51557                  xns : rooui.Toolbar 
51558                
51559             },
51560             
51561             {
51562                 xtype : 'Button',
51563                 text: 'Split',
51564                 listeners : {
51565                     click : function (_self, e)
51566                     {
51567                         //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
51568                         cell().split();
51569                         syncValue();
51570                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
51571                         toolbar.editorcore.onEditorEvent();
51572                                              
51573                     }
51574                 },
51575                 xns : rooui.Toolbar
51576             },
51577             {
51578                 xtype : 'Fill',
51579                 xns : rooui.Toolbar 
51580                
51581             },
51582         
51583           
51584             {
51585                 xtype : 'Button',
51586                 text: 'Delete',
51587                  
51588                 xns : rooui.Toolbar,
51589                 menu : {
51590                     xtype : 'Menu',
51591                     xns : rooui.menu,
51592                     items : [
51593                         {
51594                             xtype : 'Item',
51595                             html: 'Column',
51596                             listeners : {
51597                                 click : function (_self, e)
51598                                 {
51599                                     var t = table();
51600                                     
51601                                     cell().deleteColumn();
51602                                     syncValue();
51603                                     toolbar.editorcore.selectNode(t.node);
51604                                     toolbar.editorcore.onEditorEvent();   
51605                                 }
51606                             },
51607                             xns : rooui.menu
51608                         },
51609                         {
51610                             xtype : 'Item',
51611                             html: 'Row',
51612                             listeners : {
51613                                 click : function (_self, e)
51614                                 {
51615                                     var t = table();
51616                                     cell().deleteRow();
51617                                     syncValue();
51618                                     
51619                                     toolbar.editorcore.selectNode(t.node);
51620                                     toolbar.editorcore.onEditorEvent();   
51621                                                          
51622                                 }
51623                             },
51624                             xns : rooui.menu
51625                         },
51626                        {
51627                             xtype : 'Separator',
51628                             xns : rooui.menu
51629                         },
51630                         {
51631                             xtype : 'Item',
51632                             html: 'Table',
51633                             listeners : {
51634                                 click : function (_self, e)
51635                                 {
51636                                     var t = table();
51637                                     var nn = t.node.nextSibling || t.node.previousSibling;
51638                                     t.node.parentNode.removeChild(t.node);
51639                                     if (nn) { 
51640                                         toolbar.editorcore.selectNode(nn, true);
51641                                     }
51642                                     toolbar.editorcore.onEditorEvent();   
51643                                                          
51644                                 }
51645                             },
51646                             xns : rooui.menu
51647                         }
51648                     ]
51649                 }
51650             }
51651             
51652             // align... << fixme
51653             
51654         ];
51655         
51656     },
51657     
51658     
51659   /**
51660      * create a DomHelper friendly object - for use with
51661      * Roo.DomHelper.markup / overwrite / etc..
51662      * ?? should it be called with option to hide all editing features?
51663      */
51664  /**
51665      * create a DomHelper friendly object - for use with
51666      * Roo.DomHelper.markup / overwrite / etc..
51667      * ?? should it be called with option to hide all editing features?
51668      */
51669     toObject : function()
51670     {
51671         var ret = {
51672             tag : 'td',
51673             contenteditable : 'true', // this stops cell selection from picking the table.
51674             'data-block' : 'Td',
51675             valign : this.valign,
51676             style : {  
51677                 'text-align' :  this.textAlign,
51678                 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
51679                 'border-collapse' : 'collapse',
51680                 padding : '6px', // 8 for desktop / 4 for mobile
51681                 'vertical-align': this.valign
51682             },
51683             html : this.html
51684         };
51685         if (this.width != '') {
51686             ret.width = this.width;
51687             ret.style.width = this.width;
51688         }
51689         
51690         
51691         if (this.colspan > 1) {
51692             ret.colspan = this.colspan ;
51693         } 
51694         if (this.rowspan > 1) {
51695             ret.rowspan = this.rowspan ;
51696         }
51697         
51698            
51699         
51700         return ret;
51701          
51702     },
51703     
51704     readElement : function(node)
51705     {
51706         node  = node ? node : this.node ;
51707         this.width = node.style.width;
51708         this.colspan = Math.max(1,1*node.getAttribute('colspan'));
51709         this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
51710         this.html = node.innerHTML;
51711         if (node.style.textAlign != '') {
51712             this.textAlign = node.style.textAlign;
51713         }
51714         
51715         
51716     },
51717      
51718     // the default cell object... at present...
51719     emptyCell : function() {
51720         return {
51721             colspan :  1,
51722             rowspan :  1,
51723             textAlign : 'left',
51724             html : "&nbsp;" // is this going to be editable now?
51725         };
51726      
51727     },
51728     
51729     removeNode : function()
51730     {
51731         return this.node.closest('table');
51732          
51733     },
51734     
51735     cellData : false,
51736     
51737     colWidths : false,
51738     
51739     toTableArray  : function()
51740     {
51741         var ret = [];
51742         var tab = this.node.closest('tr').closest('table');
51743         Array.from(tab.rows).forEach(function(r, ri){
51744             ret[ri] = [];
51745         });
51746         var rn = 0;
51747         this.colWidths = [];
51748         var all_auto = true;
51749         Array.from(tab.rows).forEach(function(r, ri){
51750             
51751             var cn = 0;
51752             Array.from(r.cells).forEach(function(ce, ci){
51753                 var c =  {
51754                     cell : ce,
51755                     row : rn,
51756                     col: cn,
51757                     colspan : ce.colSpan,
51758                     rowspan : ce.rowSpan
51759                 };
51760                 if (ce.isEqualNode(this.node)) {
51761                     this.cellData = c;
51762                 }
51763                 // if we have been filled up by a row?
51764                 if (typeof(ret[rn][cn]) != 'undefined') {
51765                     while(typeof(ret[rn][cn]) != 'undefined') {
51766                         cn++;
51767                     }
51768                     c.col = cn;
51769                 }
51770                 
51771                 if (typeof(this.colWidths[cn]) == 'undefined' && c.colspan < 2) {
51772                     this.colWidths[cn] =   ce.style.width;
51773                     if (this.colWidths[cn] != '') {
51774                         all_auto = false;
51775                     }
51776                 }
51777                 
51778                 
51779                 if (c.colspan < 2 && c.rowspan < 2 ) {
51780                     ret[rn][cn] = c;
51781                     cn++;
51782                     return;
51783                 }
51784                 for(var j = 0; j < c.rowspan; j++) {
51785                     if (typeof(ret[rn+j]) == 'undefined') {
51786                         continue; // we have a problem..
51787                     }
51788                     ret[rn+j][cn] = c;
51789                     for(var i = 0; i < c.colspan; i++) {
51790                         ret[rn+j][cn+i] = c;
51791                     }
51792                 }
51793                 
51794                 cn += c.colspan;
51795             }, this);
51796             rn++;
51797         }, this);
51798         
51799         // initalize widths.?
51800         // either all widths or no widths..
51801         if (all_auto) {
51802             this.colWidths[0] = false; // no widths flag.
51803         }
51804         
51805         
51806         return ret;
51807         
51808     },
51809     
51810     
51811     
51812     
51813     mergeRight: function()
51814     {
51815          
51816         // get the contents of the next cell along..
51817         var tr = this.node.closest('tr');
51818         var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
51819         if (i >= tr.childNodes.length - 1) {
51820             return; // no cells on right to merge with.
51821         }
51822         var table = this.toTableArray();
51823         
51824         if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
51825             return; // nothing right?
51826         }
51827         var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
51828         // right cell - must be same rowspan and on the same row.
51829         if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
51830             return; // right hand side is not same rowspan.
51831         }
51832         
51833         
51834         
51835         this.node.innerHTML += ' ' + rc.cell.innerHTML;
51836         tr.removeChild(rc.cell);
51837         this.colspan += rc.colspan;
51838         this.node.setAttribute('colspan', this.colspan);
51839
51840         var table = this.toTableArray();
51841         this.normalizeWidths(table);
51842         this.updateWidths(table);
51843     },
51844     
51845     
51846     mergeBelow : function()
51847     {
51848         var table = this.toTableArray();
51849         if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
51850             return; // no row below
51851         }
51852         if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
51853             return; // nothing right?
51854         }
51855         var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
51856         
51857         if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
51858             return; // right hand side is not same rowspan.
51859         }
51860         this.node.innerHTML =  this.node.innerHTML + rc.cell.innerHTML ;
51861         rc.cell.parentNode.removeChild(rc.cell);
51862         this.rowspan += rc.rowspan;
51863         this.node.setAttribute('rowspan', this.rowspan);
51864     },
51865     
51866     split: function()
51867     {
51868         if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
51869             return;
51870         }
51871         var table = this.toTableArray();
51872         var cd = this.cellData;
51873         this.rowspan = 1;
51874         this.colspan = 1;
51875         
51876         for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
51877              
51878             
51879             for(var c = cd.col; c < cd.col + cd.colspan; c++) {
51880                 if (r == cd.row && c == cd.col) {
51881                     this.node.removeAttribute('rowspan');
51882                     this.node.removeAttribute('colspan');
51883                 }
51884                  
51885                 var ntd = this.node.cloneNode(); // which col/row should be 0..
51886                 ntd.removeAttribute('id'); 
51887                 ntd.style.width  = this.colWidths[c];
51888                 ntd.innerHTML = '';
51889                 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
51890             }
51891             
51892         }
51893         this.redrawAllCells(table);
51894         
51895     },
51896     
51897     
51898     
51899     redrawAllCells: function(table)
51900     {
51901         
51902          
51903         var tab = this.node.closest('tr').closest('table');
51904         var ctr = tab.rows[0].parentNode;
51905         Array.from(tab.rows).forEach(function(r, ri){
51906             
51907             Array.from(r.cells).forEach(function(ce, ci){
51908                 ce.parentNode.removeChild(ce);
51909             });
51910             r.parentNode.removeChild(r);
51911         });
51912         for(var r = 0 ; r < table.length; r++) {
51913             var re = tab.rows[r];
51914             
51915             var re = tab.ownerDocument.createElement('tr');
51916             ctr.appendChild(re);
51917             for(var c = 0 ; c < table[r].length; c++) {
51918                 if (table[r][c].cell === false) {
51919                     continue;
51920                 }
51921                 
51922                 re.appendChild(table[r][c].cell);
51923                  
51924                 table[r][c].cell = false;
51925             }
51926         }
51927         
51928     },
51929     updateWidths : function(table)
51930     {
51931         for(var r = 0 ; r < table.length; r++) {
51932            
51933             for(var c = 0 ; c < table[r].length; c++) {
51934                 if (table[r][c].cell === false) {
51935                     continue;
51936                 }
51937                 
51938                 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
51939                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
51940                     el.width = Math.floor(this.colWidths[c])  +'%';
51941                     el.updateElement(el.node);
51942                 }
51943                 if (this.colWidths[0] != false && table[r][c].colspan > 1) {
51944                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
51945                     var width = 0;
51946                     var lv = false;
51947                     for(var i = 0; i < table[r][c].colspan; i ++) {
51948                         if (typeof(this.colWidths[c + i]) != 'undefined') {
51949                             lv = this.colWidths[c + i];
51950                         } else {
51951                             this.colWidths[c + i] = lv;
51952                         }
51953                         width += Math.floor(this.colWidths[c + i]);
51954                     }
51955                     el.width = width  +'%';
51956                     el.updateElement(el.node);
51957                 }
51958                 table[r][c].cell = false; // done
51959             }
51960         }
51961     },
51962     normalizeWidths : function(table)
51963     {
51964         if (this.colWidths[0] === false) {
51965             var nw = 100.0 / this.colWidths.length;
51966             this.colWidths.forEach(function(w,i) {
51967                 this.colWidths[i] = nw;
51968             },this);
51969             return;
51970         }
51971     
51972         var t = 0, missing = [];
51973         
51974         this.colWidths.forEach(function(w,i) {
51975             //if you mix % and
51976             this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
51977             var add =  this.colWidths[i];
51978             if (add > 0) {
51979                 t+=add;
51980                 return;
51981             }
51982             missing.push(i);
51983             
51984             
51985         },this);
51986         var nc = this.colWidths.length;
51987         if (missing.length) {
51988             var mult = (nc - missing.length) / (1.0 * nc);
51989             var t = mult * t;
51990             var ew = (100 -t) / (1.0 * missing.length);
51991             this.colWidths.forEach(function(w,i) {
51992                 if (w > 0) {
51993                     this.colWidths[i] = w * mult;
51994                     return;
51995                 }
51996                 
51997                 this.colWidths[i] = ew;
51998             }, this);
51999             // have to make up numbers..
52000              
52001         }
52002         // now we should have all the widths..
52003         
52004     
52005     },
52006     
52007     shrinkColumn : function()
52008     {
52009         var table = this.toTableArray();
52010         this.normalizeWidths(table);
52011         var col = this.cellData.col;
52012         var nw = this.colWidths[col] * 0.8;
52013         if (nw < 5) {
52014             return;
52015         }
52016         var otherAdd = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
52017         this.colWidths.forEach(function(w,i) {
52018             if (i == col) {
52019                  this.colWidths[i] = nw;
52020                 return;
52021             }
52022             if (typeof(this.colWidths[i]) == 'undefined') {
52023                 this.colWidths[i] = otherAdd;
52024             } else {
52025                 this.colWidths[i] += otherAdd;
52026             }
52027         }, this);
52028         this.updateWidths(table);
52029          
52030     },
52031     growColumn : function()
52032     {
52033         var table = this.toTableArray();
52034         this.normalizeWidths(table);
52035         var col = this.cellData.col;
52036         var nw = this.colWidths[col] * 1.2;
52037         if (nw > 90) {
52038             return;
52039         }
52040         var otherSub = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
52041         this.colWidths.forEach(function(w,i) {
52042             if (i == col) {
52043                 this.colWidths[i] = nw;
52044                 return;
52045             }
52046             if (typeof(this.colWidths[i]) == 'undefined') {
52047                 this.colWidths[i] = otherSub;
52048             } else {
52049                 this.colWidths[i] -= otherSub;
52050             }
52051             
52052         }, this);
52053         this.updateWidths(table);
52054          
52055     },
52056     deleteRow : function()
52057     {
52058         // delete this rows 'tr'
52059         // if any of the cells in this row have a rowspan > 1 && row!= this row..
52060         // then reduce the rowspan.
52061         var table = this.toTableArray();
52062         // this.cellData.row;
52063         for (var i =0;i< table[this.cellData.row].length ; i++) {
52064             var c = table[this.cellData.row][i];
52065             if (c.row != this.cellData.row) {
52066                 
52067                 c.rowspan--;
52068                 c.cell.setAttribute('rowspan', c.rowspan);
52069                 continue;
52070             }
52071             if (c.rowspan > 1) {
52072                 c.rowspan--;
52073                 c.cell.setAttribute('rowspan', c.rowspan);
52074             }
52075         }
52076         table.splice(this.cellData.row,1);
52077         this.redrawAllCells(table);
52078         
52079     },
52080     deleteColumn : function()
52081     {
52082         var table = this.toTableArray();
52083         
52084         for (var i =0;i< table.length ; i++) {
52085             var c = table[i][this.cellData.col];
52086             if (c.col != this.cellData.col) {
52087                 table[i][this.cellData.col].colspan--;
52088             } else if (c.colspan > 1) {
52089                 c.colspan--;
52090                 c.cell.setAttribute('colspan', c.colspan);
52091             }
52092             table[i].splice(this.cellData.col,1);
52093         }
52094         
52095         this.redrawAllCells(table);
52096     }
52097     
52098     
52099     
52100     
52101 })
52102
52103 //<script type="text/javascript">
52104
52105 /*
52106  * Based  Ext JS Library 1.1.1
52107  * Copyright(c) 2006-2007, Ext JS, LLC.
52108  * LGPL
52109  *
52110  */
52111  
52112 /**
52113  * @class Roo.HtmlEditorCore
52114  * @extends Roo.Component
52115  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
52116  *
52117  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
52118  */
52119
52120 Roo.HtmlEditorCore = function(config){
52121     
52122     
52123     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
52124     
52125     
52126     this.addEvents({
52127         /**
52128          * @event initialize
52129          * Fires when the editor is fully initialized (including the iframe)
52130          * @param {Roo.HtmlEditorCore} this
52131          */
52132         initialize: true,
52133         /**
52134          * @event activate
52135          * Fires when the editor is first receives the focus. Any insertion must wait
52136          * until after this event.
52137          * @param {Roo.HtmlEditorCore} this
52138          */
52139         activate: true,
52140          /**
52141          * @event beforesync
52142          * Fires before the textarea is updated with content from the editor iframe. Return false
52143          * to cancel the sync.
52144          * @param {Roo.HtmlEditorCore} this
52145          * @param {String} html
52146          */
52147         beforesync: true,
52148          /**
52149          * @event beforepush
52150          * Fires before the iframe editor is updated with content from the textarea. Return false
52151          * to cancel the push.
52152          * @param {Roo.HtmlEditorCore} this
52153          * @param {String} html
52154          */
52155         beforepush: true,
52156          /**
52157          * @event sync
52158          * Fires when the textarea is updated with content from the editor iframe.
52159          * @param {Roo.HtmlEditorCore} this
52160          * @param {String} html
52161          */
52162         sync: true,
52163          /**
52164          * @event push
52165          * Fires when the iframe editor is updated with content from the textarea.
52166          * @param {Roo.HtmlEditorCore} this
52167          * @param {String} html
52168          */
52169         push: true,
52170         
52171         /**
52172          * @event editorevent
52173          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
52174          * @param {Roo.HtmlEditorCore} this
52175          */
52176         editorevent: true 
52177         
52178         
52179     });
52180     
52181     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
52182     
52183     // defaults : white / black...
52184     this.applyBlacklists();
52185     
52186     
52187     
52188 };
52189
52190
52191 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
52192
52193
52194      /**
52195      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
52196      */
52197     
52198     owner : false,
52199     
52200      /**
52201      * @cfg {String} css styling for resizing. (used on bootstrap only)
52202      */
52203     resize : false,
52204      /**
52205      * @cfg {Number} height (in pixels)
52206      */   
52207     height: 300,
52208    /**
52209      * @cfg {Number} width (in pixels)
52210      */   
52211     width: 500,
52212      /**
52213      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
52214      *         if you are doing an email editor, this probably needs disabling, it's designed
52215      */
52216     autoClean: true,
52217     
52218     /**
52219      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
52220      */
52221     enableBlocks : true,
52222     /**
52223      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
52224      * 
52225      */
52226     stylesheets: false,
52227      /**
52228      * @cfg {String} language default en - language of text (usefull for rtl languages)
52229      * 
52230      */
52231     language: 'en',
52232     
52233     /**
52234      * @cfg {boolean} allowComments - default false - allow comments in HTML source
52235      *          - by default they are stripped - if you are editing email you may need this.
52236      */
52237     allowComments: false,
52238     // id of frame..
52239     frameId: false,
52240     
52241     // private properties
52242     validationEvent : false,
52243     deferHeight: true,
52244     initialized : false,
52245     activated : false,
52246     sourceEditMode : false,
52247     onFocus : Roo.emptyFn,
52248     iframePad:3,
52249     hideMode:'offsets',
52250     
52251     clearUp: true,
52252     
52253     // blacklist + whitelisted elements..
52254     black: false,
52255     white: false,
52256      
52257     bodyCls : '',
52258
52259     
52260     undoManager : false,
52261     /**
52262      * Protected method that will not generally be called directly. It
52263      * is called when the editor initializes the iframe with HTML contents. Override this method if you
52264      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
52265      */
52266     getDocMarkup : function(){
52267         // body styles..
52268         var st = '';
52269         
52270         // inherit styels from page...?? 
52271         if (this.stylesheets === false) {
52272             
52273             Roo.get(document.head).select('style').each(function(node) {
52274                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
52275             });
52276             
52277             Roo.get(document.head).select('link').each(function(node) { 
52278                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
52279             });
52280             
52281         } else if (!this.stylesheets.length) {
52282                 // simple..
52283                 st = '<style type="text/css">' +
52284                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
52285                    '</style>';
52286         } else {
52287             for (var i in this.stylesheets) {
52288                 if (typeof(this.stylesheets[i]) != 'string') {
52289                     continue;
52290                 }
52291                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
52292             }
52293             
52294         }
52295         
52296         st +=  '<style type="text/css">' +
52297             'IMG { cursor: pointer } ' +
52298         '</style>';
52299         
52300         st += '<meta name="google" content="notranslate">';
52301         
52302         var cls = 'notranslate roo-htmleditor-body';
52303         
52304         if(this.bodyCls.length){
52305             cls += ' ' + this.bodyCls;
52306         }
52307         
52308         return '<html  class="notranslate" translate="no"><head>' + st  +
52309             //<style type="text/css">' +
52310             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
52311             //'</style>' +
52312             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
52313     },
52314
52315     // private
52316     onRender : function(ct, position)
52317     {
52318         var _t = this;
52319         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
52320         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
52321         
52322         
52323         this.el.dom.style.border = '0 none';
52324         this.el.dom.setAttribute('tabIndex', -1);
52325         this.el.addClass('x-hidden hide');
52326         
52327         
52328         
52329         if(Roo.isIE){ // fix IE 1px bogus margin
52330             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
52331         }
52332        
52333         
52334         this.frameId = Roo.id();
52335         
52336         var ifcfg = {
52337             tag: 'iframe',
52338             cls: 'form-control', // bootstrap..
52339             id: this.frameId,
52340             name: this.frameId,
52341             frameBorder : 'no',
52342             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
52343         };
52344         if (this.resize) {
52345             ifcfg.style = { resize : this.resize };
52346         }
52347         
52348         var iframe = this.owner.wrap.createChild(ifcfg, this.el); 
52349         
52350         
52351         this.iframe = iframe.dom;
52352
52353         this.assignDocWin();
52354         
52355         this.doc.designMode = 'on';
52356        
52357         this.doc.open();
52358         this.doc.write(this.getDocMarkup());
52359         this.doc.close();
52360
52361         
52362         var task = { // must defer to wait for browser to be ready
52363             run : function(){
52364                 //console.log("run task?" + this.doc.readyState);
52365                 this.assignDocWin();
52366                 if(this.doc.body || this.doc.readyState == 'complete'){
52367                     try {
52368                         this.doc.designMode="on";
52369                         
52370                     } catch (e) {
52371                         return;
52372                     }
52373                     Roo.TaskMgr.stop(task);
52374                     this.initEditor.defer(10, this);
52375                 }
52376             },
52377             interval : 10,
52378             duration: 10000,
52379             scope: this
52380         };
52381         Roo.TaskMgr.start(task);
52382
52383     },
52384
52385     // private
52386     onResize : function(w, h)
52387     {
52388          Roo.log('resize: ' +w + ',' + h );
52389         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
52390         if(!this.iframe){
52391             return;
52392         }
52393         if(typeof w == 'number'){
52394             
52395             this.iframe.style.width = w + 'px';
52396         }
52397         if(typeof h == 'number'){
52398             
52399             this.iframe.style.height = h + 'px';
52400             if(this.doc){
52401                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
52402             }
52403         }
52404         
52405     },
52406
52407     /**
52408      * Toggles the editor between standard and source edit mode.
52409      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
52410      */
52411     toggleSourceEdit : function(sourceEditMode){
52412         
52413         this.sourceEditMode = sourceEditMode === true;
52414         
52415         if(this.sourceEditMode){
52416  
52417             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
52418             
52419         }else{
52420             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
52421             //this.iframe.className = '';
52422             this.deferFocus();
52423         }
52424         //this.setSize(this.owner.wrap.getSize());
52425         //this.fireEvent('editmodechange', this, this.sourceEditMode);
52426     },
52427
52428     
52429   
52430
52431     /**
52432      * Protected method that will not generally be called directly. If you need/want
52433      * custom HTML cleanup, this is the method you should override.
52434      * @param {String} html The HTML to be cleaned
52435      * return {String} The cleaned HTML
52436      */
52437     cleanHtml : function(html)
52438     {
52439         html = String(html);
52440         if(html.length > 5){
52441             if(Roo.isSafari){ // strip safari nonsense
52442                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
52443             }
52444         }
52445         if(html == '&nbsp;'){
52446             html = '';
52447         }
52448         return html;
52449     },
52450
52451     /**
52452      * HTML Editor -> Textarea
52453      * Protected method that will not generally be called directly. Syncs the contents
52454      * of the editor iframe with the textarea.
52455      */
52456     syncValue : function()
52457     {
52458         //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
52459         if(this.initialized){
52460             
52461             if (this.undoManager) {
52462                 this.undoManager.addEvent();
52463             }
52464
52465             
52466             var bd = (this.doc.body || this.doc.documentElement);
52467            
52468             
52469             var sel = this.win.getSelection();
52470             
52471             var div = document.createElement('div');
52472             div.innerHTML = bd.innerHTML;
52473             var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
52474             if (gtx.length > 0) {
52475                 var rm = gtx.item(0).parentNode;
52476                 rm.parentNode.removeChild(rm);
52477             }
52478             
52479            
52480             if (this.enableBlocks) {
52481                 Array.from(bd.getElementsByTagName('img')).forEach(function(img) {
52482                     var fig = img.closest('figure');
52483                     if (fig) {
52484                         var bf = new Roo.htmleditor.BlockFigure({
52485                             node : fig
52486                         });
52487                         bf.updateElement();
52488                     }
52489                     
52490                 });
52491                 new Roo.htmleditor.FilterBlock({ node : div });
52492             }
52493             
52494             var html = div.innerHTML;
52495             
52496             //?? tidy?
52497             if (this.autoClean) {
52498                 new Roo.htmleditor.FilterBlack({ node : div, tag : this.black});
52499                 new Roo.htmleditor.FilterAttributes({
52500                     node : div,
52501                     attrib_white : [
52502                             'href',
52503                             'src',
52504                             'name',
52505                             'align',
52506                             'colspan',
52507                             'rowspan',
52508                             'data-display',
52509                             'data-caption-display',
52510                             'data-width',
52511                             'data-caption',
52512                             'start' ,
52513                             'style',
52514                             // youtube embed.
52515                             'class',
52516                             'allowfullscreen',
52517                             'frameborder',
52518                             'width',
52519                             'height',
52520                             'alt'
52521                             ],
52522                     attrib_clean : ['href', 'src' ] 
52523                 });
52524                 new Roo.htmleditor.FilterEmpty({ node : div});
52525                 
52526                 var tidy = new Roo.htmleditor.TidySerializer({
52527                     inner:  true
52528                 });
52529                 html  = tidy.serialize(div);
52530                 
52531             }
52532             
52533             
52534             if(Roo.isSafari){
52535                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
52536                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
52537                 if(m && m[1]){
52538                     html = '<div style="'+m[0]+'">' + html + '</div>';
52539                 }
52540             }
52541             html = this.cleanHtml(html);
52542             // fix up the special chars.. normaly like back quotes in word...
52543             // however we do not want to do this with chinese..
52544             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
52545                 
52546                 var cc = match.charCodeAt();
52547
52548                 // Get the character value, handling surrogate pairs
52549                 if (match.length == 2) {
52550                     // It's a surrogate pair, calculate the Unicode code point
52551                     var high = match.charCodeAt(0) - 0xD800;
52552                     var low  = match.charCodeAt(1) - 0xDC00;
52553                     cc = (high * 0x400) + low + 0x10000;
52554                 }  else if (
52555                     (cc >= 0x4E00 && cc < 0xA000 ) ||
52556                     (cc >= 0x3400 && cc < 0x4E00 ) ||
52557                     (cc >= 0xf900 && cc < 0xfb00 )
52558                 ) {
52559                         return match;
52560                 }  
52561          
52562                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
52563                 return "&#" + cc + ";";
52564                 
52565                 
52566             });
52567             
52568             
52569              
52570             if(this.owner.fireEvent('beforesync', this, html) !== false){
52571                 this.el.dom.value = html;
52572                 this.owner.fireEvent('sync', this, html);
52573             }
52574         }
52575     },
52576
52577     /**
52578      * TEXTAREA -> EDITABLE
52579      * Protected method that will not generally be called directly. Pushes the value of the textarea
52580      * into the iframe editor.
52581      */
52582     pushValue : function()
52583     {
52584         //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
52585         if(this.initialized){
52586             var v = this.el.dom.value.trim();
52587             
52588             
52589             if(this.owner.fireEvent('beforepush', this, v) !== false){
52590                 var d = (this.doc.body || this.doc.documentElement);
52591                 d.innerHTML = v;
52592                  
52593                 this.el.dom.value = d.innerHTML;
52594                 this.owner.fireEvent('push', this, v);
52595             }
52596             if (this.autoClean) {
52597                 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
52598                 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
52599             }
52600             if (this.enableBlocks) {
52601                 Roo.htmleditor.Block.initAll(this.doc.body);
52602             }
52603             
52604             this.updateLanguage();
52605             
52606             var lc = this.doc.body.lastChild;
52607             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
52608                 // add an extra line at the end.
52609                 this.doc.body.appendChild(this.doc.createElement('br'));
52610             }
52611             
52612             
52613         }
52614     },
52615
52616     // private
52617     deferFocus : function(){
52618         this.focus.defer(10, this);
52619     },
52620
52621     // doc'ed in Field
52622     focus : function(){
52623         if(this.win && !this.sourceEditMode){
52624             this.win.focus();
52625         }else{
52626             this.el.focus();
52627         }
52628     },
52629     
52630     assignDocWin: function()
52631     {
52632         var iframe = this.iframe;
52633         
52634          if(Roo.isIE){
52635             this.doc = iframe.contentWindow.document;
52636             this.win = iframe.contentWindow;
52637         } else {
52638 //            if (!Roo.get(this.frameId)) {
52639 //                return;
52640 //            }
52641 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
52642 //            this.win = Roo.get(this.frameId).dom.contentWindow;
52643             
52644             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
52645                 return;
52646             }
52647             
52648             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
52649             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
52650         }
52651     },
52652     
52653     // private
52654     initEditor : function(){
52655         //console.log("INIT EDITOR");
52656         this.assignDocWin();
52657         
52658         
52659         
52660         this.doc.designMode="on";
52661         this.doc.open();
52662         this.doc.write(this.getDocMarkup());
52663         this.doc.close();
52664         
52665         var dbody = (this.doc.body || this.doc.documentElement);
52666         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
52667         // this copies styles from the containing element into thsi one..
52668         // not sure why we need all of this..
52669         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
52670         
52671         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
52672         //ss['background-attachment'] = 'fixed'; // w3c
52673         dbody.bgProperties = 'fixed'; // ie
52674         dbody.setAttribute("translate", "no");
52675         
52676         //Roo.DomHelper.applyStyles(dbody, ss);
52677         Roo.EventManager.on(this.doc, {
52678              
52679             'mouseup': this.onEditorEvent,
52680             'dblclick': this.onEditorEvent,
52681             'click': this.onEditorEvent,
52682             'keyup': this.onEditorEvent,
52683             
52684             buffer:100,
52685             scope: this
52686         });
52687         Roo.EventManager.on(this.doc, {
52688             'paste': this.onPasteEvent,
52689             scope : this
52690         });
52691         if(Roo.isGecko){
52692             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
52693         }
52694         //??? needed???
52695         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
52696             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
52697         }
52698         this.initialized = true;
52699
52700         
52701         // initialize special key events - enter
52702         new Roo.htmleditor.KeyEnter({core : this});
52703         
52704          
52705         
52706         this.owner.fireEvent('initialize', this);
52707         this.pushValue();
52708     },
52709     // this is to prevent a href clicks resulting in a redirect?
52710    
52711     onPasteEvent : function(e,v)
52712     {
52713         // I think we better assume paste is going to be a dirty load of rubish from word..
52714         
52715         // even pasting into a 'email version' of this widget will have to clean up that mess.
52716         var cd = (e.browserEvent.clipboardData || window.clipboardData);
52717         
52718         // check what type of paste - if it's an image, then handle it differently.
52719         if (cd.files && cd.files.length > 0 && cd.types.indexOf('text/html') < 0) {
52720             // pasting images? 
52721             var urlAPI = (window.createObjectURL && window) || 
52722                 (window.URL && URL.revokeObjectURL && URL) || 
52723                 (window.webkitURL && webkitURL);
52724             
52725             var r = new FileReader();
52726             var t = this;
52727             r.addEventListener('load',function()
52728             {
52729                 
52730                 var d = (new DOMParser().parseFromString('<img src="' + r.result+ '">', 'text/html')).body;
52731                 // is insert asycn?
52732                 if (t.enableBlocks) {
52733                     
52734                     Array.from(d.getElementsByTagName('img')).forEach(function(img) {
52735                         if (img.closest('figure')) { // assume!! that it's aready
52736                             return;
52737                         }
52738                         var fig  = new Roo.htmleditor.BlockFigure({
52739                             image_src  : img.src
52740                         });
52741                         fig.updateElement(img); // replace it..
52742                         
52743                     });
52744                 }
52745                 t.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
52746                 t.owner.fireEvent('paste', this);
52747             });
52748             r.readAsDataURL(cd.files[0]);
52749             
52750             e.preventDefault();
52751             
52752             return false;
52753         }
52754         if (cd.types.indexOf('text/html') < 0 ) {
52755             return false;
52756         }
52757         var images = [];
52758         var html = cd.getData('text/html'); // clipboard event
52759         if (cd.types.indexOf('text/rtf') > -1) {
52760             var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
52761             images = parser.doc ? parser.doc.getElementsByType('pict') : [];
52762         }
52763         // Roo.log(images);
52764         // Roo.log(imgs);
52765         // fixme..
52766         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
52767                        .map(function(g) { return g.toDataURL(); })
52768                        .filter(function(g) { return g != 'about:blank'; });
52769         
52770         //Roo.log(html);
52771         html = this.cleanWordChars(html);
52772         
52773         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
52774         
52775         
52776         var sn = this.getParentElement();
52777         // check if d contains a table, and prevent nesting??
52778         //Roo.log(d.getElementsByTagName('table'));
52779         //Roo.log(sn);
52780         //Roo.log(sn.closest('table'));
52781         if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
52782             e.preventDefault();
52783             this.insertAtCursor("You can not nest tables");
52784             //Roo.log("prevent?"); // fixme - 
52785             return false;
52786         }
52787         
52788         
52789         
52790         if (images.length > 0) {
52791             // replace all v:imagedata - with img.
52792             var ar = Array.from(d.getElementsByTagName('v:imagedata'));
52793             Roo.each(ar, function(node) {
52794                 node.parentNode.insertBefore(d.ownerDocument.createElement('img'), node );
52795                 node.parentNode.removeChild(node);
52796             });
52797             
52798             
52799             Roo.each(d.getElementsByTagName('img'), function(img, i) {
52800                 img.setAttribute('src', images[i]);
52801             });
52802         }
52803         if (this.autoClean) {
52804             new Roo.htmleditor.FilterWord({ node : d });
52805             
52806             new Roo.htmleditor.FilterStyleToTag({ node : d });
52807             new Roo.htmleditor.FilterAttributes({
52808                 node : d,
52809                 attrib_white : [
52810                     'href',
52811                     'src',
52812                     'name',
52813                     'align',
52814                     'colspan',
52815                     'rowspan' 
52816                 /*  THESE ARE NOT ALLWOED FOR PASTE
52817                  *    'data-display',
52818                     'data-caption-display',
52819                     'data-width',
52820                     'data-caption',
52821                     'start' ,
52822                     'style',
52823                     // youtube embed.
52824                     'class',
52825                     'allowfullscreen',
52826                     'frameborder',
52827                     'width',
52828                     'height',
52829                     'alt'
52830                     */
52831                     ],
52832                 attrib_clean : ['href', 'src' ] 
52833             });
52834             new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
52835             // should be fonts..
52836             new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', ':' ]} );
52837             new Roo.htmleditor.FilterParagraph({ node : d });
52838             new Roo.htmleditor.FilterHashLink({node : d});
52839             new Roo.htmleditor.FilterSpan({ node : d });
52840             new Roo.htmleditor.FilterLongBr({ node : d });
52841             new Roo.htmleditor.FilterComment({ node : d });
52842             new Roo.htmleditor.FilterEmpty({ node : d});
52843             
52844             
52845         }
52846         if (this.enableBlocks) {
52847                 
52848             Array.from(d.getElementsByTagName('img')).forEach(function(img) {
52849                 if (img.closest('figure')) { // assume!! that it's aready
52850                     return;
52851                 }
52852                 var fig  = new Roo.htmleditor.BlockFigure({
52853                     image_src  : img.src
52854                 });
52855                 fig.updateElement(img); // replace it..
52856                 
52857             });
52858         }
52859         
52860         
52861         this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
52862         if (this.enableBlocks) {
52863             Roo.htmleditor.Block.initAll(this.doc.body);
52864         }
52865          
52866         
52867         e.preventDefault();
52868         this.owner.fireEvent('paste', this);
52869         return false;
52870         // default behaveiour should be our local cleanup paste? (optional?)
52871         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
52872         //this.owner.fireEvent('paste', e, v);
52873     },
52874     // private
52875     onDestroy : function(){
52876         
52877         
52878         
52879         if(this.rendered){
52880             
52881             //for (var i =0; i < this.toolbars.length;i++) {
52882             //    // fixme - ask toolbars for heights?
52883             //    this.toolbars[i].onDestroy();
52884            // }
52885             
52886             //this.wrap.dom.innerHTML = '';
52887             //this.wrap.remove();
52888         }
52889     },
52890
52891     // private
52892     onFirstFocus : function(){
52893         
52894         this.assignDocWin();
52895         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
52896         
52897         this.activated = true;
52898          
52899     
52900         if(Roo.isGecko){ // prevent silly gecko errors
52901             this.win.focus();
52902             var s = this.win.getSelection();
52903             if(!s.focusNode || s.focusNode.nodeType != 3){
52904                 var r = s.getRangeAt(0);
52905                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
52906                 r.collapse(true);
52907                 this.deferFocus();
52908             }
52909             try{
52910                 this.execCmd('useCSS', true);
52911                 this.execCmd('styleWithCSS', false);
52912             }catch(e){}
52913         }
52914         this.owner.fireEvent('activate', this);
52915     },
52916
52917     // private
52918     adjustFont: function(btn){
52919         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
52920         //if(Roo.isSafari){ // safari
52921         //    adjust *= 2;
52922        // }
52923         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
52924         if(Roo.isSafari){ // safari
52925             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
52926             v =  (v < 10) ? 10 : v;
52927             v =  (v > 48) ? 48 : v;
52928             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
52929             
52930         }
52931         
52932         
52933         v = Math.max(1, v+adjust);
52934         
52935         this.execCmd('FontSize', v  );
52936     },
52937
52938     onEditorEvent : function(e)
52939     {
52940          
52941         
52942         if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
52943             return; // we do not handle this.. (undo manager does..)
52944         }
52945         // clicking a 'block'?
52946         
52947         // in theory this detects if the last element is not a br, then we try and do that.
52948         // its so clicking in space at bottom triggers adding a br and moving the cursor.
52949         if (e &&
52950             e.target.nodeName == 'BODY' &&
52951             e.type == "mouseup" &&
52952             this.doc.body.lastChild
52953            ) {
52954             var lc = this.doc.body.lastChild;
52955             // gtx-trans is google translate plugin adding crap.
52956             while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
52957                 lc = lc.previousSibling;
52958             }
52959             if (lc.nodeType == 1 && lc.nodeName != 'BR') {
52960             // if last element is <BR> - then dont do anything.
52961             
52962                 var ns = this.doc.createElement('br');
52963                 this.doc.body.appendChild(ns);
52964                 range = this.doc.createRange();
52965                 range.setStartAfter(ns);
52966                 range.collapse(true);
52967                 var sel = this.win.getSelection();
52968                 sel.removeAllRanges();
52969                 sel.addRange(range);
52970             }
52971         }
52972         
52973         
52974         
52975         this.fireEditorEvent(e);
52976       //  this.updateToolbar();
52977         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
52978     },
52979     
52980     fireEditorEvent: function(e)
52981     {
52982         this.owner.fireEvent('editorevent', this, e);
52983     },
52984
52985     insertTag : function(tg)
52986     {
52987         // could be a bit smarter... -> wrap the current selected tRoo..
52988         if (tg.toLowerCase() == 'span' ||
52989             tg.toLowerCase() == 'code' ||
52990             tg.toLowerCase() == 'sup' ||
52991             tg.toLowerCase() == 'sub' 
52992             ) {
52993             
52994             range = this.createRange(this.getSelection());
52995             var wrappingNode = this.doc.createElement(tg.toLowerCase());
52996             wrappingNode.appendChild(range.extractContents());
52997             range.insertNode(wrappingNode);
52998
52999             return;
53000             
53001             
53002             
53003         }
53004         this.execCmd("formatblock",   tg);
53005         this.undoManager.addEvent(); 
53006     },
53007     
53008     insertText : function(txt)
53009     {
53010         
53011         
53012         var range = this.createRange();
53013         range.deleteContents();
53014                //alert(Sender.getAttribute('label'));
53015                
53016         range.insertNode(this.doc.createTextNode(txt));
53017         this.undoManager.addEvent();
53018     } ,
53019     
53020      
53021
53022     /**
53023      * Executes a Midas editor command on the editor document and performs necessary focus and
53024      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
53025      * @param {String} cmd The Midas command
53026      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
53027      */
53028     relayCmd : function(cmd, value)
53029     {
53030         
53031         switch (cmd) {
53032             case 'justifyleft':
53033             case 'justifyright':
53034             case 'justifycenter':
53035                 // if we are in a cell, then we will adjust the
53036                 var n = this.getParentElement();
53037                 var td = n.closest('td');
53038                 if (td) {
53039                     var bl = Roo.htmleditor.Block.factory(td);
53040                     bl.textAlign = cmd.replace('justify','');
53041                     bl.updateElement();
53042                     this.owner.fireEvent('editorevent', this);
53043                     return;
53044                 }
53045                 this.execCmd('styleWithCSS', true); // 
53046                 break;
53047             case 'bold':
53048             case 'italic':
53049             case 'underline':                
53050                 // if there is no selection, then we insert, and set the curson inside it..
53051                 this.execCmd('styleWithCSS', false); 
53052                 break;
53053                 
53054         
53055             default:
53056                 break;
53057         }
53058         
53059         
53060         this.win.focus();
53061         this.execCmd(cmd, value);
53062         this.owner.fireEvent('editorevent', this);
53063         //this.updateToolbar();
53064         this.owner.deferFocus();
53065     },
53066
53067     /**
53068      * Executes a Midas editor command directly on the editor document.
53069      * For visual commands, you should use {@link #relayCmd} instead.
53070      * <b>This should only be called after the editor is initialized.</b>
53071      * @param {String} cmd The Midas command
53072      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
53073      */
53074     execCmd : function(cmd, value){
53075         this.doc.execCommand(cmd, false, value === undefined ? null : value);
53076         this.syncValue();
53077     },
53078  
53079  
53080    
53081     /**
53082      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
53083      * to insert tRoo.
53084      * @param {String} text | dom node.. 
53085      */
53086     insertAtCursor : function(text)
53087     {
53088         
53089         if(!this.activated){
53090             return;
53091         }
53092          
53093         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
53094             this.win.focus();
53095             
53096             
53097             // from jquery ui (MIT licenced)
53098             var range, node;
53099             var win = this.win;
53100             
53101             if (win.getSelection && win.getSelection().getRangeAt) {
53102                 
53103                 // delete the existing?
53104                 
53105                 this.createRange(this.getSelection()).deleteContents();
53106                 range = win.getSelection().getRangeAt(0);
53107                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
53108                 range.insertNode(node);
53109                 range = range.cloneRange();
53110                 range.collapse(false);
53111                  
53112                 win.getSelection().removeAllRanges();
53113                 win.getSelection().addRange(range);
53114                 
53115                 
53116                 
53117             } else if (win.document.selection && win.document.selection.createRange) {
53118                 // no firefox support
53119                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
53120                 win.document.selection.createRange().pasteHTML(txt);
53121             
53122             } else {
53123                 // no firefox support
53124                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
53125                 this.execCmd('InsertHTML', txt);
53126             } 
53127             this.syncValue();
53128             
53129             this.deferFocus();
53130         }
53131     },
53132  // private
53133     mozKeyPress : function(e){
53134         if(e.ctrlKey){
53135             var c = e.getCharCode(), cmd;
53136           
53137             if(c > 0){
53138                 c = String.fromCharCode(c).toLowerCase();
53139                 switch(c){
53140                     case 'b':
53141                         cmd = 'bold';
53142                         break;
53143                     case 'i':
53144                         cmd = 'italic';
53145                         break;
53146                     
53147                     case 'u':
53148                         cmd = 'underline';
53149                         break;
53150                     
53151                     //case 'v':
53152                       //  this.cleanUpPaste.defer(100, this);
53153                       //  return;
53154                         
53155                 }
53156                 if(cmd){
53157                     
53158                     this.relayCmd(cmd);
53159                     //this.win.focus();
53160                     //this.execCmd(cmd);
53161                     //this.deferFocus();
53162                     e.preventDefault();
53163                 }
53164                 
53165             }
53166         }
53167     },
53168
53169     // private
53170     fixKeys : function(){ // load time branching for fastest keydown performance
53171         
53172         
53173         if(Roo.isIE){
53174             return function(e){
53175                 var k = e.getKey(), r;
53176                 if(k == e.TAB){
53177                     e.stopEvent();
53178                     r = this.doc.selection.createRange();
53179                     if(r){
53180                         r.collapse(true);
53181                         r.pasteHTML('&#160;&#160;&#160;&#160;');
53182                         this.deferFocus();
53183                     }
53184                     return;
53185                 }
53186                 /// this is handled by Roo.htmleditor.KeyEnter
53187                  /*
53188                 if(k == e.ENTER){
53189                     r = this.doc.selection.createRange();
53190                     if(r){
53191                         var target = r.parentElement();
53192                         if(!target || target.tagName.toLowerCase() != 'li'){
53193                             e.stopEvent();
53194                             r.pasteHTML('<br/>');
53195                             r.collapse(false);
53196                             r.select();
53197                         }
53198                     }
53199                 }
53200                 */
53201                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
53202                 //    this.cleanUpPaste.defer(100, this);
53203                 //    return;
53204                 //}
53205                 
53206                 
53207             };
53208         }else if(Roo.isOpera){
53209             return function(e){
53210                 var k = e.getKey();
53211                 if(k == e.TAB){
53212                     e.stopEvent();
53213                     this.win.focus();
53214                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
53215                     this.deferFocus();
53216                 }
53217                
53218                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
53219                 //    this.cleanUpPaste.defer(100, this);
53220                  //   return;
53221                 //}
53222                 
53223             };
53224         }else if(Roo.isSafari){
53225             return function(e){
53226                 var k = e.getKey();
53227                 
53228                 if(k == e.TAB){
53229                     e.stopEvent();
53230                     this.execCmd('InsertText','\t');
53231                     this.deferFocus();
53232                     return;
53233                 }
53234                  this.mozKeyPress(e);
53235                 
53236                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
53237                  //   this.cleanUpPaste.defer(100, this);
53238                  //   return;
53239                // }
53240                 
53241              };
53242         }
53243     }(),
53244     
53245     getAllAncestors: function()
53246     {
53247         var p = this.getSelectedNode();
53248         var a = [];
53249         if (!p) {
53250             a.push(p); // push blank onto stack..
53251             p = this.getParentElement();
53252         }
53253         
53254         
53255         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
53256             a.push(p);
53257             p = p.parentNode;
53258         }
53259         a.push(this.doc.body);
53260         return a;
53261     },
53262     lastSel : false,
53263     lastSelNode : false,
53264     
53265     
53266     getSelection : function() 
53267     {
53268         this.assignDocWin();
53269         return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
53270     },
53271     /**
53272      * Select a dom node
53273      * @param {DomElement} node the node to select
53274      */
53275     selectNode : function(node, collapse)
53276     {
53277         var nodeRange = node.ownerDocument.createRange();
53278         try {
53279             nodeRange.selectNode(node);
53280         } catch (e) {
53281             nodeRange.selectNodeContents(node);
53282         }
53283         if (collapse === true) {
53284             nodeRange.collapse(true);
53285         }
53286         //
53287         var s = this.win.getSelection();
53288         s.removeAllRanges();
53289         s.addRange(nodeRange);
53290     },
53291     
53292     getSelectedNode: function() 
53293     {
53294         // this may only work on Gecko!!!
53295         
53296         // should we cache this!!!!
53297         
53298          
53299          
53300         var range = this.createRange(this.getSelection()).cloneRange();
53301         
53302         if (Roo.isIE) {
53303             var parent = range.parentElement();
53304             while (true) {
53305                 var testRange = range.duplicate();
53306                 testRange.moveToElementText(parent);
53307                 if (testRange.inRange(range)) {
53308                     break;
53309                 }
53310                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
53311                     break;
53312                 }
53313                 parent = parent.parentElement;
53314             }
53315             return parent;
53316         }
53317         
53318         // is ancestor a text element.
53319         var ac =  range.commonAncestorContainer;
53320         if (ac.nodeType == 3) {
53321             ac = ac.parentNode;
53322         }
53323         
53324         var ar = ac.childNodes;
53325          
53326         var nodes = [];
53327         var other_nodes = [];
53328         var has_other_nodes = false;
53329         for (var i=0;i<ar.length;i++) {
53330             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
53331                 continue;
53332             }
53333             // fullly contained node.
53334             
53335             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
53336                 nodes.push(ar[i]);
53337                 continue;
53338             }
53339             
53340             // probably selected..
53341             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
53342                 other_nodes.push(ar[i]);
53343                 continue;
53344             }
53345             // outer..
53346             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
53347                 continue;
53348             }
53349             
53350             
53351             has_other_nodes = true;
53352         }
53353         if (!nodes.length && other_nodes.length) {
53354             nodes= other_nodes;
53355         }
53356         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
53357             return false;
53358         }
53359         
53360         return nodes[0];
53361     },
53362     
53363     
53364     createRange: function(sel)
53365     {
53366         // this has strange effects when using with 
53367         // top toolbar - not sure if it's a great idea.
53368         //this.editor.contentWindow.focus();
53369         if (typeof sel != "undefined") {
53370             try {
53371                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
53372             } catch(e) {
53373                 return this.doc.createRange();
53374             }
53375         } else {
53376             return this.doc.createRange();
53377         }
53378     },
53379     getParentElement: function()
53380     {
53381         
53382         this.assignDocWin();
53383         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
53384         
53385         var range = this.createRange(sel);
53386          
53387         try {
53388             var p = range.commonAncestorContainer;
53389             while (p.nodeType == 3) { // text node
53390                 p = p.parentNode;
53391             }
53392             return p;
53393         } catch (e) {
53394             return null;
53395         }
53396     
53397     },
53398     /***
53399      *
53400      * Range intersection.. the hard stuff...
53401      *  '-1' = before
53402      *  '0' = hits..
53403      *  '1' = after.
53404      *         [ -- selected range --- ]
53405      *   [fail]                        [fail]
53406      *
53407      *    basically..
53408      *      if end is before start or  hits it. fail.
53409      *      if start is after end or hits it fail.
53410      *
53411      *   if either hits (but other is outside. - then it's not 
53412      *   
53413      *    
53414      **/
53415     
53416     
53417     // @see http://www.thismuchiknow.co.uk/?p=64.
53418     rangeIntersectsNode : function(range, node)
53419     {
53420         var nodeRange = node.ownerDocument.createRange();
53421         try {
53422             nodeRange.selectNode(node);
53423         } catch (e) {
53424             nodeRange.selectNodeContents(node);
53425         }
53426     
53427         var rangeStartRange = range.cloneRange();
53428         rangeStartRange.collapse(true);
53429     
53430         var rangeEndRange = range.cloneRange();
53431         rangeEndRange.collapse(false);
53432     
53433         var nodeStartRange = nodeRange.cloneRange();
53434         nodeStartRange.collapse(true);
53435     
53436         var nodeEndRange = nodeRange.cloneRange();
53437         nodeEndRange.collapse(false);
53438     
53439         return rangeStartRange.compareBoundaryPoints(
53440                  Range.START_TO_START, nodeEndRange) == -1 &&
53441                rangeEndRange.compareBoundaryPoints(
53442                  Range.START_TO_START, nodeStartRange) == 1;
53443         
53444          
53445     },
53446     rangeCompareNode : function(range, node)
53447     {
53448         var nodeRange = node.ownerDocument.createRange();
53449         try {
53450             nodeRange.selectNode(node);
53451         } catch (e) {
53452             nodeRange.selectNodeContents(node);
53453         }
53454         
53455         
53456         range.collapse(true);
53457     
53458         nodeRange.collapse(true);
53459      
53460         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
53461         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
53462          
53463         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
53464         
53465         var nodeIsBefore   =  ss == 1;
53466         var nodeIsAfter    = ee == -1;
53467         
53468         if (nodeIsBefore && nodeIsAfter) {
53469             return 0; // outer
53470         }
53471         if (!nodeIsBefore && nodeIsAfter) {
53472             return 1; //right trailed.
53473         }
53474         
53475         if (nodeIsBefore && !nodeIsAfter) {
53476             return 2;  // left trailed.
53477         }
53478         // fully contined.
53479         return 3;
53480     },
53481  
53482     cleanWordChars : function(input) {// change the chars to hex code
53483         
53484        var swapCodes  = [ 
53485             [    8211, "&#8211;" ], 
53486             [    8212, "&#8212;" ], 
53487             [    8216,  "'" ],  
53488             [    8217, "'" ],  
53489             [    8220, '"' ],  
53490             [    8221, '"' ],  
53491             [    8226, "*" ],  
53492             [    8230, "..." ]
53493         ]; 
53494         var output = input;
53495         Roo.each(swapCodes, function(sw) { 
53496             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
53497             
53498             output = output.replace(swapper, sw[1]);
53499         });
53500         
53501         return output;
53502     },
53503     
53504      
53505     
53506         
53507     
53508     cleanUpChild : function (node)
53509     {
53510         
53511         new Roo.htmleditor.FilterComment({node : node});
53512         new Roo.htmleditor.FilterAttributes({
53513                 node : node,
53514                 attrib_black : this.ablack,
53515                 attrib_clean : this.aclean,
53516                 style_white : this.cwhite,
53517                 style_black : this.cblack
53518         });
53519         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
53520         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
53521          
53522         
53523     },
53524     
53525     /**
53526      * Clean up MS wordisms...
53527      * @deprecated - use filter directly
53528      */
53529     cleanWord : function(node)
53530     {
53531         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
53532         new Roo.htmleditor.FilterKeepChildren({node : node ? node : this.doc.body, tag : [ 'FONT', ':' ]} );
53533         
53534     },
53535    
53536     
53537     /**
53538
53539      * @deprecated - use filters
53540      */
53541     cleanTableWidths : function(node)
53542     {
53543         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
53544         
53545  
53546     },
53547     
53548      
53549         
53550     applyBlacklists : function()
53551     {
53552         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
53553         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
53554         
53555         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
53556         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
53557         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
53558         
53559         this.white = [];
53560         this.black = [];
53561         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
53562             if (b.indexOf(tag) > -1) {
53563                 return;
53564             }
53565             this.white.push(tag);
53566             
53567         }, this);
53568         
53569         Roo.each(w, function(tag) {
53570             if (b.indexOf(tag) > -1) {
53571                 return;
53572             }
53573             if (this.white.indexOf(tag) > -1) {
53574                 return;
53575             }
53576             this.white.push(tag);
53577             
53578         }, this);
53579         
53580         
53581         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
53582             if (w.indexOf(tag) > -1) {
53583                 return;
53584             }
53585             this.black.push(tag);
53586             
53587         }, this);
53588         
53589         Roo.each(b, function(tag) {
53590             if (w.indexOf(tag) > -1) {
53591                 return;
53592             }
53593             if (this.black.indexOf(tag) > -1) {
53594                 return;
53595             }
53596             this.black.push(tag);
53597             
53598         }, this);
53599         
53600         
53601         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
53602         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
53603         
53604         this.cwhite = [];
53605         this.cblack = [];
53606         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
53607             if (b.indexOf(tag) > -1) {
53608                 return;
53609             }
53610             this.cwhite.push(tag);
53611             
53612         }, this);
53613         
53614         Roo.each(w, function(tag) {
53615             if (b.indexOf(tag) > -1) {
53616                 return;
53617             }
53618             if (this.cwhite.indexOf(tag) > -1) {
53619                 return;
53620             }
53621             this.cwhite.push(tag);
53622             
53623         }, this);
53624         
53625         
53626         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
53627             if (w.indexOf(tag) > -1) {
53628                 return;
53629             }
53630             this.cblack.push(tag);
53631             
53632         }, this);
53633         
53634         Roo.each(b, function(tag) {
53635             if (w.indexOf(tag) > -1) {
53636                 return;
53637             }
53638             if (this.cblack.indexOf(tag) > -1) {
53639                 return;
53640             }
53641             this.cblack.push(tag);
53642             
53643         }, this);
53644     },
53645     
53646     setStylesheets : function(stylesheets)
53647     {
53648         if(typeof(stylesheets) == 'string'){
53649             Roo.get(this.iframe.contentDocument.head).createChild({
53650                 tag : 'link',
53651                 rel : 'stylesheet',
53652                 type : 'text/css',
53653                 href : stylesheets
53654             });
53655             
53656             return;
53657         }
53658         var _this = this;
53659      
53660         Roo.each(stylesheets, function(s) {
53661             if(!s.length){
53662                 return;
53663             }
53664             
53665             Roo.get(_this.iframe.contentDocument.head).createChild({
53666                 tag : 'link',
53667                 rel : 'stylesheet',
53668                 type : 'text/css',
53669                 href : s
53670             });
53671         });
53672
53673         
53674     },
53675     
53676     
53677     updateLanguage : function()
53678     {
53679         if (!this.iframe || !this.iframe.contentDocument) {
53680             return;
53681         }
53682         Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
53683     },
53684     
53685     
53686     removeStylesheets : function()
53687     {
53688         var _this = this;
53689         
53690         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
53691             s.remove();
53692         });
53693     },
53694     
53695     setStyle : function(style)
53696     {
53697         Roo.get(this.iframe.contentDocument.head).createChild({
53698             tag : 'style',
53699             type : 'text/css',
53700             html : style
53701         });
53702
53703         return;
53704     }
53705     
53706     // hide stuff that is not compatible
53707     /**
53708      * @event blur
53709      * @hide
53710      */
53711     /**
53712      * @event change
53713      * @hide
53714      */
53715     /**
53716      * @event focus
53717      * @hide
53718      */
53719     /**
53720      * @event specialkey
53721      * @hide
53722      */
53723     /**
53724      * @cfg {String} fieldClass @hide
53725      */
53726     /**
53727      * @cfg {String} focusClass @hide
53728      */
53729     /**
53730      * @cfg {String} autoCreate @hide
53731      */
53732     /**
53733      * @cfg {String} inputType @hide
53734      */
53735     /**
53736      * @cfg {String} invalidClass @hide
53737      */
53738     /**
53739      * @cfg {String} invalidText @hide
53740      */
53741     /**
53742      * @cfg {String} msgFx @hide
53743      */
53744     /**
53745      * @cfg {String} validateOnBlur @hide
53746      */
53747 });
53748
53749 Roo.HtmlEditorCore.white = [
53750         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
53751         
53752        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
53753        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
53754        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
53755        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
53756        'TABLE',   'UL',         'XMP', 
53757        
53758        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
53759       'THEAD',   'TR', 
53760      
53761       'DIR', 'MENU', 'OL', 'UL', 'DL',
53762        
53763       'EMBED',  'OBJECT'
53764 ];
53765
53766
53767 Roo.HtmlEditorCore.black = [
53768     //    'embed',  'object', // enable - backend responsiblity to clean thiese
53769         'APPLET', // 
53770         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
53771         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
53772         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
53773         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
53774         //'FONT' // CLEAN LATER..
53775         'COLGROUP', 'COL'   // messy tables.
53776         
53777         
53778 ];
53779 Roo.HtmlEditorCore.clean = [ // ?? needed???
53780      'SCRIPT', 'STYLE', 'TITLE', 'XML'
53781 ];
53782 Roo.HtmlEditorCore.tag_remove = [
53783     'FONT', 'TBODY'  
53784 ];
53785 // attributes..
53786
53787 Roo.HtmlEditorCore.ablack = [
53788     'on'
53789 ];
53790     
53791 Roo.HtmlEditorCore.aclean = [ 
53792     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
53793 ];
53794
53795 // protocols..
53796 Roo.HtmlEditorCore.pwhite= [
53797         'http',  'https',  'mailto'
53798 ];
53799
53800 // white listed style attributes.
53801 Roo.HtmlEditorCore.cwhite= [
53802       //  'text-align', /// default is to allow most things..
53803       
53804          
53805 //        'font-size'//??
53806 ];
53807
53808 // black listed style attributes.
53809 Roo.HtmlEditorCore.cblack= [
53810       //  'font-size' -- this can be set by the project 
53811 ];
53812
53813
53814
53815
53816     //<script type="text/javascript">
53817
53818 /*
53819  * Ext JS Library 1.1.1
53820  * Copyright(c) 2006-2007, Ext JS, LLC.
53821  * Licence LGPL
53822  * 
53823  */
53824  
53825  
53826 Roo.form.HtmlEditor = function(config){
53827     
53828     
53829     
53830     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
53831     
53832     if (!this.toolbars) {
53833         this.toolbars = [];
53834     }
53835     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
53836     
53837     
53838 };
53839
53840 /**
53841  * @class Roo.form.HtmlEditor
53842  * @extends Roo.form.Field
53843  * Provides a lightweight HTML Editor component.
53844  *
53845  * This has been tested on Fireforx / Chrome.. IE may not be so great..
53846  * 
53847  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
53848  * supported by this editor.</b><br/><br/>
53849  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
53850  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
53851  */
53852 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
53853     /**
53854      * @cfg {Boolean} clearUp
53855      */
53856     clearUp : true,
53857       /**
53858      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
53859      */
53860     toolbars : false,
53861    
53862      /**
53863      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
53864      *                        Roo.resizable.
53865      */
53866     resizable : false,
53867      /**
53868      * @cfg {Number} height (in pixels)
53869      */   
53870     height: 300,
53871    /**
53872      * @cfg {Number} width (in pixels)
53873      */   
53874     width: 500,
53875     
53876     /**
53877      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea  rootURL + '/roojs1/css/undoreset.css',   .
53878      * 
53879      */
53880     stylesheets: false,
53881     
53882     
53883      /**
53884      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
53885      * 
53886      */
53887     cblack: false,
53888     /**
53889      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
53890      * 
53891      */
53892     cwhite: false,
53893     
53894      /**
53895      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
53896      * 
53897      */
53898     black: false,
53899     /**
53900      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
53901      * 
53902      */
53903     white: false,
53904     /**
53905      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
53906      */
53907     allowComments: false,
53908     /**
53909      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
53910      */
53911     enableBlocks : true,
53912     
53913     /**
53914      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
53915      *         if you are doing an email editor, this probably needs disabling, it's designed
53916      */
53917     autoClean: true,
53918     /**
53919      * @cfg {string} bodyCls default '' default classes to add to body of editable area - usually undoreset is a good start..
53920      */
53921     bodyCls : '',
53922     /**
53923      * @cfg {String} language default en - language of text (usefull for rtl languages)
53924      * 
53925      */
53926     language: 'en',
53927     
53928      
53929     // id of frame..
53930     frameId: false,
53931     
53932     // private properties
53933     validationEvent : false,
53934     deferHeight: true,
53935     initialized : false,
53936     activated : false,
53937     
53938     onFocus : Roo.emptyFn,
53939     iframePad:3,
53940     hideMode:'offsets',
53941     
53942     actionMode : 'container', // defaults to hiding it...
53943     
53944     defaultAutoCreate : { // modified by initCompnoent..
53945         tag: "textarea",
53946         style:"width:500px;height:300px;",
53947         autocomplete: "new-password"
53948     },
53949
53950     // private
53951     initComponent : function(){
53952         this.addEvents({
53953             /**
53954              * @event initialize
53955              * Fires when the editor is fully initialized (including the iframe)
53956              * @param {HtmlEditor} this
53957              */
53958             initialize: true,
53959             /**
53960              * @event activate
53961              * Fires when the editor is first receives the focus. Any insertion must wait
53962              * until after this event.
53963              * @param {HtmlEditor} this
53964              */
53965             activate: true,
53966              /**
53967              * @event beforesync
53968              * Fires before the textarea is updated with content from the editor iframe. Return false
53969              * to cancel the sync.
53970              * @param {HtmlEditor} this
53971              * @param {String} html
53972              */
53973             beforesync: true,
53974              /**
53975              * @event beforepush
53976              * Fires before the iframe editor is updated with content from the textarea. Return false
53977              * to cancel the push.
53978              * @param {HtmlEditor} this
53979              * @param {String} html
53980              */
53981             beforepush: true,
53982              /**
53983              * @event sync
53984              * Fires when the textarea is updated with content from the editor iframe.
53985              * @param {HtmlEditor} this
53986              * @param {String} html
53987              */
53988             sync: true,
53989              /**
53990              * @event push
53991              * Fires when the iframe editor is updated with content from the textarea.
53992              * @param {HtmlEditor} this
53993              * @param {String} html
53994              */
53995             push: true,
53996              /**
53997              * @event editmodechange
53998              * Fires when the editor switches edit modes
53999              * @param {HtmlEditor} this
54000              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
54001              */
54002             editmodechange: true,
54003             /**
54004              * @event editorevent
54005              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
54006              * @param {HtmlEditor} this
54007              */
54008             editorevent: true,
54009             /**
54010              * @event firstfocus
54011              * Fires when on first focus - needed by toolbars..
54012              * @param {HtmlEditor} this
54013              */
54014             firstfocus: true,
54015             /**
54016              * @event autosave
54017              * Auto save the htmlEditor value as a file into Events
54018              * @param {HtmlEditor} this
54019              */
54020             autosave: true,
54021             /**
54022              * @event savedpreview
54023              * preview the saved version of htmlEditor
54024              * @param {HtmlEditor} this
54025              */
54026             savedpreview: true,
54027             
54028             /**
54029             * @event stylesheetsclick
54030             * Fires when press the Sytlesheets button
54031             * @param {Roo.HtmlEditorCore} this
54032             */
54033             stylesheetsclick: true,
54034             /**
54035             * @event paste
54036             * Fires when press user pastes into the editor
54037             * @param {Roo.HtmlEditorCore} this
54038             */
54039             paste: true 
54040             
54041         });
54042         this.defaultAutoCreate =  {
54043             tag: "textarea",
54044             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
54045             autocomplete: "new-password"
54046         };
54047     },
54048
54049     /**
54050      * Protected method that will not generally be called directly. It
54051      * is called when the editor creates its toolbar. Override this method if you need to
54052      * add custom toolbar buttons.
54053      * @param {HtmlEditor} editor
54054      */
54055     createToolbar : function(editor){
54056         Roo.log("create toolbars");
54057         if (!editor.toolbars || !editor.toolbars.length) {
54058             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
54059         }
54060         
54061         for (var i =0 ; i < editor.toolbars.length;i++) {
54062             editor.toolbars[i] = Roo.factory(
54063                     typeof(editor.toolbars[i]) == 'string' ?
54064                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
54065                 Roo.form.HtmlEditor);
54066             editor.toolbars[i].init(editor);
54067         }
54068          
54069         
54070     },
54071     /**
54072      * get the Context selected node
54073      * @returns {DomElement|boolean} selected node if active or false if none
54074      * 
54075      */
54076     getSelectedNode : function()
54077     {
54078         if (this.toolbars.length < 2 || !this.toolbars[1].tb) {
54079             return false;
54080         }
54081         return this.toolbars[1].tb.selectedNode;
54082     
54083     },
54084     // private
54085     onRender : function(ct, position)
54086     {
54087         var _t = this;
54088         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
54089         
54090         this.wrap = this.el.wrap({
54091             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
54092         });
54093         
54094         this.editorcore.onRender(ct, position);
54095          
54096         if (this.resizable) {
54097             this.resizeEl = new Roo.Resizable(this.wrap, {
54098                 pinned : true,
54099                 wrap: true,
54100                 dynamic : true,
54101                 minHeight : this.height,
54102                 height: this.height,
54103                 handles : this.resizable,
54104                 width: this.width,
54105                 listeners : {
54106                     resize : function(r, w, h) {
54107                         _t.onResize(w,h); // -something
54108                     }
54109                 }
54110             });
54111             
54112         }
54113         this.createToolbar(this);
54114        
54115         
54116         if(!this.width){
54117             this.setSize(this.wrap.getSize());
54118         }
54119         if (this.resizeEl) {
54120             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
54121             // should trigger onReize..
54122         }
54123         
54124         this.keyNav = new Roo.KeyNav(this.el, {
54125             
54126             "tab" : function(e){
54127                 e.preventDefault();
54128                 
54129                 var value = this.getValue();
54130                 
54131                 var start = this.el.dom.selectionStart;
54132                 var end = this.el.dom.selectionEnd;
54133                 
54134                 if(!e.shiftKey){
54135                     
54136                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
54137                     this.el.dom.setSelectionRange(end + 1, end + 1);
54138                     return;
54139                 }
54140                 
54141                 var f = value.substring(0, start).split("\t");
54142                 
54143                 if(f.pop().length != 0){
54144                     return;
54145                 }
54146                 
54147                 this.setValue(f.join("\t") + value.substring(end));
54148                 this.el.dom.setSelectionRange(start - 1, start - 1);
54149                 
54150             },
54151             
54152             "home" : function(e){
54153                 e.preventDefault();
54154                 
54155                 var curr = this.el.dom.selectionStart;
54156                 var lines = this.getValue().split("\n");
54157                 
54158                 if(!lines.length){
54159                     return;
54160                 }
54161                 
54162                 if(e.ctrlKey){
54163                     this.el.dom.setSelectionRange(0, 0);
54164                     return;
54165                 }
54166                 
54167                 var pos = 0;
54168                 
54169                 for (var i = 0; i < lines.length;i++) {
54170                     pos += lines[i].length;
54171                     
54172                     if(i != 0){
54173                         pos += 1;
54174                     }
54175                     
54176                     if(pos < curr){
54177                         continue;
54178                     }
54179                     
54180                     pos -= lines[i].length;
54181                     
54182                     break;
54183                 }
54184                 
54185                 if(!e.shiftKey){
54186                     this.el.dom.setSelectionRange(pos, pos);
54187                     return;
54188                 }
54189                 
54190                 this.el.dom.selectionStart = pos;
54191                 this.el.dom.selectionEnd = curr;
54192             },
54193             
54194             "end" : function(e){
54195                 e.preventDefault();
54196                 
54197                 var curr = this.el.dom.selectionStart;
54198                 var lines = this.getValue().split("\n");
54199                 
54200                 if(!lines.length){
54201                     return;
54202                 }
54203                 
54204                 if(e.ctrlKey){
54205                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
54206                     return;
54207                 }
54208                 
54209                 var pos = 0;
54210                 
54211                 for (var i = 0; i < lines.length;i++) {
54212                     
54213                     pos += lines[i].length;
54214                     
54215                     if(i != 0){
54216                         pos += 1;
54217                     }
54218                     
54219                     if(pos < curr){
54220                         continue;
54221                     }
54222                     
54223                     break;
54224                 }
54225                 
54226                 if(!e.shiftKey){
54227                     this.el.dom.setSelectionRange(pos, pos);
54228                     return;
54229                 }
54230                 
54231                 this.el.dom.selectionStart = curr;
54232                 this.el.dom.selectionEnd = pos;
54233             },
54234
54235             scope : this,
54236
54237             doRelay : function(foo, bar, hname){
54238                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
54239             },
54240
54241             forceKeyDown: true
54242         });
54243         
54244 //        if(this.autosave && this.w){
54245 //            this.autoSaveFn = setInterval(this.autosave, 1000);
54246 //        }
54247     },
54248
54249     // private
54250     onResize : function(w, h)
54251     {
54252         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
54253         var ew = false;
54254         var eh = false;
54255         
54256         if(this.el ){
54257             if(typeof w == 'number'){
54258                 var aw = w - this.wrap.getFrameWidth('lr');
54259                 this.el.setWidth(this.adjustWidth('textarea', aw));
54260                 ew = aw;
54261             }
54262             if(typeof h == 'number'){
54263                 var tbh = 0;
54264                 for (var i =0; i < this.toolbars.length;i++) {
54265                     // fixme - ask toolbars for heights?
54266                     tbh += this.toolbars[i].tb.el.getHeight();
54267                     if (this.toolbars[i].footer) {
54268                         tbh += this.toolbars[i].footer.el.getHeight();
54269                     }
54270                 }
54271                 
54272                 
54273                 
54274                 
54275                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
54276                 ah -= 5; // knock a few pixes off for look..
54277 //                Roo.log(ah);
54278                 this.el.setHeight(this.adjustWidth('textarea', ah));
54279                 var eh = ah;
54280             }
54281         }
54282         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
54283         this.editorcore.onResize(ew,eh);
54284         
54285     },
54286
54287     /**
54288      * Toggles the editor between standard and source edit mode.
54289      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
54290      */
54291     toggleSourceEdit : function(sourceEditMode)
54292     {
54293         this.editorcore.toggleSourceEdit(sourceEditMode);
54294         
54295         if(this.editorcore.sourceEditMode){
54296             Roo.log('editor - showing textarea');
54297             
54298 //            Roo.log('in');
54299 //            Roo.log(this.syncValue());
54300             this.editorcore.syncValue();
54301             this.el.removeClass('x-hidden');
54302             this.el.dom.removeAttribute('tabIndex');
54303             this.el.focus();
54304             this.el.dom.scrollTop = 0;
54305             
54306             
54307             for (var i = 0; i < this.toolbars.length; i++) {
54308                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
54309                     this.toolbars[i].tb.hide();
54310                     this.toolbars[i].footer.hide();
54311                 }
54312             }
54313             
54314         }else{
54315             Roo.log('editor - hiding textarea');
54316 //            Roo.log('out')
54317 //            Roo.log(this.pushValue()); 
54318             this.editorcore.pushValue();
54319             
54320             this.el.addClass('x-hidden');
54321             this.el.dom.setAttribute('tabIndex', -1);
54322             
54323             for (var i = 0; i < this.toolbars.length; i++) {
54324                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
54325                     this.toolbars[i].tb.show();
54326                     this.toolbars[i].footer.show();
54327                 }
54328             }
54329             
54330             //this.deferFocus();
54331         }
54332         
54333         this.setSize(this.wrap.getSize());
54334         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
54335         
54336         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
54337     },
54338  
54339     // private (for BoxComponent)
54340     adjustSize : Roo.BoxComponent.prototype.adjustSize,
54341
54342     // private (for BoxComponent)
54343     getResizeEl : function(){
54344         return this.wrap;
54345     },
54346
54347     // private (for BoxComponent)
54348     getPositionEl : function(){
54349         return this.wrap;
54350     },
54351
54352     // private
54353     initEvents : function(){
54354         this.originalValue = this.getValue();
54355     },
54356
54357     /**
54358      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
54359      * @method
54360      */
54361     markInvalid : Roo.emptyFn,
54362     /**
54363      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
54364      * @method
54365      */
54366     clearInvalid : Roo.emptyFn,
54367
54368     setValue : function(v){
54369         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
54370         this.editorcore.pushValue();
54371     },
54372
54373     /**
54374      * update the language in the body - really done by core
54375      * @param {String} language - eg. en / ar / zh-CN etc..
54376      */
54377     updateLanguage : function(lang)
54378     {
54379         this.language = lang;
54380         this.editorcore.language = lang;
54381         this.editorcore.updateLanguage();
54382      
54383     },
54384     // private
54385     deferFocus : function(){
54386         this.focus.defer(10, this);
54387     },
54388
54389     // doc'ed in Field
54390     focus : function(){
54391         this.editorcore.focus();
54392         
54393     },
54394       
54395
54396     // private
54397     onDestroy : function(){
54398         
54399         
54400         
54401         if(this.rendered){
54402             
54403             for (var i =0; i < this.toolbars.length;i++) {
54404                 // fixme - ask toolbars for heights?
54405                 this.toolbars[i].onDestroy();
54406             }
54407             
54408             this.wrap.dom.innerHTML = '';
54409             this.wrap.remove();
54410         }
54411     },
54412
54413     // private
54414     onFirstFocus : function(){
54415         //Roo.log("onFirstFocus");
54416         this.editorcore.onFirstFocus();
54417          for (var i =0; i < this.toolbars.length;i++) {
54418             this.toolbars[i].onFirstFocus();
54419         }
54420         
54421     },
54422     
54423     // private
54424     syncValue : function()
54425     {
54426         this.editorcore.syncValue();
54427     },
54428     
54429     pushValue : function()
54430     {
54431         this.editorcore.pushValue();
54432     },
54433     
54434     setStylesheets : function(stylesheets)
54435     {
54436         this.editorcore.setStylesheets(stylesheets);
54437     },
54438     
54439     removeStylesheets : function()
54440     {
54441         this.editorcore.removeStylesheets();
54442     }
54443      
54444     
54445     // hide stuff that is not compatible
54446     /**
54447      * @event blur
54448      * @hide
54449      */
54450     /**
54451      * @event change
54452      * @hide
54453      */
54454     /**
54455      * @event focus
54456      * @hide
54457      */
54458     /**
54459      * @event specialkey
54460      * @hide
54461      */
54462     /**
54463      * @cfg {String} fieldClass @hide
54464      */
54465     /**
54466      * @cfg {String} focusClass @hide
54467      */
54468     /**
54469      * @cfg {String} autoCreate @hide
54470      */
54471     /**
54472      * @cfg {String} inputType @hide
54473      */
54474     /**
54475      * @cfg {String} invalidClass @hide
54476      */
54477     /**
54478      * @cfg {String} invalidText @hide
54479      */
54480     /**
54481      * @cfg {String} msgFx @hide
54482      */
54483     /**
54484      * @cfg {String} validateOnBlur @hide
54485      */
54486 });
54487  
54488     /*
54489  * Based on
54490  * Ext JS Library 1.1.1
54491  * Copyright(c) 2006-2007, Ext JS, LLC.
54492  *  
54493  
54494  */
54495
54496 /**
54497  * @class Roo.form.HtmlEditor.ToolbarStandard
54498  * Basic Toolbar
54499
54500  * Usage:
54501  *
54502  new Roo.form.HtmlEditor({
54503     ....
54504     toolbars : [
54505         new Roo.form.HtmlEditorToolbar1({
54506             disable : { fonts: 1 , format: 1, ..., ... , ...],
54507             btns : [ .... ]
54508         })
54509     }
54510      
54511  * 
54512  * @cfg {Object} disable List of elements to disable..
54513  * @cfg {Roo.Toolbar.Item|Roo.Toolbar.Button|Roo.Toolbar.SplitButton|Roo.form.Field} btns[] List of additional buttons.
54514  * 
54515  * 
54516  * NEEDS Extra CSS? 
54517  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
54518  */
54519  
54520 Roo.form.HtmlEditor.ToolbarStandard = function(config)
54521 {
54522     
54523     Roo.apply(this, config);
54524     
54525     // default disabled, based on 'good practice'..
54526     this.disable = this.disable || {};
54527     Roo.applyIf(this.disable, {
54528         fontSize : true,
54529         colors : true,
54530         specialElements : true
54531     });
54532     
54533     
54534     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
54535     // dont call parent... till later.
54536 }
54537
54538 Roo.form.HtmlEditor.ToolbarStandard.prototype = {
54539     
54540     tb: false,
54541     
54542     rendered: false,
54543     
54544     editor : false,
54545     editorcore : false,
54546     /**
54547      * @cfg {Object} disable  List of toolbar elements to disable
54548          
54549      */
54550     disable : false,
54551     
54552     
54553      /**
54554      * @cfg {String} createLinkText The default text for the create link prompt
54555      */
54556     createLinkText : 'Please enter the URL for the link:',
54557     /**
54558      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
54559      */
54560     defaultLinkValue : 'http:/'+'/',
54561    
54562     
54563       /**
54564      * @cfg {Array} fontFamilies An array of available font families
54565      */
54566     fontFamilies : [
54567         'Arial',
54568         'Courier New',
54569         'Tahoma',
54570         'Times New Roman',
54571         'Verdana'
54572     ],
54573     
54574     specialChars : [
54575            "&#169;",
54576           "&#174;",     
54577           "&#8482;",    
54578           "&#163;" ,    
54579          // "&#8212;",    
54580           "&#8230;",    
54581           "&#247;" ,    
54582         //  "&#225;" ,     ?? a acute?
54583            "&#8364;"    , //Euro
54584        //   "&#8220;"    ,
54585         //  "&#8221;"    ,
54586         //  "&#8226;"    ,
54587           "&#176;"  //   , // degrees
54588
54589          // "&#233;"     , // e ecute
54590          // "&#250;"     , // u ecute?
54591     ],
54592     
54593     specialElements : [
54594         {
54595             text: "Insert Table",
54596             xtype: 'MenuItem',
54597             xns : Roo.Menu,
54598             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
54599                 
54600         },
54601         {    
54602             text: "Insert Image",
54603             xtype: 'MenuItem',
54604             xns : Roo.Menu,
54605             ihtml : '<img src="about:blank"/>'
54606             
54607         }
54608         
54609          
54610     ],
54611     
54612     
54613     inputElements : [ 
54614             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
54615             "input:submit", "input:button", "select", "textarea", "label" ],
54616     formats : [
54617         ["p"] ,  
54618         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
54619         ["pre"],[ "code"], 
54620         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
54621         ['div'],['span'],
54622         ['sup'],['sub']
54623     ],
54624     
54625     cleanStyles : [
54626         "font-size"
54627     ],
54628      /**
54629      * @cfg {String} defaultFont default font to use.
54630      */
54631     defaultFont: 'tahoma',
54632    
54633     fontSelect : false,
54634     
54635     
54636     formatCombo : false,
54637     
54638     init : function(editor)
54639     {
54640         this.editor = editor;
54641         this.editorcore = editor.editorcore ? editor.editorcore : editor;
54642         var editorcore = this.editorcore;
54643         
54644         var _t = this;
54645         
54646         var fid = editorcore.frameId;
54647         var etb = this;
54648         function btn(id, toggle, handler){
54649             var xid = fid + '-'+ id ;
54650             return {
54651                 id : xid,
54652                 cmd : id,
54653                 cls : 'x-btn-icon x-edit-'+id,
54654                 enableToggle:toggle !== false,
54655                 scope: _t, // was editor...
54656                 handler:handler||_t.relayBtnCmd,
54657                 clickEvent:'mousedown',
54658                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
54659                 tabIndex:-1
54660             };
54661         }
54662         
54663         
54664         
54665         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
54666         this.tb = tb;
54667          // stop form submits
54668         tb.el.on('click', function(e){
54669             e.preventDefault(); // what does this do?
54670         });
54671
54672         if(!this.disable.font) { // && !Roo.isSafari){
54673             /* why no safari for fonts 
54674             editor.fontSelect = tb.el.createChild({
54675                 tag:'select',
54676                 tabIndex: -1,
54677                 cls:'x-font-select',
54678                 html: this.createFontOptions()
54679             });
54680             
54681             editor.fontSelect.on('change', function(){
54682                 var font = editor.fontSelect.dom.value;
54683                 editor.relayCmd('fontname', font);
54684                 editor.deferFocus();
54685             }, editor);
54686             
54687             tb.add(
54688                 editor.fontSelect.dom,
54689                 '-'
54690             );
54691             */
54692             
54693         };
54694         if(!this.disable.formats){
54695             this.formatCombo = new Roo.form.ComboBox({
54696                 store: new Roo.data.SimpleStore({
54697                     id : 'tag',
54698                     fields: ['tag'],
54699                     data : this.formats // from states.js
54700                 }),
54701                 blockFocus : true,
54702                 name : '',
54703                 //autoCreate : {tag: "div",  size: "20"},
54704                 displayField:'tag',
54705                 typeAhead: false,
54706                 mode: 'local',
54707                 editable : false,
54708                 triggerAction: 'all',
54709                 emptyText:'Add tag',
54710                 selectOnFocus:true,
54711                 width:135,
54712                 listeners : {
54713                     'select': function(c, r, i) {
54714                         editorcore.insertTag(r.get('tag'));
54715                         editor.focus();
54716                     }
54717                 }
54718
54719             });
54720             tb.addField(this.formatCombo);
54721             
54722         }
54723         
54724         if(!this.disable.format){
54725             tb.add(
54726                 btn('bold'),
54727                 btn('italic'),
54728                 btn('underline'),
54729                 btn('strikethrough')
54730             );
54731         };
54732         if(!this.disable.fontSize){
54733             tb.add(
54734                 '-',
54735                 
54736                 
54737                 btn('increasefontsize', false, editorcore.adjustFont),
54738                 btn('decreasefontsize', false, editorcore.adjustFont)
54739             );
54740         };
54741         
54742         
54743         if(!this.disable.colors){
54744             tb.add(
54745                 '-', {
54746                     id:editorcore.frameId +'-forecolor',
54747                     cls:'x-btn-icon x-edit-forecolor',
54748                     clickEvent:'mousedown',
54749                     tooltip: this.buttonTips['forecolor'] || undefined,
54750                     tabIndex:-1,
54751                     menu : new Roo.menu.ColorMenu({
54752                         allowReselect: true,
54753                         focus: Roo.emptyFn,
54754                         value:'000000',
54755                         plain:true,
54756                         selectHandler: function(cp, color){
54757                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
54758                             editor.deferFocus();
54759                         },
54760                         scope: editorcore,
54761                         clickEvent:'mousedown'
54762                     })
54763                 }, {
54764                     id:editorcore.frameId +'backcolor',
54765                     cls:'x-btn-icon x-edit-backcolor',
54766                     clickEvent:'mousedown',
54767                     tooltip: this.buttonTips['backcolor'] || undefined,
54768                     tabIndex:-1,
54769                     menu : new Roo.menu.ColorMenu({
54770                         focus: Roo.emptyFn,
54771                         value:'FFFFFF',
54772                         plain:true,
54773                         allowReselect: true,
54774                         selectHandler: function(cp, color){
54775                             if(Roo.isGecko){
54776                                 editorcore.execCmd('useCSS', false);
54777                                 editorcore.execCmd('hilitecolor', color);
54778                                 editorcore.execCmd('useCSS', true);
54779                                 editor.deferFocus();
54780                             }else{
54781                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
54782                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
54783                                 editor.deferFocus();
54784                             }
54785                         },
54786                         scope:editorcore,
54787                         clickEvent:'mousedown'
54788                     })
54789                 }
54790             );
54791         };
54792         // now add all the items...
54793         
54794
54795         if(!this.disable.alignments){
54796             tb.add(
54797                 '-',
54798                 btn('justifyleft'),
54799                 btn('justifycenter'),
54800                 btn('justifyright')
54801             );
54802         };
54803
54804         //if(!Roo.isSafari){
54805             if(!this.disable.links){
54806                 tb.add(
54807                     '-',
54808                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
54809                 );
54810             };
54811
54812             if(!this.disable.lists){
54813                 tb.add(
54814                     '-',
54815                     btn('insertorderedlist'),
54816                     btn('insertunorderedlist')
54817                 );
54818             }
54819             if(!this.disable.sourceEdit){
54820                 tb.add(
54821                     '-',
54822                     btn('sourceedit', true, function(btn){
54823                         this.toggleSourceEdit(btn.pressed);
54824                     })
54825                 );
54826             }
54827         //}
54828         
54829         var smenu = { };
54830         // special menu.. - needs to be tidied up..
54831         if (!this.disable.special) {
54832             smenu = {
54833                 text: "&#169;",
54834                 cls: 'x-edit-none',
54835                 
54836                 menu : {
54837                     items : []
54838                 }
54839             };
54840             for (var i =0; i < this.specialChars.length; i++) {
54841                 smenu.menu.items.push({
54842                     
54843                     html: this.specialChars[i],
54844                     handler: function(a,b) {
54845                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
54846                         //editor.insertAtCursor(a.html);
54847                         
54848                     },
54849                     tabIndex:-1
54850                 });
54851             }
54852             
54853             
54854             tb.add(smenu);
54855             
54856             
54857         }
54858         
54859         var cmenu = { };
54860         if (!this.disable.cleanStyles) {
54861             cmenu = {
54862                 cls: 'x-btn-icon x-btn-clear',
54863                 
54864                 menu : {
54865                     items : []
54866                 }
54867             };
54868             for (var i =0; i < this.cleanStyles.length; i++) {
54869                 cmenu.menu.items.push({
54870                     actiontype : this.cleanStyles[i],
54871                     html: 'Remove ' + this.cleanStyles[i],
54872                     handler: function(a,b) {
54873 //                        Roo.log(a);
54874 //                        Roo.log(b);
54875                         var c = Roo.get(editorcore.doc.body);
54876                         c.select('[style]').each(function(s) {
54877                             s.dom.style.removeProperty(a.actiontype);
54878                         });
54879                         editorcore.syncValue();
54880                     },
54881                     tabIndex:-1
54882                 });
54883             }
54884             cmenu.menu.items.push({
54885                 actiontype : 'tablewidths',
54886                 html: 'Remove Table Widths',
54887                 handler: function(a,b) {
54888                     editorcore.cleanTableWidths();
54889                     editorcore.syncValue();
54890                 },
54891                 tabIndex:-1
54892             });
54893             cmenu.menu.items.push({
54894                 actiontype : 'word',
54895                 html: 'Remove MS Word Formating',
54896                 handler: function(a,b) {
54897                     editorcore.cleanWord();
54898                     editorcore.syncValue();
54899                 },
54900                 tabIndex:-1
54901             });
54902             
54903             cmenu.menu.items.push({
54904                 actiontype : 'all',
54905                 html: 'Remove All Styles',
54906                 handler: function(a,b) {
54907                     
54908                     var c = Roo.get(editorcore.doc.body);
54909                     c.select('[style]').each(function(s) {
54910                         s.dom.removeAttribute('style');
54911                     });
54912                     editorcore.syncValue();
54913                 },
54914                 tabIndex:-1
54915             });
54916             
54917             cmenu.menu.items.push({
54918                 actiontype : 'all',
54919                 html: 'Remove All CSS Classes',
54920                 handler: function(a,b) {
54921                     
54922                     var c = Roo.get(editorcore.doc.body);
54923                     c.select('[class]').each(function(s) {
54924                         s.dom.removeAttribute('class');
54925                     });
54926                     editorcore.cleanWord();
54927                     editorcore.syncValue();
54928                 },
54929                 tabIndex:-1
54930             });
54931             
54932              cmenu.menu.items.push({
54933                 actiontype : 'tidy',
54934                 html: 'Tidy HTML Source',
54935                 handler: function(a,b) {
54936                     new Roo.htmleditor.Tidy(editorcore.doc.body);
54937                     editorcore.syncValue();
54938                 },
54939                 tabIndex:-1
54940             });
54941             
54942             
54943             tb.add(cmenu);
54944         }
54945          
54946         if (!this.disable.specialElements) {
54947             var semenu = {
54948                 text: "Other;",
54949                 cls: 'x-edit-none',
54950                 menu : {
54951                     items : []
54952                 }
54953             };
54954             for (var i =0; i < this.specialElements.length; i++) {
54955                 semenu.menu.items.push(
54956                     Roo.apply({ 
54957                         handler: function(a,b) {
54958                             editor.insertAtCursor(this.ihtml);
54959                         }
54960                     }, this.specialElements[i])
54961                 );
54962                     
54963             }
54964             
54965             tb.add(semenu);
54966             
54967             
54968         }
54969          
54970         
54971         if (this.btns) {
54972             for(var i =0; i< this.btns.length;i++) {
54973                 var b = Roo.factory(this.btns[i],this.btns[i].xns || Roo.form);
54974                 b.cls =  'x-edit-none';
54975                 
54976                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
54977                     b.cls += ' x-init-enable';
54978                 }
54979                 
54980                 b.scope = editorcore;
54981                 tb.add(b);
54982             }
54983         
54984         }
54985         
54986         
54987         
54988         // disable everything...
54989         
54990         this.tb.items.each(function(item){
54991             
54992            if(
54993                 item.id != editorcore.frameId+ '-sourceedit' && 
54994                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
54995             ){
54996                 
54997                 item.disable();
54998             }
54999         });
55000         this.rendered = true;
55001         
55002         // the all the btns;
55003         editor.on('editorevent', this.updateToolbar, this);
55004         // other toolbars need to implement this..
55005         //editor.on('editmodechange', this.updateToolbar, this);
55006     },
55007     
55008     
55009     relayBtnCmd : function(btn) {
55010         this.editorcore.relayCmd(btn.cmd);
55011     },
55012     // private used internally
55013     createLink : function(){
55014         //Roo.log("create link?");
55015         var ec = this.editorcore;
55016         var ar = ec.getAllAncestors();
55017         var n = false;
55018         for(var i = 0;i< ar.length;i++) {
55019             if (ar[i] && ar[i].nodeName == 'A') {
55020                 n = ar[i];
55021                 break;
55022             }
55023         }
55024         
55025         (function() {
55026             
55027             Roo.MessageBox.show({
55028                 title : "Add / Edit Link URL",
55029                 msg : "Enter the url for the link",
55030                 buttons: Roo.MessageBox.OKCANCEL,
55031                 fn: function(btn, url){
55032                     if (btn != 'ok') {
55033                         return;
55034                     }
55035                     if(url && url != 'http:/'+'/'){
55036                         if (n) {
55037                             n.setAttribute('href', url);
55038                         } else {
55039                             ec.relayCmd('createlink', url);
55040                         }
55041                     }
55042                 },
55043                 minWidth:250,
55044                 prompt:true,
55045                 //multiline: multiline,
55046                 modal : true,
55047                 value :  n  ? n.getAttribute('href') : '' 
55048             });
55049             
55050              
55051         }).defer(100, this); // we have to defer this , otherwise the mouse click gives focus to the main window.
55052         
55053     },
55054
55055     
55056     /**
55057      * Protected method that will not generally be called directly. It triggers
55058      * a toolbar update by reading the markup state of the current selection in the editor.
55059      */
55060     updateToolbar: function(){
55061
55062         if(!this.editorcore.activated){
55063             this.editor.onFirstFocus();
55064             return;
55065         }
55066
55067         var btns = this.tb.items.map, 
55068             doc = this.editorcore.doc,
55069             frameId = this.editorcore.frameId;
55070
55071         if(!this.disable.font && !Roo.isSafari){
55072             /*
55073             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
55074             if(name != this.fontSelect.dom.value){
55075                 this.fontSelect.dom.value = name;
55076             }
55077             */
55078         }
55079         if(!this.disable.format){
55080             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
55081             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
55082             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
55083             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
55084         }
55085         if(!this.disable.alignments){
55086             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
55087             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
55088             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
55089         }
55090         if(!Roo.isSafari && !this.disable.lists){
55091             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
55092             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
55093         }
55094         
55095         var ans = this.editorcore.getAllAncestors();
55096         if (this.formatCombo) {
55097             
55098             
55099             var store = this.formatCombo.store;
55100             this.formatCombo.setValue("");
55101             for (var i =0; i < ans.length;i++) {
55102                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
55103                     // select it..
55104                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
55105                     break;
55106                 }
55107             }
55108         }
55109         
55110         
55111         
55112         // hides menus... - so this cant be on a menu...
55113         Roo.menu.MenuMgr.hideAll();
55114
55115         //this.editorsyncValue();
55116     },
55117    
55118     
55119     createFontOptions : function(){
55120         var buf = [], fs = this.fontFamilies, ff, lc;
55121         
55122         
55123         
55124         for(var i = 0, len = fs.length; i< len; i++){
55125             ff = fs[i];
55126             lc = ff.toLowerCase();
55127             buf.push(
55128                 '<option value="',lc,'" style="font-family:',ff,';"',
55129                     (this.defaultFont == lc ? ' selected="true">' : '>'),
55130                     ff,
55131                 '</option>'
55132             );
55133         }
55134         return buf.join('');
55135     },
55136     
55137     toggleSourceEdit : function(sourceEditMode){
55138         
55139         Roo.log("toolbar toogle");
55140         if(sourceEditMode === undefined){
55141             sourceEditMode = !this.sourceEditMode;
55142         }
55143         this.sourceEditMode = sourceEditMode === true;
55144         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
55145         // just toggle the button?
55146         if(btn.pressed !== this.sourceEditMode){
55147             btn.toggle(this.sourceEditMode);
55148             return;
55149         }
55150         
55151         if(sourceEditMode){
55152             Roo.log("disabling buttons");
55153             this.tb.items.each(function(item){
55154                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
55155                     item.disable();
55156                 }
55157             });
55158           
55159         }else{
55160             Roo.log("enabling buttons");
55161             if(this.editorcore.initialized){
55162                 this.tb.items.each(function(item){
55163                     item.enable();
55164                 });
55165                 // initialize 'blocks'
55166                 Roo.each(Roo.get(this.editorcore.doc.body).query('*[data-block]'), function(e) {
55167                     Roo.htmleditor.Block.factory(e).updateElement(e);
55168                 },this);
55169             
55170             }
55171             
55172         }
55173         Roo.log("calling toggole on editor");
55174         // tell the editor that it's been pressed..
55175         this.editor.toggleSourceEdit(sourceEditMode);
55176        
55177     },
55178      /**
55179      * Object collection of toolbar tooltips for the buttons in the editor. The key
55180      * is the command id associated with that button and the value is a valid QuickTips object.
55181      * For example:
55182 <pre><code>
55183 {
55184     bold : {
55185         title: 'Bold (Ctrl+B)',
55186         text: 'Make the selected text bold.',
55187         cls: 'x-html-editor-tip'
55188     },
55189     italic : {
55190         title: 'Italic (Ctrl+I)',
55191         text: 'Make the selected text italic.',
55192         cls: 'x-html-editor-tip'
55193     },
55194     ...
55195 </code></pre>
55196     * @type Object
55197      */
55198     buttonTips : {
55199         bold : {
55200             title: 'Bold (Ctrl+B)',
55201             text: 'Make the selected text bold.',
55202             cls: 'x-html-editor-tip'
55203         },
55204         italic : {
55205             title: 'Italic (Ctrl+I)',
55206             text: 'Make the selected text italic.',
55207             cls: 'x-html-editor-tip'
55208         },
55209         underline : {
55210             title: 'Underline (Ctrl+U)',
55211             text: 'Underline the selected text.',
55212             cls: 'x-html-editor-tip'
55213         },
55214         strikethrough : {
55215             title: 'Strikethrough',
55216             text: 'Strikethrough the selected text.',
55217             cls: 'x-html-editor-tip'
55218         },
55219         increasefontsize : {
55220             title: 'Grow Text',
55221             text: 'Increase the font size.',
55222             cls: 'x-html-editor-tip'
55223         },
55224         decreasefontsize : {
55225             title: 'Shrink Text',
55226             text: 'Decrease the font size.',
55227             cls: 'x-html-editor-tip'
55228         },
55229         backcolor : {
55230             title: 'Text Highlight Color',
55231             text: 'Change the background color of the selected text.',
55232             cls: 'x-html-editor-tip'
55233         },
55234         forecolor : {
55235             title: 'Font Color',
55236             text: 'Change the color of the selected text.',
55237             cls: 'x-html-editor-tip'
55238         },
55239         justifyleft : {
55240             title: 'Align Text Left',
55241             text: 'Align text to the left.',
55242             cls: 'x-html-editor-tip'
55243         },
55244         justifycenter : {
55245             title: 'Center Text',
55246             text: 'Center text in the editor.',
55247             cls: 'x-html-editor-tip'
55248         },
55249         justifyright : {
55250             title: 'Align Text Right',
55251             text: 'Align text to the right.',
55252             cls: 'x-html-editor-tip'
55253         },
55254         insertunorderedlist : {
55255             title: 'Bullet List',
55256             text: 'Start a bulleted list.',
55257             cls: 'x-html-editor-tip'
55258         },
55259         insertorderedlist : {
55260             title: 'Numbered List',
55261             text: 'Start a numbered list.',
55262             cls: 'x-html-editor-tip'
55263         },
55264         createlink : {
55265             title: 'Hyperlink',
55266             text: 'Make the selected text a hyperlink.',
55267             cls: 'x-html-editor-tip'
55268         },
55269         sourceedit : {
55270             title: 'Source Edit',
55271             text: 'Switch to source editing mode.',
55272             cls: 'x-html-editor-tip'
55273         }
55274     },
55275     // private
55276     onDestroy : function(){
55277         if(this.rendered){
55278             
55279             this.tb.items.each(function(item){
55280                 if(item.menu){
55281                     item.menu.removeAll();
55282                     if(item.menu.el){
55283                         item.menu.el.destroy();
55284                     }
55285                 }
55286                 item.destroy();
55287             });
55288              
55289         }
55290     },
55291     onFirstFocus: function() {
55292         this.tb.items.each(function(item){
55293            item.enable();
55294         });
55295     }
55296 };
55297
55298
55299
55300
55301 // <script type="text/javascript">
55302 /*
55303  * Based on
55304  * Ext JS Library 1.1.1
55305  * Copyright(c) 2006-2007, Ext JS, LLC.
55306  *  
55307  
55308  */
55309
55310  
55311 /**
55312  * @class Roo.form.HtmlEditor.ToolbarContext
55313  * Context Toolbar
55314  * 
55315  * Usage:
55316  *
55317  new Roo.form.HtmlEditor({
55318     ....
55319     toolbars : [
55320         { xtype: 'ToolbarStandard', styles : {} }
55321         { xtype: 'ToolbarContext', disable : {} }
55322     ]
55323 })
55324
55325      
55326  * 
55327  * @config : {Object} disable List of elements to disable.. (not done yet.)
55328  * @config : {Object} styles  Map of styles available.
55329  * 
55330  */
55331
55332 Roo.form.HtmlEditor.ToolbarContext = function(config)
55333 {
55334     
55335     Roo.apply(this, config);
55336     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
55337     // dont call parent... till later.
55338     this.styles = this.styles || {};
55339 }
55340
55341  
55342
55343 Roo.form.HtmlEditor.ToolbarContext.types = {
55344     'IMG' : [
55345         {
55346             name : 'width',
55347             title: "Width",
55348             width: 40
55349         },
55350         {
55351             name : 'height',
55352             title: "Height",
55353             width: 40
55354         },
55355         {
55356             name : 'align',
55357             title: "Align",
55358             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
55359             width : 80
55360             
55361         },
55362         {
55363             name : 'border',
55364             title: "Border",
55365             width: 40
55366         },
55367         {
55368             name : 'alt',
55369             title: "Alt",
55370             width: 120
55371         },
55372         {
55373             name : 'src',
55374             title: "Src",
55375             width: 220
55376         }
55377         
55378     ],
55379     
55380     'FIGURE' : [
55381         {
55382             name : 'align',
55383             title: "Align",
55384             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
55385             width : 80  
55386         }
55387     ],
55388     'A' : [
55389         {
55390             name : 'name',
55391             title: "Name",
55392             width: 50
55393         },
55394         {
55395             name : 'target',
55396             title: "Target",
55397             width: 120
55398         },
55399         {
55400             name : 'href',
55401             title: "Href",
55402             width: 220
55403         } // border?
55404         
55405     ],
55406     
55407     'INPUT' : [
55408         {
55409             name : 'name',
55410             title: "name",
55411             width: 120
55412         },
55413         {
55414             name : 'value',
55415             title: "Value",
55416             width: 120
55417         },
55418         {
55419             name : 'width',
55420             title: "Width",
55421             width: 40
55422         }
55423     ],
55424     'LABEL' : [
55425          {
55426             name : 'for',
55427             title: "For",
55428             width: 120
55429         }
55430     ],
55431     'TEXTAREA' : [
55432         {
55433             name : 'name',
55434             title: "name",
55435             width: 120
55436         },
55437         {
55438             name : 'rows',
55439             title: "Rows",
55440             width: 20
55441         },
55442         {
55443             name : 'cols',
55444             title: "Cols",
55445             width: 20
55446         }
55447     ],
55448     'SELECT' : [
55449         {
55450             name : 'name',
55451             title: "name",
55452             width: 120
55453         },
55454         {
55455             name : 'selectoptions',
55456             title: "Options",
55457             width: 200
55458         }
55459     ],
55460     
55461     // should we really allow this??
55462     // should this just be 
55463     'BODY' : [
55464         
55465         {
55466             name : 'title',
55467             title: "Title",
55468             width: 200,
55469             disabled : true
55470         }
55471     ],
55472  
55473     '*' : [
55474         // empty.
55475     ]
55476
55477 };
55478
55479 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
55480 Roo.form.HtmlEditor.ToolbarContext.stores = false;
55481
55482 Roo.form.HtmlEditor.ToolbarContext.options = {
55483         'font-family'  : [ 
55484                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
55485                 [ 'Courier New', 'Courier New'],
55486                 [ 'Tahoma', 'Tahoma'],
55487                 [ 'Times New Roman,serif', 'Times'],
55488                 [ 'Verdana','Verdana' ]
55489         ]
55490 };
55491
55492 // fixme - these need to be configurable..
55493  
55494
55495 //Roo.form.HtmlEditor.ToolbarContext.types
55496
55497
55498 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
55499     
55500     tb: false,
55501     
55502     rendered: false,
55503     
55504     editor : false,
55505     editorcore : false,
55506     /**
55507      * @cfg {Object} disable  List of toolbar elements to disable
55508          
55509      */
55510     disable : false,
55511     /**
55512      * @cfg {Object} styles List of styles 
55513      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
55514      *
55515      * These must be defined in the page, so they get rendered correctly..
55516      * .headline { }
55517      * TD.underline { }
55518      * 
55519      */
55520     styles : false,
55521     
55522     options: false,
55523     
55524     toolbars : false,
55525     
55526     init : function(editor)
55527     {
55528         this.editor = editor;
55529         this.editorcore = editor.editorcore ? editor.editorcore : editor;
55530         var editorcore = this.editorcore;
55531         
55532         var fid = editorcore.frameId;
55533         var etb = this;
55534         function btn(id, toggle, handler){
55535             var xid = fid + '-'+ id ;
55536             return {
55537                 id : xid,
55538                 cmd : id,
55539                 cls : 'x-btn-icon x-edit-'+id,
55540                 enableToggle:toggle !== false,
55541                 scope: editorcore, // was editor...
55542                 handler:handler||editorcore.relayBtnCmd,
55543                 clickEvent:'mousedown',
55544                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
55545                 tabIndex:-1
55546             };
55547         }
55548         // create a new element.
55549         var wdiv = editor.wrap.createChild({
55550                 tag: 'div'
55551             }, editor.wrap.dom.firstChild.nextSibling, true);
55552         
55553         // can we do this more than once??
55554         
55555          // stop form submits
55556       
55557  
55558         // disable everything...
55559         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
55560         this.toolbars = {};
55561         // block toolbars are built in updateToolbar when needed.
55562         for (var i in  ty) {
55563             
55564             this.toolbars[i] = this.buildToolbar(ty[i],i);
55565         }
55566         this.tb = this.toolbars.BODY;
55567         this.tb.el.show();
55568         this.buildFooter();
55569         this.footer.show();
55570         editor.on('hide', function( ) { this.footer.hide() }, this);
55571         editor.on('show', function( ) { this.footer.show() }, this);
55572         
55573          
55574         this.rendered = true;
55575         
55576         // the all the btns;
55577         editor.on('editorevent', this.updateToolbar, this);
55578         // other toolbars need to implement this..
55579         //editor.on('editmodechange', this.updateToolbar, this);
55580     },
55581     
55582     
55583     
55584     /**
55585      * Protected method that will not generally be called directly. It triggers
55586      * a toolbar update by reading the markup state of the current selection in the editor.
55587      *
55588      * Note you can force an update by calling on('editorevent', scope, false)
55589      */
55590     updateToolbar: function(editor ,ev, sel)
55591     {
55592         
55593         if (ev) {
55594             ev.stopEvent(); // se if we can stop this looping with mutiple events.
55595         }
55596         
55597         //Roo.log(ev);
55598         // capture mouse up - this is handy for selecting images..
55599         // perhaps should go somewhere else...
55600         if(!this.editorcore.activated){
55601              this.editor.onFirstFocus();
55602             return;
55603         }
55604         //Roo.log(ev ? ev.target : 'NOTARGET');
55605         
55606         
55607         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
55608         // selectNode - might want to handle IE?
55609         
55610         
55611         
55612         if (ev &&
55613             (ev.type == 'mouseup' || ev.type == 'click' ) &&
55614             ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
55615             // they have click on an image...
55616             // let's see if we can change the selection...
55617             sel = ev.target;
55618             
55619             // this triggers looping?
55620             //this.editorcore.selectNode(sel);
55621              
55622         }
55623         
55624         // this forces an id..
55625         Array.from(this.editorcore.doc.body.querySelectorAll('.roo-ed-selection')).forEach(function(e) {
55626              e.classList.remove('roo-ed-selection');
55627         });
55628         //Roo.select('.roo-ed-selection', false, this.editorcore.doc).removeClass('roo-ed-selection');
55629         //Roo.get(node).addClass('roo-ed-selection');
55630       
55631         //var updateFooter = sel ? false : true; 
55632         
55633         
55634         var ans = this.editorcore.getAllAncestors();
55635         
55636         // pick
55637         var ty = Roo.form.HtmlEditor.ToolbarContext.types;
55638         
55639         if (!sel) { 
55640             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
55641             sel = sel ? sel : this.editorcore.doc.body;
55642             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
55643             
55644         }
55645         
55646         var tn = sel.tagName.toUpperCase();
55647         var lastSel = this.tb.selectedNode;
55648         this.tb.selectedNode = sel;
55649         var left_label = tn;
55650         
55651         // ok see if we are editing a block?
55652         
55653         var db = false;
55654         // you are not actually selecting the block.
55655         if (sel && sel.hasAttribute('data-block')) {
55656             db = sel;
55657         } else if (sel && sel.closest('[data-block]')) {
55658             
55659             db = sel.closest('[data-block]');
55660             //var cepar = sel.closest('[contenteditable=true]');
55661             //if (db && cepar && cepar.tagName != 'BODY') {
55662             //   db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
55663             //}   
55664         }
55665         
55666         
55667         var block = false;
55668         //if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
55669         if (db && this.editorcore.enableBlocks) {
55670             block = Roo.htmleditor.Block.factory(db);
55671             
55672             
55673             if (block) {
55674                  db.className = (
55675                         db.classList.length > 0  ? db.className + ' ' : ''
55676                     )  + 'roo-ed-selection';
55677                  
55678                  // since we removed it earlier... its not there..
55679                 tn = 'BLOCK.' + db.getAttribute('data-block');
55680                 
55681                 //this.editorcore.selectNode(db);
55682                 if (typeof(this.toolbars[tn]) == 'undefined') {
55683                    this.toolbars[tn] = this.buildToolbar( false  ,tn ,block.friendly_name, block);
55684                 }
55685                 this.toolbars[tn].selectedNode = db;
55686                 left_label = block.friendly_name;
55687                 ans = this.editorcore.getAllAncestors();
55688             }
55689             
55690                 
55691             
55692         }
55693         
55694         
55695         if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
55696             return; // no change?
55697         }
55698         
55699         
55700           
55701         this.tb.el.hide();
55702         ///console.log("show: " + tn);
55703         this.tb =  typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
55704         
55705         this.tb.el.show();
55706         // update name
55707         this.tb.items.first().el.innerHTML = left_label + ':&nbsp;';
55708         
55709         
55710         // update attributes
55711         if (block && this.tb.fields) {
55712              
55713             this.tb.fields.each(function(e) {
55714                 e.setValue(block[e.name]);
55715             });
55716             
55717             
55718         } else  if (this.tb.fields && this.tb.selectedNode) {
55719             this.tb.fields.each( function(e) {
55720                 if (e.stylename) {
55721                     e.setValue(this.tb.selectedNode.style[e.stylename]);
55722                     return;
55723                 } 
55724                 e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
55725             }, this);
55726             this.updateToolbarStyles(this.tb.selectedNode);  
55727         }
55728         
55729         
55730        
55731         Roo.menu.MenuMgr.hideAll();
55732
55733         
55734         
55735     
55736         // update the footer
55737         //
55738         this.updateFooter(ans);
55739              
55740     },
55741     
55742     updateToolbarStyles : function(sel)
55743     {
55744         var hasStyles = false;
55745         for(var i in this.styles) {
55746             hasStyles = true;
55747             break;
55748         }
55749         
55750         // update styles
55751         if (hasStyles && this.tb.hasStyles) { 
55752             var st = this.tb.fields.item(0);
55753             
55754             st.store.removeAll();
55755             var cn = sel.className.split(/\s+/);
55756             
55757             var avs = [];
55758             if (this.styles['*']) {
55759                 
55760                 Roo.each(this.styles['*'], function(v) {
55761                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
55762                 });
55763             }
55764             if (this.styles[tn]) { 
55765                 Roo.each(this.styles[tn], function(v) {
55766                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
55767                 });
55768             }
55769             
55770             st.store.loadData(avs);
55771             st.collapse();
55772             st.setValue(cn);
55773         }
55774     },
55775     
55776      
55777     updateFooter : function(ans)
55778     {
55779         var html = '';
55780         if (ans === false) {
55781             this.footDisp.dom.innerHTML = '';
55782             return;
55783         }
55784         
55785         this.footerEls = ans.reverse();
55786         Roo.each(this.footerEls, function(a,i) {
55787             if (!a) { return; }
55788             html += html.length ? ' &gt; '  :  '';
55789             
55790             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
55791             
55792         });
55793        
55794         // 
55795         var sz = this.footDisp.up('td').getSize();
55796         this.footDisp.dom.style.width = (sz.width -10) + 'px';
55797         this.footDisp.dom.style.marginLeft = '5px';
55798         
55799         this.footDisp.dom.style.overflow = 'hidden';
55800         
55801         this.footDisp.dom.innerHTML = html;
55802             
55803         
55804     },
55805    
55806        
55807     // private
55808     onDestroy : function(){
55809         if(this.rendered){
55810             
55811             this.tb.items.each(function(item){
55812                 if(item.menu){
55813                     item.menu.removeAll();
55814                     if(item.menu.el){
55815                         item.menu.el.destroy();
55816                     }
55817                 }
55818                 item.destroy();
55819             });
55820              
55821         }
55822     },
55823     onFirstFocus: function() {
55824         // need to do this for all the toolbars..
55825         this.tb.items.each(function(item){
55826            item.enable();
55827         });
55828     },
55829     buildToolbar: function(tlist, nm, friendly_name, block)
55830     {
55831         var editor = this.editor;
55832         var editorcore = this.editorcore;
55833          // create a new element.
55834         var wdiv = editor.wrap.createChild({
55835                 tag: 'div'
55836             }, editor.wrap.dom.firstChild.nextSibling, true);
55837         
55838        
55839         var tb = new Roo.Toolbar(wdiv);
55840         ///this.tb = tb; // << this sets the active toolbar..
55841         if (tlist === false && block) {
55842             tlist = block.contextMenu(this);
55843         }
55844         
55845         tb.hasStyles = false;
55846         tb.name = nm;
55847         
55848         tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ":&nbsp;");
55849         
55850         var styles = Array.from(this.styles);
55851         
55852         
55853         // styles...
55854         if (styles && styles.length) {
55855             tb.hasStyles = true;
55856             // this needs a multi-select checkbox...
55857             tb.addField( new Roo.form.ComboBox({
55858                 store: new Roo.data.SimpleStore({
55859                     id : 'val',
55860                     fields: ['val', 'selected'],
55861                     data : [] 
55862                 }),
55863                 name : '-roo-edit-className',
55864                 attrname : 'className',
55865                 displayField: 'val',
55866                 typeAhead: false,
55867                 mode: 'local',
55868                 editable : false,
55869                 triggerAction: 'all',
55870                 emptyText:'Select Style',
55871                 selectOnFocus:true,
55872                 width: 130,
55873                 listeners : {
55874                     'select': function(c, r, i) {
55875                         // initial support only for on class per el..
55876                         tb.selectedNode.className =  r ? r.get('val') : '';
55877                         editorcore.syncValue();
55878                     }
55879                 }
55880     
55881             }));
55882         }
55883         
55884         var tbc = Roo.form.HtmlEditor.ToolbarContext;
55885         
55886         
55887         for (var i = 0; i < tlist.length; i++) {
55888             
55889             // newer versions will use xtype cfg to create menus.
55890             if (typeof(tlist[i].xtype) != 'undefined') {
55891                 
55892                 tb[typeof(tlist[i].name)== 'undefined' ? 'add' : 'addField'](Roo.factory(tlist[i]));
55893                 
55894                 
55895                 continue;
55896             }
55897             
55898             var item = tlist[i];
55899             tb.add(item.title + ":&nbsp;");
55900             
55901             
55902             //optname == used so you can configure the options available..
55903             var opts = item.opts ? item.opts : false;
55904             if (item.optname) { // use the b
55905                 opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
55906            
55907             }
55908             
55909             if (opts) {
55910                 // opts == pulldown..
55911                 tb.addField( new Roo.form.ComboBox({
55912                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
55913                         id : 'val',
55914                         fields: ['val', 'display'],
55915                         data : opts  
55916                     }),
55917                     name : '-roo-edit-' + tlist[i].name,
55918                     
55919                     attrname : tlist[i].name,
55920                     stylename : item.style ? item.style : false,
55921                     
55922                     displayField: item.displayField ? item.displayField : 'val',
55923                     valueField :  'val',
55924                     typeAhead: false,
55925                     mode: typeof(tbc.stores[tlist[i].name]) != 'undefined'  ? 'remote' : 'local',
55926                     editable : false,
55927                     triggerAction: 'all',
55928                     emptyText:'Select',
55929                     selectOnFocus:true,
55930                     width: item.width ? item.width  : 130,
55931                     listeners : {
55932                         'select': function(c, r, i) {
55933                              
55934                             
55935                             if (c.stylename) {
55936                                 tb.selectedNode.style[c.stylename] =  r.get('val');
55937                                 editorcore.syncValue();
55938                                 return;
55939                             }
55940                             if (r === false) {
55941                                 tb.selectedNode.removeAttribute(c.attrname);
55942                                 editorcore.syncValue();
55943                                 return;
55944                             }
55945                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
55946                             editorcore.syncValue();
55947                         }
55948                     }
55949
55950                 }));
55951                 continue;
55952                     
55953                  
55954                 /*
55955                 tb.addField( new Roo.form.TextField({
55956                     name: i,
55957                     width: 100,
55958                     //allowBlank:false,
55959                     value: ''
55960                 }));
55961                 continue;
55962                 */
55963             }
55964             tb.addField( new Roo.form.TextField({
55965                 name: '-roo-edit-' + tlist[i].name,
55966                 attrname : tlist[i].name,
55967                 
55968                 width: item.width,
55969                 //allowBlank:true,
55970                 value: '',
55971                 listeners: {
55972                     'change' : function(f, nv, ov) {
55973                         
55974                          
55975                         tb.selectedNode.setAttribute(f.attrname, nv);
55976                         editorcore.syncValue();
55977                     }
55978                 }
55979             }));
55980              
55981         }
55982         
55983         var _this = this;
55984         var show_delete = !block || block.deleteTitle !== false;
55985         if(nm == 'BODY'){
55986             show_delete = false;
55987             tb.addSeparator();
55988         
55989             tb.addButton( {
55990                 text: 'Stylesheets',
55991
55992                 listeners : {
55993                     click : function ()
55994                     {
55995                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
55996                     }
55997                 }
55998             });
55999         }
56000         
56001         tb.addFill();
56002         if (show_delete) {
56003             tb.addButton({
56004                 text: block && block.deleteTitle ? block.deleteTitle  : 'Remove Block or Formating', // remove the tag, and puts the children outside...
56005         
56006                 listeners : {
56007                     click : function ()
56008                     {
56009                         var sn = tb.selectedNode;
56010                         if (block) {
56011                             sn = Roo.htmleditor.Block.factory(tb.selectedNode).removeNode();
56012                             
56013                         }
56014                         if (!sn) {
56015                             return;
56016                         }
56017                         var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
56018                         if (sn.hasAttribute('data-block')) {
56019                             stn =  sn.nextSibling || sn.previousSibling || sn.parentNode;
56020                             sn.parentNode.removeChild(sn);
56021                             
56022                         } else if (sn && sn.tagName != 'BODY') {
56023                             // remove and keep parents.
56024                             a = new Roo.htmleditor.FilterKeepChildren({tag : false});
56025                             a.replaceTag(sn);
56026                         }
56027                         
56028                         
56029                         var range = editorcore.createRange();
56030             
56031                         range.setStart(stn,0);
56032                         range.setEnd(stn,0); 
56033                         var selection = editorcore.getSelection();
56034                         selection.removeAllRanges();
56035                         selection.addRange(range);
56036                         
56037                         
56038                         //_this.updateToolbar(null, null, pn);
56039                         _this.updateToolbar(null, null, null);
56040                         _this.updateFooter(false);
56041                         
56042                     }
56043                 }
56044                 
56045                         
56046                     
56047                 
56048             });
56049         }    
56050         
56051         tb.el.on('click', function(e){
56052             e.preventDefault(); // what does this do?
56053         });
56054         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
56055         tb.el.hide();
56056         
56057         // dont need to disable them... as they will get hidden
56058         return tb;
56059          
56060         
56061     },
56062     buildFooter : function()
56063     {
56064         
56065         var fel = this.editor.wrap.createChild();
56066         this.footer = new Roo.Toolbar(fel);
56067         // toolbar has scrolly on left / right?
56068         var footDisp= new Roo.Toolbar.Fill();
56069         var _t = this;
56070         this.footer.add(
56071             {
56072                 text : '&lt;',
56073                 xtype: 'Button',
56074                 handler : function() {
56075                     _t.footDisp.scrollTo('left',0,true)
56076                 }
56077             }
56078         );
56079         this.footer.add( footDisp );
56080         this.footer.add( 
56081             {
56082                 text : '&gt;',
56083                 xtype: 'Button',
56084                 handler : function() {
56085                     // no animation..
56086                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
56087                 }
56088             }
56089         );
56090         var fel = Roo.get(footDisp.el);
56091         fel.addClass('x-editor-context');
56092         this.footDispWrap = fel; 
56093         this.footDispWrap.overflow  = 'hidden';
56094         
56095         this.footDisp = fel.createChild();
56096         this.footDispWrap.on('click', this.onContextClick, this)
56097         
56098         
56099     },
56100     // when the footer contect changes
56101     onContextClick : function (ev,dom)
56102     {
56103         ev.preventDefault();
56104         var  cn = dom.className;
56105         //Roo.log(cn);
56106         if (!cn.match(/x-ed-loc-/)) {
56107             return;
56108         }
56109         var n = cn.split('-').pop();
56110         var ans = this.footerEls;
56111         var sel = ans[n];
56112         
56113         this.editorcore.selectNode(sel);
56114         
56115         
56116         this.updateToolbar(null, null, sel);
56117         
56118         
56119     }
56120     
56121     
56122     
56123     
56124     
56125 });
56126
56127
56128
56129
56130
56131 /*
56132  * Based on:
56133  * Ext JS Library 1.1.1
56134  * Copyright(c) 2006-2007, Ext JS, LLC.
56135  *
56136  * Originally Released Under LGPL - original licence link has changed is not relivant.
56137  *
56138  * Fork - LGPL
56139  * <script type="text/javascript">
56140  */
56141  
56142 /**
56143  * @class Roo.form.BasicForm
56144  * @extends Roo.util.Observable
56145  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
56146  * @constructor
56147  * @param {String/HTMLElement/Roo.Element} el The form element or its id
56148  * @param {Object} config Configuration options
56149  */
56150 Roo.form.BasicForm = function(el, config){
56151     this.allItems = [];
56152     this.childForms = [];
56153     Roo.apply(this, config);
56154     /*
56155      * The Roo.form.Field items in this form.
56156      * @type MixedCollection
56157      */
56158      
56159      
56160     this.items = new Roo.util.MixedCollection(false, function(o){
56161         return o.id || (o.id = Roo.id());
56162     });
56163     this.addEvents({
56164         /**
56165          * @event beforeaction
56166          * Fires before any action is performed. Return false to cancel the action.
56167          * @param {Form} this
56168          * @param {Action} action The action to be performed
56169          */
56170         beforeaction: true,
56171         /**
56172          * @event actionfailed
56173          * Fires when an action fails.
56174          * @param {Form} this
56175          * @param {Action} action The action that failed
56176          */
56177         actionfailed : true,
56178         /**
56179          * @event actioncomplete
56180          * Fires when an action is completed.
56181          * @param {Form} this
56182          * @param {Action} action The action that completed
56183          */
56184         actioncomplete : true
56185     });
56186     if(el){
56187         this.initEl(el);
56188     }
56189     Roo.form.BasicForm.superclass.constructor.call(this);
56190     
56191     Roo.form.BasicForm.popover.apply();
56192 };
56193
56194 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
56195     /**
56196      * @cfg {String} method
56197      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
56198      */
56199     /**
56200      * @cfg {DataReader} reader
56201      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
56202      * This is optional as there is built-in support for processing JSON.
56203      */
56204     /**
56205      * @cfg {DataReader} errorReader
56206      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
56207      * This is completely optional as there is built-in support for processing JSON.
56208      */
56209     /**
56210      * @cfg {String} url
56211      * The URL to use for form actions if one isn't supplied in the action options.
56212      */
56213     /**
56214      * @cfg {Boolean} fileUpload
56215      * Set to true if this form is a file upload.
56216      */
56217      
56218     /**
56219      * @cfg {Object} baseParams
56220      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
56221      */
56222      /**
56223      
56224     /**
56225      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
56226      */
56227     timeout: 30,
56228
56229     // private
56230     activeAction : null,
56231
56232     /**
56233      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
56234      * or setValues() data instead of when the form was first created.
56235      */
56236     trackResetOnLoad : false,
56237     
56238     
56239     /**
56240      * childForms - used for multi-tab forms
56241      * @type {Array}
56242      */
56243     childForms : false,
56244     
56245     /**
56246      * allItems - full list of fields.
56247      * @type {Array}
56248      */
56249     allItems : false,
56250     
56251     /**
56252      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
56253      * element by passing it or its id or mask the form itself by passing in true.
56254      * @type Mixed
56255      */
56256     waitMsgTarget : false,
56257     
56258     /**
56259      * @type Boolean
56260      */
56261     disableMask : false,
56262     
56263     /**
56264      * @cfg {Boolean} errorMask Should the form be masked (and the active element highlighted on error - default false
56265      */
56266     errorMask : false,
56267     
56268     /**
56269      * @cfg {Number} maskOffset space around form element to mask if there is an error Default 100
56270      */
56271     maskOffset : 100,
56272
56273     // private
56274     initEl : function(el){
56275         this.el = Roo.get(el);
56276         this.id = this.el.id || Roo.id();
56277         this.el.on('submit', this.onSubmit, this);
56278         this.el.addClass('x-form');
56279     },
56280
56281     // private
56282     onSubmit : function(e){
56283         e.stopEvent();
56284     },
56285
56286     /**
56287      * Returns true if client-side validation on the form is successful.
56288      * @return Boolean
56289      */
56290     isValid : function(){
56291         var valid = true;
56292         var target = false;
56293         this.items.each(function(f){
56294             if(f.validate()){
56295                 return;
56296             }
56297             
56298             valid = false;
56299                 
56300             if(!target && f.el.isVisible(true)){
56301                 target = f;
56302             }
56303         });
56304         
56305         if(this.errorMask && !valid){
56306             Roo.form.BasicForm.popover.mask(this, target);
56307         }
56308         
56309         return valid;
56310     },
56311     /**
56312      * Returns array of invalid form fields.
56313      * @return Array
56314      */
56315     
56316     invalidFields : function()
56317     {
56318         var ret = [];
56319         this.items.each(function(f){
56320             if(f.validate()){
56321                 return;
56322             }
56323             ret.push(f);
56324             
56325         });
56326         
56327         return ret;
56328     },
56329     
56330     
56331     /**
56332      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
56333      * @return Boolean
56334      */
56335     isDirty : function(){
56336         var dirty = false;
56337         this.items.each(function(f){
56338            if(f.isDirty()){
56339                dirty = true;
56340                return false;
56341            }
56342         });
56343         return dirty;
56344     },
56345     
56346     /**
56347      * Returns true if any fields in this form have changed since their original load. (New version)
56348      * @return Boolean
56349      */
56350     
56351     hasChanged : function()
56352     {
56353         var dirty = false;
56354         this.items.each(function(f){
56355            if(f.hasChanged()){
56356                dirty = true;
56357                return false;
56358            }
56359         });
56360         return dirty;
56361         
56362     },
56363     /**
56364      * Resets all hasChanged to 'false' -
56365      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
56366      * So hasChanged storage is only to be used for this purpose
56367      * @return Boolean
56368      */
56369     resetHasChanged : function()
56370     {
56371         this.items.each(function(f){
56372            f.resetHasChanged();
56373         });
56374         
56375     },
56376     
56377     
56378     /**
56379      * Performs a predefined action (submit or load) or custom actions you define on this form.
56380      * @param {String} actionName The name of the action type
56381      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
56382      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
56383      * accept other config options):
56384      * <pre>
56385 Property          Type             Description
56386 ----------------  ---------------  ----------------------------------------------------------------------------------
56387 url               String           The url for the action (defaults to the form's url)
56388 method            String           The form method to use (defaults to the form's method, or POST if not defined)
56389 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
56390 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
56391                                    validate the form on the client (defaults to false)
56392      * </pre>
56393      * @return {BasicForm} this
56394      */
56395     doAction : function(action, options){
56396         if(typeof action == 'string'){
56397             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
56398         }
56399         if(this.fireEvent('beforeaction', this, action) !== false){
56400             this.beforeAction(action);
56401             action.run.defer(100, action);
56402         }
56403         return this;
56404     },
56405
56406     /**
56407      * Shortcut to do a submit action.
56408      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
56409      * @return {BasicForm} this
56410      */
56411     submit : function(options){
56412         this.doAction('submit', options);
56413         return this;
56414     },
56415
56416     /**
56417      * Shortcut to do a load action.
56418      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
56419      * @return {BasicForm} this
56420      */
56421     load : function(options){
56422         this.doAction('load', options);
56423         return this;
56424     },
56425
56426     /**
56427      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
56428      * @param {Record} record The record to edit
56429      * @return {BasicForm} this
56430      */
56431     updateRecord : function(record){
56432         record.beginEdit();
56433         var fs = record.fields;
56434         fs.each(function(f){
56435             var field = this.findField(f.name);
56436             if(field){
56437                 record.set(f.name, field.getValue());
56438             }
56439         }, this);
56440         record.endEdit();
56441         return this;
56442     },
56443
56444     /**
56445      * Loads an Roo.data.Record into this form.
56446      * @param {Record} record The record to load
56447      * @return {BasicForm} this
56448      */
56449     loadRecord : function(record){
56450         this.setValues(record.data);
56451         return this;
56452     },
56453
56454     // private
56455     beforeAction : function(action){
56456         var o = action.options;
56457         
56458         if(!this.disableMask) {
56459             if(this.waitMsgTarget === true){
56460                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
56461             }else if(this.waitMsgTarget){
56462                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
56463                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
56464             }else {
56465                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
56466             }
56467         }
56468         
56469          
56470     },
56471
56472     // private
56473     afterAction : function(action, success){
56474         this.activeAction = null;
56475         var o = action.options;
56476         
56477         if(!this.disableMask) {
56478             if(this.waitMsgTarget === true){
56479                 this.el.unmask();
56480             }else if(this.waitMsgTarget){
56481                 this.waitMsgTarget.unmask();
56482             }else{
56483                 Roo.MessageBox.updateProgress(1);
56484                 Roo.MessageBox.hide();
56485             }
56486         }
56487         
56488         if(success){
56489             if(o.reset){
56490                 this.reset();
56491             }
56492             Roo.callback(o.success, o.scope, [this, action]);
56493             this.fireEvent('actioncomplete', this, action);
56494             
56495         }else{
56496             
56497             // failure condition..
56498             // we have a scenario where updates need confirming.
56499             // eg. if a locking scenario exists..
56500             // we look for { errors : { needs_confirm : true }} in the response.
56501             if (
56502                 (typeof(action.result) != 'undefined')  &&
56503                 (typeof(action.result.errors) != 'undefined')  &&
56504                 (typeof(action.result.errors.needs_confirm) != 'undefined')
56505            ){
56506                 var _t = this;
56507                 Roo.MessageBox.confirm(
56508                     "Change requires confirmation",
56509                     action.result.errorMsg,
56510                     function(r) {
56511                         if (r != 'yes') {
56512                             return;
56513                         }
56514                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
56515                     }
56516                     
56517                 );
56518                 
56519                 
56520                 
56521                 return;
56522             }
56523             
56524             Roo.callback(o.failure, o.scope, [this, action]);
56525             // show an error message if no failed handler is set..
56526             if (!this.hasListener('actionfailed')) {
56527                 Roo.MessageBox.alert("Error",
56528                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
56529                         action.result.errorMsg :
56530                         "Saving Failed, please check your entries or try again"
56531                 );
56532             }
56533             
56534             this.fireEvent('actionfailed', this, action);
56535         }
56536         
56537     },
56538
56539     /**
56540      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
56541      * @param {String} id The value to search for
56542      * @return Field
56543      */
56544     findField : function(id){
56545         var field = this.items.get(id);
56546         if(!field){
56547             this.items.each(function(f){
56548                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
56549                     field = f;
56550                     return false;
56551                 }
56552             });
56553         }
56554         return field || null;
56555     },
56556
56557     /**
56558      * Add a secondary form to this one, 
56559      * Used to provide tabbed forms. One form is primary, with hidden values 
56560      * which mirror the elements from the other forms.
56561      * 
56562      * @param {Roo.form.Form} form to add.
56563      * 
56564      */
56565     addForm : function(form)
56566     {
56567        
56568         if (this.childForms.indexOf(form) > -1) {
56569             // already added..
56570             return;
56571         }
56572         this.childForms.push(form);
56573         var n = '';
56574         Roo.each(form.allItems, function (fe) {
56575             
56576             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
56577             if (this.findField(n)) { // already added..
56578                 return;
56579             }
56580             var add = new Roo.form.Hidden({
56581                 name : n
56582             });
56583             add.render(this.el);
56584             
56585             this.add( add );
56586         }, this);
56587         
56588     },
56589     /**
56590      * Mark fields in this form invalid in bulk.
56591      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
56592      * @return {BasicForm} this
56593      */
56594     markInvalid : function(errors){
56595         if(errors instanceof Array){
56596             for(var i = 0, len = errors.length; i < len; i++){
56597                 var fieldError = errors[i];
56598                 var f = this.findField(fieldError.id);
56599                 if(f){
56600                     f.markInvalid(fieldError.msg);
56601                 }
56602             }
56603         }else{
56604             var field, id;
56605             for(id in errors){
56606                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
56607                     field.markInvalid(errors[id]);
56608                 }
56609             }
56610         }
56611         Roo.each(this.childForms || [], function (f) {
56612             f.markInvalid(errors);
56613         });
56614         
56615         return this;
56616     },
56617
56618     /**
56619      * Set values for fields in this form in bulk.
56620      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
56621      * @return {BasicForm} this
56622      */
56623     setValues : function(values){
56624         if(values instanceof Array){ // array of objects
56625             for(var i = 0, len = values.length; i < len; i++){
56626                 var v = values[i];
56627                 var f = this.findField(v.id);
56628                 if(f){
56629                     f.setValue(v.value);
56630                     if(this.trackResetOnLoad){
56631                         f.originalValue = f.getValue();
56632                     }
56633                 }
56634             }
56635         }else{ // object hash
56636             var field, id;
56637             for(id in values){
56638                 if(typeof values[id] != 'function' && (field = this.findField(id))){
56639                     
56640                     
56641                     
56642                     
56643                     if (field.setFromData && 
56644                         field.valueField && 
56645                         field.displayField &&
56646                         // combos' with local stores can 
56647                         // be queried via setValue()
56648                         // to set their value..
56649                         (field.store && !field.store.isLocal)
56650                         ) {
56651                         // it's a combo
56652                         var sd = { };
56653                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
56654                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
56655                         field.setFromData(sd);
56656                         
56657                     } else if (field.inputType && field.inputType == 'radio') {
56658                         
56659                         field.setValue(values[id]);
56660                     } else {
56661                         field.setValue(values[id]);
56662                     }
56663                     
56664                     
56665                     if(this.trackResetOnLoad){
56666                         field.originalValue = field.getValue();
56667                     }
56668                 }
56669             }
56670         }
56671         this.resetHasChanged();
56672         
56673         
56674         Roo.each(this.childForms || [], function (f) {
56675             f.setValues(values);
56676             f.resetHasChanged();
56677         });
56678                 
56679         return this;
56680     },
56681  
56682     /**
56683      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
56684      * they are returned as an array.
56685      * @param {Boolean} asString (def)
56686      * @return {Object}
56687      */
56688     getValues : function(asString)
56689     {
56690         if (this.childForms) {
56691             // copy values from the child forms
56692             Roo.each(this.childForms, function (f) {
56693                 this.setValues(f.getFieldValues()); // get the full set of data, as we might be copying comboboxes from external into this one.
56694             }, this);
56695         }
56696         
56697         // use formdata
56698         if (typeof(FormData) != 'undefined' && asString !== true) {
56699             // this relies on a 'recent' version of chrome apparently...
56700             try {
56701                 var fd = (new FormData(this.el.dom)).entries();
56702                 var ret = {};
56703                 var ent = fd.next();
56704                 while (!ent.done) {
56705                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
56706                     ent = fd.next();
56707                 };
56708                 return ret;
56709             } catch(e) {
56710                 
56711             }
56712             
56713         }
56714         
56715         
56716         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
56717         if(asString === true){
56718             return fs;
56719         }
56720         return Roo.urlDecode(fs);
56721     },
56722     
56723     /**
56724      * Returns the fields in this form as an object with key/value pairs. 
56725      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
56726      * Normally this will not return readOnly data 
56727      * @param {Boolean} with_readonly return readonly field data.
56728      * @return {Object}
56729      */
56730     getFieldValues : function(with_readonly)
56731     {
56732         if (this.childForms) {
56733             // copy values from the child forms
56734             // should this call getFieldValues - probably not as we do not currently copy
56735             // hidden fields when we generate..
56736             Roo.each(this.childForms, function (f) {
56737                 this.setValues(f.getFieldValues());
56738             }, this);
56739         }
56740         
56741         var ret = {};
56742         this.items.each(function(f){
56743             
56744             if (f.readOnly && with_readonly !== true) {
56745                 return; // skip read only values. - this is in theory to stop 'old' values being copied over new ones
56746                         // if a subform contains a copy of them.
56747                         // if you have subforms with the same editable data, you will need to copy the data back
56748                         // and forth.
56749             }
56750             
56751             if (!f.getName()) {
56752                 return;
56753             }
56754             var v = f.getValue();
56755             if (f.inputType =='radio') {
56756                 if (typeof(ret[f.getName()]) == 'undefined') {
56757                     ret[f.getName()] = ''; // empty..
56758                 }
56759                 
56760                 if (!f.el.dom.checked) {
56761                     return;
56762                     
56763                 }
56764                 v = f.el.dom.value;
56765                 
56766             }
56767             
56768             // not sure if this supported any more..
56769             if ((typeof(v) == 'object') && f.getRawValue) {
56770                 v = f.getRawValue() ; // dates..
56771             }
56772             // combo boxes where name != hiddenName...
56773             if (f.name != f.getName()) {
56774                 ret[f.name] = f.getRawValue();
56775             }
56776             ret[f.getName()] = v;
56777         });
56778         
56779         return ret;
56780     },
56781
56782     /**
56783      * Clears all invalid messages in this form.
56784      * @return {BasicForm} this
56785      */
56786     clearInvalid : function(){
56787         this.items.each(function(f){
56788            f.clearInvalid();
56789         });
56790         
56791         Roo.each(this.childForms || [], function (f) {
56792             f.clearInvalid();
56793         });
56794         
56795         
56796         return this;
56797     },
56798
56799     /**
56800      * Resets this form.
56801      * @return {BasicForm} this
56802      */
56803     reset : function(){
56804         this.items.each(function(f){
56805             f.reset();
56806         });
56807         
56808         Roo.each(this.childForms || [], function (f) {
56809             f.reset();
56810         });
56811         this.resetHasChanged();
56812         
56813         return this;
56814     },
56815
56816     /**
56817      * Add Roo.form components to this form.
56818      * @param {Field} field1
56819      * @param {Field} field2 (optional)
56820      * @param {Field} etc (optional)
56821      * @return {BasicForm} this
56822      */
56823     add : function(){
56824         this.items.addAll(Array.prototype.slice.call(arguments, 0));
56825         return this;
56826     },
56827
56828
56829     /**
56830      * Removes a field from the items collection (does NOT remove its markup).
56831      * @param {Field} field
56832      * @return {BasicForm} this
56833      */
56834     remove : function(field){
56835         this.items.remove(field);
56836         return this;
56837     },
56838
56839     /**
56840      * Looks at the fields in this form, checks them for an id attribute,
56841      * and calls applyTo on the existing dom element with that id.
56842      * @return {BasicForm} this
56843      */
56844     render : function(){
56845         this.items.each(function(f){
56846             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
56847                 f.applyTo(f.id);
56848             }
56849         });
56850         return this;
56851     },
56852
56853     /**
56854      * Calls {@link Ext#apply} for all fields in this form with the passed object.
56855      * @param {Object} values
56856      * @return {BasicForm} this
56857      */
56858     applyToFields : function(o){
56859         this.items.each(function(f){
56860            Roo.apply(f, o);
56861         });
56862         return this;
56863     },
56864
56865     /**
56866      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
56867      * @param {Object} values
56868      * @return {BasicForm} this
56869      */
56870     applyIfToFields : function(o){
56871         this.items.each(function(f){
56872            Roo.applyIf(f, o);
56873         });
56874         return this;
56875     }
56876 });
56877
56878 // back compat
56879 Roo.BasicForm = Roo.form.BasicForm;
56880
56881 Roo.apply(Roo.form.BasicForm, {
56882     
56883     popover : {
56884         
56885         padding : 5,
56886         
56887         isApplied : false,
56888         
56889         isMasked : false,
56890         
56891         form : false,
56892         
56893         target : false,
56894         
56895         intervalID : false,
56896         
56897         maskEl : false,
56898         
56899         apply : function()
56900         {
56901             if(this.isApplied){
56902                 return;
56903             }
56904             
56905             this.maskEl = {
56906                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
56907                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
56908                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
56909                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
56910             };
56911             
56912             this.maskEl.top.enableDisplayMode("block");
56913             this.maskEl.left.enableDisplayMode("block");
56914             this.maskEl.bottom.enableDisplayMode("block");
56915             this.maskEl.right.enableDisplayMode("block");
56916             
56917             Roo.get(document.body).on('click', function(){
56918                 this.unmask();
56919             }, this);
56920             
56921             Roo.get(document.body).on('touchstart', function(){
56922                 this.unmask();
56923             }, this);
56924             
56925             this.isApplied = true
56926         },
56927         
56928         mask : function(form, target)
56929         {
56930             this.form = form;
56931             
56932             this.target = target;
56933             
56934             if(!this.form.errorMask || !target.el){
56935                 return;
56936             }
56937             
56938             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
56939             
56940             var ot = this.target.el.calcOffsetsTo(scrollable);
56941             
56942             var scrollTo = ot[1] - this.form.maskOffset;
56943             
56944             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
56945             
56946             scrollable.scrollTo('top', scrollTo);
56947             
56948             var el = this.target.wrap || this.target.el;
56949             
56950             var box = el.getBox();
56951             
56952             this.maskEl.top.setStyle('position', 'absolute');
56953             this.maskEl.top.setStyle('z-index', 10000);
56954             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
56955             this.maskEl.top.setLeft(0);
56956             this.maskEl.top.setTop(0);
56957             this.maskEl.top.show();
56958             
56959             this.maskEl.left.setStyle('position', 'absolute');
56960             this.maskEl.left.setStyle('z-index', 10000);
56961             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
56962             this.maskEl.left.setLeft(0);
56963             this.maskEl.left.setTop(box.y - this.padding);
56964             this.maskEl.left.show();
56965
56966             this.maskEl.bottom.setStyle('position', 'absolute');
56967             this.maskEl.bottom.setStyle('z-index', 10000);
56968             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
56969             this.maskEl.bottom.setLeft(0);
56970             this.maskEl.bottom.setTop(box.bottom + this.padding);
56971             this.maskEl.bottom.show();
56972
56973             this.maskEl.right.setStyle('position', 'absolute');
56974             this.maskEl.right.setStyle('z-index', 10000);
56975             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
56976             this.maskEl.right.setLeft(box.right + this.padding);
56977             this.maskEl.right.setTop(box.y - this.padding);
56978             this.maskEl.right.show();
56979
56980             this.intervalID = window.setInterval(function() {
56981                 Roo.form.BasicForm.popover.unmask();
56982             }, 10000);
56983
56984             window.onwheel = function(){ return false;};
56985             
56986             (function(){ this.isMasked = true; }).defer(500, this);
56987             
56988         },
56989         
56990         unmask : function()
56991         {
56992             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
56993                 return;
56994             }
56995             
56996             this.maskEl.top.setStyle('position', 'absolute');
56997             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
56998             this.maskEl.top.hide();
56999
57000             this.maskEl.left.setStyle('position', 'absolute');
57001             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
57002             this.maskEl.left.hide();
57003
57004             this.maskEl.bottom.setStyle('position', 'absolute');
57005             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
57006             this.maskEl.bottom.hide();
57007
57008             this.maskEl.right.setStyle('position', 'absolute');
57009             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
57010             this.maskEl.right.hide();
57011             
57012             window.onwheel = function(){ return true;};
57013             
57014             if(this.intervalID){
57015                 window.clearInterval(this.intervalID);
57016                 this.intervalID = false;
57017             }
57018             
57019             this.isMasked = false;
57020             
57021         }
57022         
57023     }
57024     
57025 });/*
57026  * Based on:
57027  * Ext JS Library 1.1.1
57028  * Copyright(c) 2006-2007, Ext JS, LLC.
57029  *
57030  * Originally Released Under LGPL - original licence link has changed is not relivant.
57031  *
57032  * Fork - LGPL
57033  * <script type="text/javascript">
57034  */
57035
57036 /**
57037  * @class Roo.form.Form
57038  * @extends Roo.form.BasicForm
57039  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
57040  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
57041  * @constructor
57042  * @param {Object} config Configuration options
57043  */
57044 Roo.form.Form = function(config){
57045     var xitems =  [];
57046     if (config.items) {
57047         xitems = config.items;
57048         delete config.items;
57049     }
57050    
57051     
57052     Roo.form.Form.superclass.constructor.call(this, null, config);
57053     this.url = this.url || this.action;
57054     if(!this.root){
57055         this.root = new Roo.form.Layout(Roo.applyIf({
57056             id: Roo.id()
57057         }, config));
57058     }
57059     this.active = this.root;
57060     /**
57061      * Array of all the buttons that have been added to this form via {@link addButton}
57062      * @type Array
57063      */
57064     this.buttons = [];
57065     this.allItems = [];
57066     this.addEvents({
57067         /**
57068          * @event clientvalidation
57069          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
57070          * @param {Form} this
57071          * @param {Boolean} valid true if the form has passed client-side validation
57072          */
57073         clientvalidation: true,
57074         /**
57075          * @event rendered
57076          * Fires when the form is rendered
57077          * @param {Roo.form.Form} form
57078          */
57079         rendered : true
57080     });
57081     
57082     if (this.progressUrl) {
57083             // push a hidden field onto the list of fields..
57084             this.addxtype( {
57085                     xns: Roo.form, 
57086                     xtype : 'Hidden', 
57087                     name : 'UPLOAD_IDENTIFIER' 
57088             });
57089         }
57090         
57091     
57092     Roo.each(xitems, this.addxtype, this);
57093     
57094 };
57095
57096 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
57097      /**
57098      * @cfg {Roo.Button} buttons[] buttons at bottom of form
57099      */
57100     
57101     /**
57102      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
57103      */
57104     /**
57105      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
57106      */
57107     /**
57108      * @cfg {String} buttonAlign (left|center|right)  Valid values are "left," "center" and "right" (defaults to "center")
57109      */
57110     buttonAlign:'center',
57111
57112     /**
57113      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
57114      */
57115     minButtonWidth:75,
57116
57117     /**
57118      * @cfg {String} labelAlign (left|top|right) Valid values are "left," "top" and "right" (defaults to "left").
57119      * This property cascades to child containers if not set.
57120      */
57121     labelAlign:'left',
57122
57123     /**
57124      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
57125      * fires a looping event with that state. This is required to bind buttons to the valid
57126      * state using the config value formBind:true on the button.
57127      */
57128     monitorValid : false,
57129
57130     /**
57131      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
57132      */
57133     monitorPoll : 200,
57134     
57135     /**
57136      * @cfg {String} progressUrl - Url to return progress data 
57137      */
57138     
57139     progressUrl : false,
57140     /**
57141      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
57142      * sending a formdata with extra parameters - eg uploaded elements.
57143      */
57144     
57145     formData : false,
57146     
57147     /**
57148      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
57149      * fields are added and the column is closed. If no fields are passed the column remains open
57150      * until end() is called.
57151      * @param {Object} config The config to pass to the column
57152      * @param {Field} field1 (optional)
57153      * @param {Field} field2 (optional)
57154      * @param {Field} etc (optional)
57155      * @return Column The column container object
57156      */
57157     column : function(c){
57158         var col = new Roo.form.Column(c);
57159         this.start(col);
57160         if(arguments.length > 1){ // duplicate code required because of Opera
57161             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
57162             this.end();
57163         }
57164         return col;
57165     },
57166
57167     /**
57168      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
57169      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
57170      * until end() is called.
57171      * @param {Object} config The config to pass to the fieldset
57172      * @param {Field} field1 (optional)
57173      * @param {Field} field2 (optional)
57174      * @param {Field} etc (optional)
57175      * @return FieldSet The fieldset container object
57176      */
57177     fieldset : function(c){
57178         var fs = new Roo.form.FieldSet(c);
57179         this.start(fs);
57180         if(arguments.length > 1){ // duplicate code required because of Opera
57181             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
57182             this.end();
57183         }
57184         return fs;
57185     },
57186
57187     /**
57188      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
57189      * fields are added and the container is closed. If no fields are passed the container remains open
57190      * until end() is called.
57191      * @param {Object} config The config to pass to the Layout
57192      * @param {Field} field1 (optional)
57193      * @param {Field} field2 (optional)
57194      * @param {Field} etc (optional)
57195      * @return Layout The container object
57196      */
57197     container : function(c){
57198         var l = new Roo.form.Layout(c);
57199         this.start(l);
57200         if(arguments.length > 1){ // duplicate code required because of Opera
57201             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
57202             this.end();
57203         }
57204         return l;
57205     },
57206
57207     /**
57208      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
57209      * @param {Object} container A Roo.form.Layout or subclass of Layout
57210      * @return {Form} this
57211      */
57212     start : function(c){
57213         // cascade label info
57214         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
57215         this.active.stack.push(c);
57216         c.ownerCt = this.active;
57217         this.active = c;
57218         return this;
57219     },
57220
57221     /**
57222      * Closes the current open container
57223      * @return {Form} this
57224      */
57225     end : function(){
57226         if(this.active == this.root){
57227             return this;
57228         }
57229         this.active = this.active.ownerCt;
57230         return this;
57231     },
57232
57233     /**
57234      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
57235      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
57236      * as the label of the field.
57237      * @param {Field} field1
57238      * @param {Field} field2 (optional)
57239      * @param {Field} etc. (optional)
57240      * @return {Form} this
57241      */
57242     add : function(){
57243         this.active.stack.push.apply(this.active.stack, arguments);
57244         this.allItems.push.apply(this.allItems,arguments);
57245         var r = [];
57246         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
57247             if(a[i].isFormField){
57248                 r.push(a[i]);
57249             }
57250         }
57251         if(r.length > 0){
57252             Roo.form.Form.superclass.add.apply(this, r);
57253         }
57254         return this;
57255     },
57256     
57257
57258     
57259     
57260     
57261      /**
57262      * Find any element that has been added to a form, using it's ID or name
57263      * This can include framesets, columns etc. along with regular fields..
57264      * @param {String} id - id or name to find.
57265      
57266      * @return {Element} e - or false if nothing found.
57267      */
57268     findbyId : function(id)
57269     {
57270         var ret = false;
57271         if (!id) {
57272             return ret;
57273         }
57274         Roo.each(this.allItems, function(f){
57275             if (f.id == id || f.name == id ){
57276                 ret = f;
57277                 return false;
57278             }
57279         });
57280         return ret;
57281     },
57282
57283     
57284     
57285     /**
57286      * Render this form into the passed container. This should only be called once!
57287      * @param {String/HTMLElement/Element} container The element this component should be rendered into
57288      * @return {Form} this
57289      */
57290     render : function(ct)
57291     {
57292         
57293         
57294         
57295         ct = Roo.get(ct);
57296         var o = this.autoCreate || {
57297             tag: 'form',
57298             method : this.method || 'POST',
57299             id : this.id || Roo.id()
57300         };
57301         this.initEl(ct.createChild(o));
57302
57303         this.root.render(this.el);
57304         
57305        
57306              
57307         this.items.each(function(f){
57308             f.render('x-form-el-'+f.id);
57309         });
57310
57311         if(this.buttons.length > 0){
57312             // tables are required to maintain order and for correct IE layout
57313             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
57314                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
57315                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
57316             }}, null, true);
57317             var tr = tb.getElementsByTagName('tr')[0];
57318             for(var i = 0, len = this.buttons.length; i < len; i++) {
57319                 var b = this.buttons[i];
57320                 var td = document.createElement('td');
57321                 td.className = 'x-form-btn-td';
57322                 b.render(tr.appendChild(td));
57323             }
57324         }
57325         if(this.monitorValid){ // initialize after render
57326             this.startMonitoring();
57327         }
57328         this.fireEvent('rendered', this);
57329         return this;
57330     },
57331
57332     /**
57333      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
57334      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
57335      * object or a valid Roo.DomHelper element config
57336      * @param {Function} handler The function called when the button is clicked
57337      * @param {Object} scope (optional) The scope of the handler function
57338      * @return {Roo.Button}
57339      */
57340     addButton : function(config, handler, scope){
57341         var bc = {
57342             handler: handler,
57343             scope: scope,
57344             minWidth: this.minButtonWidth,
57345             hideParent:true
57346         };
57347         if(typeof config == "string"){
57348             bc.text = config;
57349         }else{
57350             Roo.apply(bc, config);
57351         }
57352         var btn = new Roo.Button(null, bc);
57353         this.buttons.push(btn);
57354         return btn;
57355     },
57356
57357      /**
57358      * Adds a series of form elements (using the xtype property as the factory method.
57359      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
57360      * @param {Object} config 
57361      */
57362     
57363     addxtype : function()
57364     {
57365         var ar = Array.prototype.slice.call(arguments, 0);
57366         var ret = false;
57367         for(var i = 0; i < ar.length; i++) {
57368             if (!ar[i]) {
57369                 continue; // skip -- if this happends something invalid got sent, we 
57370                 // should ignore it, as basically that interface element will not show up
57371                 // and that should be pretty obvious!!
57372             }
57373             
57374             if (Roo.form[ar[i].xtype]) {
57375                 ar[i].form = this;
57376                 var fe = Roo.factory(ar[i], Roo.form);
57377                 if (!ret) {
57378                     ret = fe;
57379                 }
57380                 fe.form = this;
57381                 if (fe.store) {
57382                     fe.store.form = this;
57383                 }
57384                 if (fe.isLayout) {  
57385                          
57386                     this.start(fe);
57387                     this.allItems.push(fe);
57388                     if (fe.items && fe.addxtype) {
57389                         fe.addxtype.apply(fe, fe.items);
57390                         delete fe.items;
57391                     }
57392                      this.end();
57393                     continue;
57394                 }
57395                 
57396                 
57397                  
57398                 this.add(fe);
57399               //  console.log('adding ' + ar[i].xtype);
57400             }
57401             if (ar[i].xtype == 'Button') {  
57402                 //console.log('adding button');
57403                 //console.log(ar[i]);
57404                 this.addButton(ar[i]);
57405                 this.allItems.push(fe);
57406                 continue;
57407             }
57408             
57409             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
57410                 alert('end is not supported on xtype any more, use items');
57411             //    this.end();
57412             //    //console.log('adding end');
57413             }
57414             
57415         }
57416         return ret;
57417     },
57418     
57419     /**
57420      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
57421      * option "monitorValid"
57422      */
57423     startMonitoring : function(){
57424         if(!this.bound){
57425             this.bound = true;
57426             Roo.TaskMgr.start({
57427                 run : this.bindHandler,
57428                 interval : this.monitorPoll || 200,
57429                 scope: this
57430             });
57431         }
57432     },
57433
57434     /**
57435      * Stops monitoring of the valid state of this form
57436      */
57437     stopMonitoring : function(){
57438         this.bound = false;
57439     },
57440
57441     // private
57442     bindHandler : function(){
57443         if(!this.bound){
57444             return false; // stops binding
57445         }
57446         var valid = true;
57447         this.items.each(function(f){
57448             if(!f.isValid(true)){
57449                 valid = false;
57450                 return false;
57451             }
57452         });
57453         for(var i = 0, len = this.buttons.length; i < len; i++){
57454             var btn = this.buttons[i];
57455             if(btn.formBind === true && btn.disabled === valid){
57456                 btn.setDisabled(!valid);
57457             }
57458         }
57459         this.fireEvent('clientvalidation', this, valid);
57460     }
57461     
57462     
57463     
57464     
57465     
57466     
57467     
57468     
57469 });
57470
57471
57472 // back compat
57473 Roo.Form = Roo.form.Form;
57474 /*
57475  * Based on:
57476  * Ext JS Library 1.1.1
57477  * Copyright(c) 2006-2007, Ext JS, LLC.
57478  *
57479  * Originally Released Under LGPL - original licence link has changed is not relivant.
57480  *
57481  * Fork - LGPL
57482  * <script type="text/javascript">
57483  */
57484
57485 // as we use this in bootstrap.
57486 Roo.namespace('Roo.form');
57487  /**
57488  * @class Roo.form.Action
57489  * Internal Class used to handle form actions
57490  * @constructor
57491  * @param {Roo.form.BasicForm} el The form element or its id
57492  * @param {Object} config Configuration options
57493  */
57494
57495  
57496  
57497 // define the action interface
57498 Roo.form.Action = function(form, options){
57499     this.form = form;
57500     this.options = options || {};
57501 };
57502 /**
57503  * Client Validation Failed
57504  * @const 
57505  */
57506 Roo.form.Action.CLIENT_INVALID = 'client';
57507 /**
57508  * Server Validation Failed
57509  * @const 
57510  */
57511 Roo.form.Action.SERVER_INVALID = 'server';
57512  /**
57513  * Connect to Server Failed
57514  * @const 
57515  */
57516 Roo.form.Action.CONNECT_FAILURE = 'connect';
57517 /**
57518  * Reading Data from Server Failed
57519  * @const 
57520  */
57521 Roo.form.Action.LOAD_FAILURE = 'load';
57522
57523 Roo.form.Action.prototype = {
57524     type : 'default',
57525     failureType : undefined,
57526     response : undefined,
57527     result : undefined,
57528
57529     // interface method
57530     run : function(options){
57531
57532     },
57533
57534     // interface method
57535     success : function(response){
57536
57537     },
57538
57539     // interface method
57540     handleResponse : function(response){
57541
57542     },
57543
57544     // default connection failure
57545     failure : function(response){
57546         
57547         this.response = response;
57548         this.failureType = Roo.form.Action.CONNECT_FAILURE;
57549         this.form.afterAction(this, false);
57550     },
57551
57552     processResponse : function(response){
57553         this.response = response;
57554         if(!response.responseText){
57555             return true;
57556         }
57557         this.result = this.handleResponse(response);
57558         return this.result;
57559     },
57560
57561     // utility functions used internally
57562     getUrl : function(appendParams){
57563         var url = this.options.url || this.form.url || this.form.el.dom.action;
57564         if(appendParams){
57565             var p = this.getParams();
57566             if(p){
57567                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
57568             }
57569         }
57570         return url;
57571     },
57572
57573     getMethod : function(){
57574         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
57575     },
57576
57577     getParams : function(){
57578         var bp = this.form.baseParams;
57579         var p = this.options.params;
57580         if(p){
57581             if(typeof p == "object"){
57582                 p = Roo.urlEncode(Roo.applyIf(p, bp));
57583             }else if(typeof p == 'string' && bp){
57584                 p += '&' + Roo.urlEncode(bp);
57585             }
57586         }else if(bp){
57587             p = Roo.urlEncode(bp);
57588         }
57589         return p;
57590     },
57591
57592     createCallback : function(){
57593         return {
57594             success: this.success,
57595             failure: this.failure,
57596             scope: this,
57597             timeout: (this.form.timeout*1000),
57598             upload: this.form.fileUpload ? this.success : undefined
57599         };
57600     }
57601 };
57602
57603 Roo.form.Action.Submit = function(form, options){
57604     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
57605 };
57606
57607 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
57608     type : 'submit',
57609
57610     haveProgress : false,
57611     uploadComplete : false,
57612     
57613     // uploadProgress indicator.
57614     uploadProgress : function()
57615     {
57616         if (!this.form.progressUrl) {
57617             return;
57618         }
57619         
57620         if (!this.haveProgress) {
57621             Roo.MessageBox.progress("Uploading", "Uploading");
57622         }
57623         if (this.uploadComplete) {
57624            Roo.MessageBox.hide();
57625            return;
57626         }
57627         
57628         this.haveProgress = true;
57629    
57630         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
57631         
57632         var c = new Roo.data.Connection();
57633         c.request({
57634             url : this.form.progressUrl,
57635             params: {
57636                 id : uid
57637             },
57638             method: 'GET',
57639             success : function(req){
57640                //console.log(data);
57641                 var rdata = false;
57642                 var edata;
57643                 try  {
57644                    rdata = Roo.decode(req.responseText)
57645                 } catch (e) {
57646                     Roo.log("Invalid data from server..");
57647                     Roo.log(edata);
57648                     return;
57649                 }
57650                 if (!rdata || !rdata.success) {
57651                     Roo.log(rdata);
57652                     Roo.MessageBox.alert(Roo.encode(rdata));
57653                     return;
57654                 }
57655                 var data = rdata.data;
57656                 
57657                 if (this.uploadComplete) {
57658                    Roo.MessageBox.hide();
57659                    return;
57660                 }
57661                    
57662                 if (data){
57663                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
57664                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
57665                     );
57666                 }
57667                 this.uploadProgress.defer(2000,this);
57668             },
57669        
57670             failure: function(data) {
57671                 Roo.log('progress url failed ');
57672                 Roo.log(data);
57673             },
57674             scope : this
57675         });
57676            
57677     },
57678     
57679     
57680     run : function()
57681     {
57682         // run get Values on the form, so it syncs any secondary forms.
57683         this.form.getValues();
57684         
57685         var o = this.options;
57686         var method = this.getMethod();
57687         var isPost = method == 'POST';
57688         if(o.clientValidation === false || this.form.isValid()){
57689             
57690             if (this.form.progressUrl) {
57691                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
57692                     (new Date() * 1) + '' + Math.random());
57693                     
57694             } 
57695             
57696             
57697             Roo.Ajax.request(Roo.apply(this.createCallback(), {
57698                 form:this.form.el.dom,
57699                 url:this.getUrl(!isPost),
57700                 method: method,
57701                 params:isPost ? this.getParams() : null,
57702                 isUpload: this.form.fileUpload,
57703                 formData : this.form.formData
57704             }));
57705             
57706             this.uploadProgress();
57707
57708         }else if (o.clientValidation !== false){ // client validation failed
57709             this.failureType = Roo.form.Action.CLIENT_INVALID;
57710             this.form.afterAction(this, false);
57711         }
57712     },
57713
57714     success : function(response)
57715     {
57716         this.uploadComplete= true;
57717         if (this.haveProgress) {
57718             Roo.MessageBox.hide();
57719         }
57720         
57721         
57722         var result = this.processResponse(response);
57723         if(result === true || result.success){
57724             this.form.afterAction(this, true);
57725             return;
57726         }
57727         if(result.errors){
57728             this.form.markInvalid(result.errors);
57729             this.failureType = Roo.form.Action.SERVER_INVALID;
57730         }
57731         this.form.afterAction(this, false);
57732     },
57733     failure : function(response)
57734     {
57735         this.uploadComplete= true;
57736         if (this.haveProgress) {
57737             Roo.MessageBox.hide();
57738         }
57739         
57740         this.response = response;
57741         this.failureType = Roo.form.Action.CONNECT_FAILURE;
57742         this.form.afterAction(this, false);
57743     },
57744     
57745     handleResponse : function(response){
57746         if(this.form.errorReader){
57747             var rs = this.form.errorReader.read(response);
57748             var errors = [];
57749             if(rs.records){
57750                 for(var i = 0, len = rs.records.length; i < len; i++) {
57751                     var r = rs.records[i];
57752                     errors[i] = r.data;
57753                 }
57754             }
57755             if(errors.length < 1){
57756                 errors = null;
57757             }
57758             return {
57759                 success : rs.success,
57760                 errors : errors
57761             };
57762         }
57763         var ret = false;
57764         try {
57765             var rt = response.responseText;
57766             if (rt.match(/^\<!--\[CDATA\[/)) {
57767                 rt = rt.replace(/^\<!--\[CDATA\[/,'');
57768                 rt = rt.replace(/\]\]--\>$/,'');
57769             }
57770             
57771             ret = Roo.decode(rt);
57772         } catch (e) {
57773             ret = {
57774                 success: false,
57775                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
57776                 errors : []
57777             };
57778         }
57779         return ret;
57780         
57781     }
57782 });
57783
57784
57785 Roo.form.Action.Load = function(form, options){
57786     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
57787     this.reader = this.form.reader;
57788 };
57789
57790 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
57791     type : 'load',
57792
57793     run : function(){
57794         
57795         Roo.Ajax.request(Roo.apply(
57796                 this.createCallback(), {
57797                     method:this.getMethod(),
57798                     url:this.getUrl(false),
57799                     params:this.getParams()
57800         }));
57801     },
57802
57803     success : function(response){
57804         
57805         var result = this.processResponse(response);
57806         if(result === true || !result.success || !result.data){
57807             this.failureType = Roo.form.Action.LOAD_FAILURE;
57808             this.form.afterAction(this, false);
57809             return;
57810         }
57811         this.form.clearInvalid();
57812         this.form.setValues(result.data);
57813         this.form.afterAction(this, true);
57814     },
57815
57816     handleResponse : function(response){
57817         if(this.form.reader){
57818             var rs = this.form.reader.read(response);
57819             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
57820             return {
57821                 success : rs.success,
57822                 data : data
57823             };
57824         }
57825         return Roo.decode(response.responseText);
57826     }
57827 });
57828
57829 Roo.form.Action.ACTION_TYPES = {
57830     'load' : Roo.form.Action.Load,
57831     'submit' : Roo.form.Action.Submit
57832 };/*
57833  * Based on:
57834  * Ext JS Library 1.1.1
57835  * Copyright(c) 2006-2007, Ext JS, LLC.
57836  *
57837  * Originally Released Under LGPL - original licence link has changed is not relivant.
57838  *
57839  * Fork - LGPL
57840  * <script type="text/javascript">
57841  */
57842  
57843 /**
57844  * @class Roo.form.Layout
57845  * @extends Roo.Component
57846  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
57847  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
57848  * @constructor
57849  * @param {Object} config Configuration options
57850  */
57851 Roo.form.Layout = function(config){
57852     var xitems = [];
57853     if (config.items) {
57854         xitems = config.items;
57855         delete config.items;
57856     }
57857     Roo.form.Layout.superclass.constructor.call(this, config);
57858     this.stack = [];
57859     Roo.each(xitems, this.addxtype, this);
57860      
57861 };
57862
57863 Roo.extend(Roo.form.Layout, Roo.Component, {
57864     /**
57865      * @cfg {String/Object} autoCreate
57866      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
57867      */
57868     /**
57869      * @cfg {String/Object/Function} style
57870      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
57871      * a function which returns such a specification.
57872      */
57873     /**
57874      * @cfg {String} labelAlign (left|top|right)
57875      * Valid values are "left," "top" and "right" (defaults to "left")
57876      */
57877     /**
57878      * @cfg {Number} labelWidth
57879      * Fixed width in pixels of all field labels (defaults to undefined)
57880      */
57881     /**
57882      * @cfg {Boolean} clear
57883      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
57884      */
57885     clear : true,
57886     /**
57887      * @cfg {String} labelSeparator
57888      * The separator to use after field labels (defaults to ':')
57889      */
57890     labelSeparator : ':',
57891     /**
57892      * @cfg {Boolean} hideLabels
57893      * True to suppress the display of field labels in this layout (defaults to false)
57894      */
57895     hideLabels : false,
57896
57897     // private
57898     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
57899     
57900     isLayout : true,
57901     
57902     // private
57903     onRender : function(ct, position){
57904         if(this.el){ // from markup
57905             this.el = Roo.get(this.el);
57906         }else {  // generate
57907             var cfg = this.getAutoCreate();
57908             this.el = ct.createChild(cfg, position);
57909         }
57910         if(this.style){
57911             this.el.applyStyles(this.style);
57912         }
57913         if(this.labelAlign){
57914             this.el.addClass('x-form-label-'+this.labelAlign);
57915         }
57916         if(this.hideLabels){
57917             this.labelStyle = "display:none";
57918             this.elementStyle = "padding-left:0;";
57919         }else{
57920             if(typeof this.labelWidth == 'number'){
57921                 this.labelStyle = "width:"+this.labelWidth+"px;";
57922                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
57923             }
57924             if(this.labelAlign == 'top'){
57925                 this.labelStyle = "width:auto;";
57926                 this.elementStyle = "padding-left:0;";
57927             }
57928         }
57929         var stack = this.stack;
57930         var slen = stack.length;
57931         if(slen > 0){
57932             if(!this.fieldTpl){
57933                 var t = new Roo.Template(
57934                     '<div class="x-form-item {5}">',
57935                         '<label for="{0}" style="{2}">{1}{4}</label>',
57936                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
57937                         '</div>',
57938                     '</div><div class="x-form-clear-left"></div>'
57939                 );
57940                 t.disableFormats = true;
57941                 t.compile();
57942                 Roo.form.Layout.prototype.fieldTpl = t;
57943             }
57944             for(var i = 0; i < slen; i++) {
57945                 if(stack[i].isFormField){
57946                     this.renderField(stack[i]);
57947                 }else{
57948                     this.renderComponent(stack[i]);
57949                 }
57950             }
57951         }
57952         if(this.clear){
57953             this.el.createChild({cls:'x-form-clear'});
57954         }
57955     },
57956
57957     // private
57958     renderField : function(f){
57959         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
57960                f.id, //0
57961                f.fieldLabel, //1
57962                f.labelStyle||this.labelStyle||'', //2
57963                this.elementStyle||'', //3
57964                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
57965                f.itemCls||this.itemCls||''  //5
57966        ], true).getPrevSibling());
57967     },
57968
57969     // private
57970     renderComponent : function(c){
57971         c.render(c.isLayout ? this.el : this.el.createChild());    
57972     },
57973     /**
57974      * Adds a object form elements (using the xtype property as the factory method.)
57975      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
57976      * @param {Object} config 
57977      */
57978     addxtype : function(o)
57979     {
57980         // create the lement.
57981         o.form = this.form;
57982         var fe = Roo.factory(o, Roo.form);
57983         this.form.allItems.push(fe);
57984         this.stack.push(fe);
57985         
57986         if (fe.isFormField) {
57987             this.form.items.add(fe);
57988         }
57989          
57990         return fe;
57991     }
57992 });
57993
57994
57995 /**
57996  * @class Roo.form.Column
57997  * @extends Roo.form.Layout
57998  * @children Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
57999  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
58000  * @constructor
58001  * @param {Object} config Configuration options
58002  */
58003 Roo.form.Column = function(config){
58004     Roo.form.Column.superclass.constructor.call(this, config);
58005 };
58006
58007 Roo.extend(Roo.form.Column, Roo.form.Layout, {
58008     /**
58009      * @cfg {Number/String} width
58010      * The fixed width of the column in pixels or CSS value (defaults to "auto")
58011      */
58012     /**
58013      * @cfg {String/Object} autoCreate
58014      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
58015      */
58016
58017     // private
58018     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
58019
58020     // private
58021     onRender : function(ct, position){
58022         Roo.form.Column.superclass.onRender.call(this, ct, position);
58023         if(this.width){
58024             this.el.setWidth(this.width);
58025         }
58026     }
58027 });
58028
58029 /**
58030  * @class Roo.form.Row
58031  * @extends Roo.form.Layout
58032  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
58033  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
58034  * @constructor
58035  * @param {Object} config Configuration options
58036  */
58037
58038  
58039 Roo.form.Row = function(config){
58040     Roo.form.Row.superclass.constructor.call(this, config);
58041 };
58042  
58043 Roo.extend(Roo.form.Row, Roo.form.Layout, {
58044       /**
58045      * @cfg {Number/String} width
58046      * The fixed width of the column in pixels or CSS value (defaults to "auto")
58047      */
58048     /**
58049      * @cfg {Number/String} height
58050      * The fixed height of the column in pixels or CSS value (defaults to "auto")
58051      */
58052     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
58053     
58054     padWidth : 20,
58055     // private
58056     onRender : function(ct, position){
58057         //console.log('row render');
58058         if(!this.rowTpl){
58059             var t = new Roo.Template(
58060                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
58061                     '<label for="{0}" style="{2}">{1}{4}</label>',
58062                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
58063                     '</div>',
58064                 '</div>'
58065             );
58066             t.disableFormats = true;
58067             t.compile();
58068             Roo.form.Layout.prototype.rowTpl = t;
58069         }
58070         this.fieldTpl = this.rowTpl;
58071         
58072         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
58073         var labelWidth = 100;
58074         
58075         if ((this.labelAlign != 'top')) {
58076             if (typeof this.labelWidth == 'number') {
58077                 labelWidth = this.labelWidth
58078             }
58079             this.padWidth =  20 + labelWidth;
58080             
58081         }
58082         
58083         Roo.form.Column.superclass.onRender.call(this, ct, position);
58084         if(this.width){
58085             this.el.setWidth(this.width);
58086         }
58087         if(this.height){
58088             this.el.setHeight(this.height);
58089         }
58090     },
58091     
58092     // private
58093     renderField : function(f){
58094         f.fieldEl = this.fieldTpl.append(this.el, [
58095                f.id, f.fieldLabel,
58096                f.labelStyle||this.labelStyle||'',
58097                this.elementStyle||'',
58098                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
58099                f.itemCls||this.itemCls||'',
58100                f.width ? f.width + this.padWidth : 160 + this.padWidth
58101        ],true);
58102     }
58103 });
58104  
58105
58106 /**
58107  * @class Roo.form.FieldSet
58108  * @extends Roo.form.Layout
58109  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
58110  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
58111  * @constructor
58112  * @param {Object} config Configuration options
58113  */
58114 Roo.form.FieldSet = function(config){
58115     Roo.form.FieldSet.superclass.constructor.call(this, config);
58116 };
58117
58118 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
58119     /**
58120      * @cfg {String} legend
58121      * The text to display as the legend for the FieldSet (defaults to '')
58122      */
58123     /**
58124      * @cfg {String/Object} autoCreate
58125      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
58126      */
58127
58128     // private
58129     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
58130
58131     // private
58132     onRender : function(ct, position){
58133         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
58134         if(this.legend){
58135             this.setLegend(this.legend);
58136         }
58137     },
58138
58139     // private
58140     setLegend : function(text){
58141         if(this.rendered){
58142             this.el.child('legend').update(text);
58143         }
58144     }
58145 });/*
58146  * Based on:
58147  * Ext JS Library 1.1.1
58148  * Copyright(c) 2006-2007, Ext JS, LLC.
58149  *
58150  * Originally Released Under LGPL - original licence link has changed is not relivant.
58151  *
58152  * Fork - LGPL
58153  * <script type="text/javascript">
58154  */
58155 /**
58156  * @class Roo.form.VTypes
58157  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
58158  * @static
58159  */
58160 Roo.form.VTypes = function(){
58161     // closure these in so they are only created once.
58162     var alpha = /^[a-zA-Z_]+$/;
58163     var alphanum = /^[a-zA-Z0-9_]+$/;
58164     var email = /^([\w'-]+)(\.[\w'-]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
58165     var url = /^(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
58166     var urlWeb = /^((https?):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
58167
58168     // All these messages and functions are configurable
58169     return {
58170         /**
58171          * The function used to validate email addresses
58172          * @param {String} value The email address
58173          */
58174         email : function(v){
58175             return email.test(v);
58176         },
58177         /**
58178          * The error text to display when the email validation function returns false
58179          * @type String
58180          */
58181         emailText : 'This field should be an e-mail address in the format "user@domain.com"',
58182         /**
58183          * The keystroke filter mask to be applied on email input
58184          * @type RegExp
58185          */
58186         emailMask : /[a-z0-9_\.\-@]/i,
58187
58188         /**
58189          * The function used to validate URLs
58190          * @param {String} value The URL
58191          */
58192         url : function(v){
58193             return url.test(v);
58194         },
58195         /**
58196          * The funciton used to validate URLs (only allow schemes 'https' and 'http')
58197          * @param {String} v The URL
58198          */
58199         urlWeb : function(v) {
58200             return urlWeb.test(v);
58201         },
58202         /**
58203          * The error text to display when the url validation function returns false
58204          * @type String
58205          */
58206         urlText : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
58207         
58208         /**
58209          * The function used to validate alpha values
58210          * @param {String} value The value
58211          */
58212         alpha : function(v){
58213             return alpha.test(v);
58214         },
58215         /**
58216          * The error text to display when the alpha validation function returns false
58217          * @type String
58218          */
58219         alphaText : 'This field should only contain letters and _',
58220         /**
58221          * The keystroke filter mask to be applied on alpha input
58222          * @type RegExp
58223          */
58224         alphaMask : /[a-z_]/i,
58225
58226         /**
58227          * The function used to validate alphanumeric values
58228          * @param {String} value The value
58229          */
58230         alphanum : function(v){
58231             return alphanum.test(v);
58232         },
58233         /**
58234          * The error text to display when the alphanumeric validation function returns false
58235          * @type String
58236          */
58237         alphanumText : 'This field should only contain letters, numbers and _',
58238         /**
58239          * The keystroke filter mask to be applied on alphanumeric input
58240          * @type RegExp
58241          */
58242         alphanumMask : /[a-z0-9_]/i
58243     };
58244 }();//<script type="text/javascript">
58245
58246 /**
58247  * @class Roo.form.FCKeditor
58248  * @extends Roo.form.TextArea
58249  * Wrapper around the FCKEditor http://www.fckeditor.net
58250  * @constructor
58251  * Creates a new FCKeditor
58252  * @param {Object} config Configuration options
58253  */
58254 Roo.form.FCKeditor = function(config){
58255     Roo.form.FCKeditor.superclass.constructor.call(this, config);
58256     this.addEvents({
58257          /**
58258          * @event editorinit
58259          * Fired when the editor is initialized - you can add extra handlers here..
58260          * @param {FCKeditor} this
58261          * @param {Object} the FCK object.
58262          */
58263         editorinit : true
58264     });
58265     
58266     
58267 };
58268 Roo.form.FCKeditor.editors = { };
58269 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
58270 {
58271     //defaultAutoCreate : {
58272     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
58273     //},
58274     // private
58275     /**
58276      * @cfg {Object} fck options - see fck manual for details.
58277      */
58278     fckconfig : false,
58279     
58280     /**
58281      * @cfg {Object} fck toolbar set (Basic or Default)
58282      */
58283     toolbarSet : 'Basic',
58284     /**
58285      * @cfg {Object} fck BasePath
58286      */ 
58287     basePath : '/fckeditor/',
58288     
58289     
58290     frame : false,
58291     
58292     value : '',
58293     
58294    
58295     onRender : function(ct, position)
58296     {
58297         if(!this.el){
58298             this.defaultAutoCreate = {
58299                 tag: "textarea",
58300                 style:"width:300px;height:60px;",
58301                 autocomplete: "new-password"
58302             };
58303         }
58304         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
58305         /*
58306         if(this.grow){
58307             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
58308             if(this.preventScrollbars){
58309                 this.el.setStyle("overflow", "hidden");
58310             }
58311             this.el.setHeight(this.growMin);
58312         }
58313         */
58314         //console.log('onrender' + this.getId() );
58315         Roo.form.FCKeditor.editors[this.getId()] = this;
58316          
58317
58318         this.replaceTextarea() ;
58319         
58320     },
58321     
58322     getEditor : function() {
58323         return this.fckEditor;
58324     },
58325     /**
58326      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
58327      * @param {Mixed} value The value to set
58328      */
58329     
58330     
58331     setValue : function(value)
58332     {
58333         //console.log('setValue: ' + value);
58334         
58335         if(typeof(value) == 'undefined') { // not sure why this is happending...
58336             return;
58337         }
58338         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
58339         
58340         //if(!this.el || !this.getEditor()) {
58341         //    this.value = value;
58342             //this.setValue.defer(100,this,[value]);    
58343         //    return;
58344         //} 
58345         
58346         if(!this.getEditor()) {
58347             return;
58348         }
58349         
58350         this.getEditor().SetData(value);
58351         
58352         //
58353
58354     },
58355
58356     /**
58357      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
58358      * @return {Mixed} value The field value
58359      */
58360     getValue : function()
58361     {
58362         
58363         if (this.frame && this.frame.dom.style.display == 'none') {
58364             return Roo.form.FCKeditor.superclass.getValue.call(this);
58365         }
58366         
58367         if(!this.el || !this.getEditor()) {
58368            
58369            // this.getValue.defer(100,this); 
58370             return this.value;
58371         }
58372        
58373         
58374         var value=this.getEditor().GetData();
58375         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
58376         return Roo.form.FCKeditor.superclass.getValue.call(this);
58377         
58378
58379     },
58380
58381     /**
58382      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
58383      * @return {Mixed} value The field value
58384      */
58385     getRawValue : function()
58386     {
58387         if (this.frame && this.frame.dom.style.display == 'none') {
58388             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
58389         }
58390         
58391         if(!this.el || !this.getEditor()) {
58392             //this.getRawValue.defer(100,this); 
58393             return this.value;
58394             return;
58395         }
58396         
58397         
58398         
58399         var value=this.getEditor().GetData();
58400         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
58401         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
58402          
58403     },
58404     
58405     setSize : function(w,h) {
58406         
58407         
58408         
58409         //if (this.frame && this.frame.dom.style.display == 'none') {
58410         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
58411         //    return;
58412         //}
58413         //if(!this.el || !this.getEditor()) {
58414         //    this.setSize.defer(100,this, [w,h]); 
58415         //    return;
58416         //}
58417         
58418         
58419         
58420         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
58421         
58422         this.frame.dom.setAttribute('width', w);
58423         this.frame.dom.setAttribute('height', h);
58424         this.frame.setSize(w,h);
58425         
58426     },
58427     
58428     toggleSourceEdit : function(value) {
58429         
58430       
58431          
58432         this.el.dom.style.display = value ? '' : 'none';
58433         this.frame.dom.style.display = value ?  'none' : '';
58434         
58435     },
58436     
58437     
58438     focus: function(tag)
58439     {
58440         if (this.frame.dom.style.display == 'none') {
58441             return Roo.form.FCKeditor.superclass.focus.call(this);
58442         }
58443         if(!this.el || !this.getEditor()) {
58444             this.focus.defer(100,this, [tag]); 
58445             return;
58446         }
58447         
58448         
58449         
58450         
58451         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
58452         this.getEditor().Focus();
58453         if (tgs.length) {
58454             if (!this.getEditor().Selection.GetSelection()) {
58455                 this.focus.defer(100,this, [tag]); 
58456                 return;
58457             }
58458             
58459             
58460             var r = this.getEditor().EditorDocument.createRange();
58461             r.setStart(tgs[0],0);
58462             r.setEnd(tgs[0],0);
58463             this.getEditor().Selection.GetSelection().removeAllRanges();
58464             this.getEditor().Selection.GetSelection().addRange(r);
58465             this.getEditor().Focus();
58466         }
58467         
58468     },
58469     
58470     
58471     
58472     replaceTextarea : function()
58473     {
58474         if ( document.getElementById( this.getId() + '___Frame' ) ) {
58475             return ;
58476         }
58477         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
58478         //{
58479             // We must check the elements firstly using the Id and then the name.
58480         var oTextarea = document.getElementById( this.getId() );
58481         
58482         var colElementsByName = document.getElementsByName( this.getId() ) ;
58483          
58484         oTextarea.style.display = 'none' ;
58485
58486         if ( oTextarea.tabIndex ) {            
58487             this.TabIndex = oTextarea.tabIndex ;
58488         }
58489         
58490         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
58491         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
58492         this.frame = Roo.get(this.getId() + '___Frame')
58493     },
58494     
58495     _getConfigHtml : function()
58496     {
58497         var sConfig = '' ;
58498
58499         for ( var o in this.fckconfig ) {
58500             sConfig += sConfig.length > 0  ? '&amp;' : '';
58501             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
58502         }
58503
58504         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
58505     },
58506     
58507     
58508     _getIFrameHtml : function()
58509     {
58510         var sFile = 'fckeditor.html' ;
58511         /* no idea what this is about..
58512         try
58513         {
58514             if ( (/fcksource=true/i).test( window.top.location.search ) )
58515                 sFile = 'fckeditor.original.html' ;
58516         }
58517         catch (e) { 
58518         */
58519
58520         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
58521         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
58522         
58523         
58524         var html = '<iframe id="' + this.getId() +
58525             '___Frame" src="' + sLink +
58526             '" width="' + this.width +
58527             '" height="' + this.height + '"' +
58528             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
58529             ' frameborder="0" scrolling="no"></iframe>' ;
58530
58531         return html ;
58532     },
58533     
58534     _insertHtmlBefore : function( html, element )
58535     {
58536         if ( element.insertAdjacentHTML )       {
58537             // IE
58538             element.insertAdjacentHTML( 'beforeBegin', html ) ;
58539         } else { // Gecko
58540             var oRange = document.createRange() ;
58541             oRange.setStartBefore( element ) ;
58542             var oFragment = oRange.createContextualFragment( html );
58543             element.parentNode.insertBefore( oFragment, element ) ;
58544         }
58545     }
58546     
58547     
58548   
58549     
58550     
58551     
58552     
58553
58554 });
58555
58556 //Roo.reg('fckeditor', Roo.form.FCKeditor);
58557
58558 function FCKeditor_OnComplete(editorInstance){
58559     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
58560     f.fckEditor = editorInstance;
58561     //console.log("loaded");
58562     f.fireEvent('editorinit', f, editorInstance);
58563
58564   
58565
58566  
58567
58568
58569
58570
58571
58572
58573
58574
58575
58576
58577
58578
58579
58580
58581
58582 //<script type="text/javascript">
58583 /**
58584  * @class Roo.form.GridField
58585  * @extends Roo.form.Field
58586  * Embed a grid (or editable grid into a form)
58587  * STATUS ALPHA
58588  * 
58589  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
58590  * it needs 
58591  * xgrid.store = Roo.data.Store
58592  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
58593  * xgrid.store.reader = Roo.data.JsonReader 
58594  * 
58595  * 
58596  * @constructor
58597  * Creates a new GridField
58598  * @param {Object} config Configuration options
58599  */
58600 Roo.form.GridField = function(config){
58601     Roo.form.GridField.superclass.constructor.call(this, config);
58602      
58603 };
58604
58605 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
58606     /**
58607      * @cfg {Number} width  - used to restrict width of grid..
58608      */
58609     width : 100,
58610     /**
58611      * @cfg {Number} height - used to restrict height of grid..
58612      */
58613     height : 50,
58614      /**
58615      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
58616          * 
58617          *}
58618      */
58619     xgrid : false, 
58620     /**
58621      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
58622      * {tag: "input", type: "checkbox", autocomplete: "off"})
58623      */
58624    // defaultAutoCreate : { tag: 'div' },
58625     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
58626     /**
58627      * @cfg {String} addTitle Text to include for adding a title.
58628      */
58629     addTitle : false,
58630     //
58631     onResize : function(){
58632         Roo.form.Field.superclass.onResize.apply(this, arguments);
58633     },
58634
58635     initEvents : function(){
58636         // Roo.form.Checkbox.superclass.initEvents.call(this);
58637         // has no events...
58638        
58639     },
58640
58641
58642     getResizeEl : function(){
58643         return this.wrap;
58644     },
58645
58646     getPositionEl : function(){
58647         return this.wrap;
58648     },
58649
58650     // private
58651     onRender : function(ct, position){
58652         
58653         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
58654         var style = this.style;
58655         delete this.style;
58656         
58657         Roo.form.GridField.superclass.onRender.call(this, ct, position);
58658         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
58659         this.viewEl = this.wrap.createChild({ tag: 'div' });
58660         if (style) {
58661             this.viewEl.applyStyles(style);
58662         }
58663         if (this.width) {
58664             this.viewEl.setWidth(this.width);
58665         }
58666         if (this.height) {
58667             this.viewEl.setHeight(this.height);
58668         }
58669         //if(this.inputValue !== undefined){
58670         //this.setValue(this.value);
58671         
58672         
58673         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
58674         
58675         
58676         this.grid.render();
58677         this.grid.getDataSource().on('remove', this.refreshValue, this);
58678         this.grid.getDataSource().on('update', this.refreshValue, this);
58679         this.grid.on('afteredit', this.refreshValue, this);
58680  
58681     },
58682      
58683     
58684     /**
58685      * Sets the value of the item. 
58686      * @param {String} either an object  or a string..
58687      */
58688     setValue : function(v){
58689         //this.value = v;
58690         v = v || []; // empty set..
58691         // this does not seem smart - it really only affects memoryproxy grids..
58692         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
58693             var ds = this.grid.getDataSource();
58694             // assumes a json reader..
58695             var data = {}
58696             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
58697             ds.loadData( data);
58698         }
58699         // clear selection so it does not get stale.
58700         if (this.grid.sm) { 
58701             this.grid.sm.clearSelections();
58702         }
58703         
58704         Roo.form.GridField.superclass.setValue.call(this, v);
58705         this.refreshValue();
58706         // should load data in the grid really....
58707     },
58708     
58709     // private
58710     refreshValue: function() {
58711          var val = [];
58712         this.grid.getDataSource().each(function(r) {
58713             val.push(r.data);
58714         });
58715         this.el.dom.value = Roo.encode(val);
58716     }
58717     
58718      
58719     
58720     
58721 });/*
58722  * Based on:
58723  * Ext JS Library 1.1.1
58724  * Copyright(c) 2006-2007, Ext JS, LLC.
58725  *
58726  * Originally Released Under LGPL - original licence link has changed is not relivant.
58727  *
58728  * Fork - LGPL
58729  * <script type="text/javascript">
58730  */
58731 /**
58732  * @class Roo.form.DisplayField
58733  * @extends Roo.form.Field
58734  * A generic Field to display non-editable data.
58735  * @cfg {Boolean} closable (true|false) default false
58736  * @constructor
58737  * Creates a new Display Field item.
58738  * @param {Object} config Configuration options
58739  */
58740 Roo.form.DisplayField = function(config){
58741     Roo.form.DisplayField.superclass.constructor.call(this, config);
58742     
58743     this.addEvents({
58744         /**
58745          * @event close
58746          * Fires after the click the close btn
58747              * @param {Roo.form.DisplayField} this
58748              */
58749         close : true
58750     });
58751 };
58752
58753 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
58754     inputType:      'hidden',
58755     allowBlank:     true,
58756     readOnly:         true,
58757     
58758  
58759     /**
58760      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
58761      */
58762     focusClass : undefined,
58763     /**
58764      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
58765      */
58766     fieldClass: 'x-form-field',
58767     
58768      /**
58769      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
58770      */
58771     valueRenderer: undefined,
58772     
58773     width: 100,
58774     /**
58775      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
58776      * {tag: "input", type: "checkbox", autocomplete: "off"})
58777      */
58778      
58779  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
58780  
58781     closable : false,
58782     
58783     onResize : function(){
58784         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
58785         
58786     },
58787
58788     initEvents : function(){
58789         // Roo.form.Checkbox.superclass.initEvents.call(this);
58790         // has no events...
58791         
58792         if(this.closable){
58793             this.closeEl.on('click', this.onClose, this);
58794         }
58795        
58796     },
58797
58798
58799     getResizeEl : function(){
58800         return this.wrap;
58801     },
58802
58803     getPositionEl : function(){
58804         return this.wrap;
58805     },
58806
58807     // private
58808     onRender : function(ct, position){
58809         
58810         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
58811         //if(this.inputValue !== undefined){
58812         this.wrap = this.el.wrap();
58813         
58814         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
58815         
58816         if(this.closable){
58817             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
58818         }
58819         
58820         if (this.bodyStyle) {
58821             this.viewEl.applyStyles(this.bodyStyle);
58822         }
58823         //this.viewEl.setStyle('padding', '2px');
58824         
58825         this.setValue(this.value);
58826         
58827     },
58828 /*
58829     // private
58830     initValue : Roo.emptyFn,
58831
58832   */
58833
58834         // private
58835     onClick : function(){
58836         
58837     },
58838
58839     /**
58840      * Sets the checked state of the checkbox.
58841      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
58842      */
58843     setValue : function(v){
58844         this.value = v;
58845         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
58846         // this might be called before we have a dom element..
58847         if (!this.viewEl) {
58848             return;
58849         }
58850         this.viewEl.dom.innerHTML = html;
58851         Roo.form.DisplayField.superclass.setValue.call(this, v);
58852
58853     },
58854     
58855     onClose : function(e)
58856     {
58857         e.preventDefault();
58858         
58859         this.fireEvent('close', this);
58860     }
58861 });/*
58862  * 
58863  * Licence- LGPL
58864  * 
58865  */
58866
58867 /**
58868  * @class Roo.form.DayPicker
58869  * @extends Roo.form.Field
58870  * A Day picker show [M] [T] [W] ....
58871  * @constructor
58872  * Creates a new Day Picker
58873  * @param {Object} config Configuration options
58874  */
58875 Roo.form.DayPicker= function(config){
58876     Roo.form.DayPicker.superclass.constructor.call(this, config);
58877      
58878 };
58879
58880 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
58881     /**
58882      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
58883      */
58884     focusClass : undefined,
58885     /**
58886      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
58887      */
58888     fieldClass: "x-form-field",
58889    
58890     /**
58891      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
58892      * {tag: "input", type: "checkbox", autocomplete: "off"})
58893      */
58894     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
58895     
58896    
58897     actionMode : 'viewEl', 
58898     //
58899     // private
58900  
58901     inputType : 'hidden',
58902     
58903      
58904     inputElement: false, // real input element?
58905     basedOn: false, // ????
58906     
58907     isFormField: true, // not sure where this is needed!!!!
58908
58909     onResize : function(){
58910         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
58911         if(!this.boxLabel){
58912             this.el.alignTo(this.wrap, 'c-c');
58913         }
58914     },
58915
58916     initEvents : function(){
58917         Roo.form.Checkbox.superclass.initEvents.call(this);
58918         this.el.on("click", this.onClick,  this);
58919         this.el.on("change", this.onClick,  this);
58920     },
58921
58922
58923     getResizeEl : function(){
58924         return this.wrap;
58925     },
58926
58927     getPositionEl : function(){
58928         return this.wrap;
58929     },
58930
58931     
58932     // private
58933     onRender : function(ct, position){
58934         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
58935        
58936         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
58937         
58938         var r1 = '<table><tr>';
58939         var r2 = '<tr class="x-form-daypick-icons">';
58940         for (var i=0; i < 7; i++) {
58941             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
58942             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
58943         }
58944         
58945         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
58946         viewEl.select('img').on('click', this.onClick, this);
58947         this.viewEl = viewEl;   
58948         
58949         
58950         // this will not work on Chrome!!!
58951         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
58952         this.el.on('propertychange', this.setFromHidden,  this);  //ie
58953         
58954         
58955           
58956
58957     },
58958
58959     // private
58960     initValue : Roo.emptyFn,
58961
58962     /**
58963      * Returns the checked state of the checkbox.
58964      * @return {Boolean} True if checked, else false
58965      */
58966     getValue : function(){
58967         return this.el.dom.value;
58968         
58969     },
58970
58971         // private
58972     onClick : function(e){ 
58973         //this.setChecked(!this.checked);
58974         Roo.get(e.target).toggleClass('x-menu-item-checked');
58975         this.refreshValue();
58976         //if(this.el.dom.checked != this.checked){
58977         //    this.setValue(this.el.dom.checked);
58978        // }
58979     },
58980     
58981     // private
58982     refreshValue : function()
58983     {
58984         var val = '';
58985         this.viewEl.select('img',true).each(function(e,i,n)  {
58986             val += e.is(".x-menu-item-checked") ? String(n) : '';
58987         });
58988         this.setValue(val, true);
58989     },
58990
58991     /**
58992      * Sets the checked state of the checkbox.
58993      * On is always based on a string comparison between inputValue and the param.
58994      * @param {Boolean/String} value - the value to set 
58995      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
58996      */
58997     setValue : function(v,suppressEvent){
58998         if (!this.el.dom) {
58999             return;
59000         }
59001         var old = this.el.dom.value ;
59002         this.el.dom.value = v;
59003         if (suppressEvent) {
59004             return ;
59005         }
59006          
59007         // update display..
59008         this.viewEl.select('img',true).each(function(e,i,n)  {
59009             
59010             var on = e.is(".x-menu-item-checked");
59011             var newv = v.indexOf(String(n)) > -1;
59012             if (on != newv) {
59013                 e.toggleClass('x-menu-item-checked');
59014             }
59015             
59016         });
59017         
59018         
59019         this.fireEvent('change', this, v, old);
59020         
59021         
59022     },
59023    
59024     // handle setting of hidden value by some other method!!?!?
59025     setFromHidden: function()
59026     {
59027         if(!this.el){
59028             return;
59029         }
59030         //console.log("SET FROM HIDDEN");
59031         //alert('setFrom hidden');
59032         this.setValue(this.el.dom.value);
59033     },
59034     
59035     onDestroy : function()
59036     {
59037         if(this.viewEl){
59038             Roo.get(this.viewEl).remove();
59039         }
59040          
59041         Roo.form.DayPicker.superclass.onDestroy.call(this);
59042     }
59043
59044 });/*
59045  * RooJS Library 1.1.1
59046  * Copyright(c) 2008-2011  Alan Knowles
59047  *
59048  * License - LGPL
59049  */
59050  
59051
59052 /**
59053  * @class Roo.form.ComboCheck
59054  * @extends Roo.form.ComboBox
59055  * A combobox for multiple select items.
59056  *
59057  * FIXME - could do with a reset button..
59058  * 
59059  * @constructor
59060  * Create a new ComboCheck
59061  * @param {Object} config Configuration options
59062  */
59063 Roo.form.ComboCheck = function(config){
59064     Roo.form.ComboCheck.superclass.constructor.call(this, config);
59065     // should verify some data...
59066     // like
59067     // hiddenName = required..
59068     // displayField = required
59069     // valudField == required
59070     var req= [ 'hiddenName', 'displayField', 'valueField' ];
59071     var _t = this;
59072     Roo.each(req, function(e) {
59073         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
59074             throw "Roo.form.ComboCheck : missing value for: " + e;
59075         }
59076     });
59077     
59078     
59079 };
59080
59081 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
59082      
59083      
59084     editable : false,
59085      
59086     selectedClass: 'x-menu-item-checked', 
59087     
59088     // private
59089     onRender : function(ct, position){
59090         var _t = this;
59091         
59092         
59093         
59094         if(!this.tpl){
59095             var cls = 'x-combo-list';
59096
59097             
59098             this.tpl =  new Roo.Template({
59099                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
59100                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
59101                    '<span>{' + this.displayField + '}</span>' +
59102                     '</div>' 
59103                 
59104             });
59105         }
59106  
59107         
59108         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
59109         this.view.singleSelect = false;
59110         this.view.multiSelect = true;
59111         this.view.toggleSelect = true;
59112         this.pageTb.add(new Roo.Toolbar.Fill(),{
59113             
59114             text: 'Select All',
59115             handler: function() {
59116                 _t.selectAll();
59117             }
59118         },
59119         {
59120             text: 'Done',
59121             handler: function() {
59122                 _t.collapse();
59123             }
59124         });
59125     },
59126     
59127     cleanLeadingSpace : function(e)
59128     {
59129         // this is disabled, as it retriggers setvalue on blur
59130         return;
59131     },
59132     doForce : function() {
59133         // no idea what this did, but it blanks out our values.
59134         return;
59135     },
59136     onViewOver : function(e, t){
59137         // do nothing...
59138         return;
59139         
59140     },
59141     
59142     onViewClick : function(doFocus,index){
59143         return;
59144         
59145     },
59146     select: function () {
59147         //Roo.log("SELECT CALLED");
59148     },
59149      
59150     selectByValue : function(xv, scrollIntoView){
59151         var ar = this.getValueArray();
59152         var sels = [];
59153         
59154         Roo.each(ar, function(v) {
59155             if(v === undefined || v === null){
59156                 return;
59157             }
59158             var r = this.findRecord(this.valueField, v);
59159             if(r){
59160                 sels.push(this.store.indexOf(r))
59161                 
59162             }
59163         },this);
59164         this.view.select(sels);
59165         return false;
59166     },
59167     
59168     selectAll : function()
59169     {
59170         var sels = [];
59171         this.store.each(function(r,i) {
59172             sels.push(i);
59173         });
59174         this.view.select(sels);
59175         this.collapse();
59176         return false;
59177
59178     },
59179     
59180     onSelect : function(record, index){
59181        // Roo.log("onselect Called");
59182        // this is only called by the clear button now..
59183         this.view.clearSelections();
59184         this.setValue('[]');
59185         if (this.value != this.valueBefore) {
59186             this.fireEvent('change', this, this.value, this.valueBefore);
59187             this.valueBefore = this.value;
59188         }
59189     },
59190     getValueArray : function()
59191     {
59192         var ar = [] ;
59193         
59194         try {
59195             //Roo.log(this.value);
59196             if (typeof(this.value) == 'undefined') {
59197                 return [];
59198             }
59199             var ar = Roo.decode(this.value);
59200             return  ar instanceof Array ? ar : []; //?? valid?
59201             
59202         } catch(e) {
59203             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
59204             return [];
59205         }
59206          
59207     },
59208     expand : function ()
59209     {
59210         
59211         Roo.form.ComboCheck.superclass.expand.call(this);
59212         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
59213         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
59214         
59215
59216     },
59217     
59218     collapse : function(){
59219         Roo.form.ComboCheck.superclass.collapse.call(this);
59220         var sl = this.view.getSelectedIndexes();
59221         var st = this.store;
59222         var nv = [];
59223         var tv = [];
59224         var r;
59225         Roo.each(sl, function(i) {
59226             r = st.getAt(i);
59227             nv.push(r.get(this.valueField));
59228         },this);
59229         this.setValue(Roo.encode(nv));
59230         if (this.value != this.valueBefore) {
59231
59232             this.fireEvent('change', this, this.value, this.valueBefore);
59233             this.valueBefore = this.value;
59234         }
59235         
59236     },
59237     
59238     setValue : function(v){
59239         // Roo.log(v);
59240         this.value = v;
59241         
59242         var vals = this.getValueArray();
59243         var tv = [];
59244         Roo.each(vals, function(k) {
59245             var r = this.findRecord(this.valueField, k);
59246             if(r){
59247                 tv.push(r.data[this.displayField]);
59248             }else if(this.valueNotFoundText !== undefined){
59249                 tv.push( this.valueNotFoundText );
59250             }
59251         },this);
59252        // Roo.log(tv);
59253         
59254         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
59255         this.hiddenField.value = v;
59256         this.value = v;
59257     }
59258     
59259 });/*
59260  * Based on:
59261  * Ext JS Library 1.1.1
59262  * Copyright(c) 2006-2007, Ext JS, LLC.
59263  *
59264  * Originally Released Under LGPL - original licence link has changed is not relivant.
59265  *
59266  * Fork - LGPL
59267  * <script type="text/javascript">
59268  */
59269  
59270 /**
59271  * @class Roo.form.Signature
59272  * @extends Roo.form.Field
59273  * Signature field.  
59274  * @constructor
59275  * 
59276  * @param {Object} config Configuration options
59277  */
59278
59279 Roo.form.Signature = function(config){
59280     Roo.form.Signature.superclass.constructor.call(this, config);
59281     
59282     this.addEvents({// not in used??
59283          /**
59284          * @event confirm
59285          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
59286              * @param {Roo.form.Signature} combo This combo box
59287              */
59288         'confirm' : true,
59289         /**
59290          * @event reset
59291          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
59292              * @param {Roo.form.ComboBox} combo This combo box
59293              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
59294              */
59295         'reset' : true
59296     });
59297 };
59298
59299 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
59300     /**
59301      * @cfg {Object} labels Label to use when rendering a form.
59302      * defaults to 
59303      * labels : { 
59304      *      clear : "Clear",
59305      *      confirm : "Confirm"
59306      *  }
59307      */
59308     labels : { 
59309         clear : "Clear",
59310         confirm : "Confirm"
59311     },
59312     /**
59313      * @cfg {Number} width The signature panel width (defaults to 300)
59314      */
59315     width: 300,
59316     /**
59317      * @cfg {Number} height The signature panel height (defaults to 100)
59318      */
59319     height : 100,
59320     /**
59321      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
59322      */
59323     allowBlank : false,
59324     
59325     //private
59326     // {Object} signPanel The signature SVG panel element (defaults to {})
59327     signPanel : {},
59328     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
59329     isMouseDown : false,
59330     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
59331     isConfirmed : false,
59332     // {String} signatureTmp SVG mapping string (defaults to empty string)
59333     signatureTmp : '',
59334     
59335     
59336     defaultAutoCreate : { // modified by initCompnoent..
59337         tag: "input",
59338         type:"hidden"
59339     },
59340
59341     // private
59342     onRender : function(ct, position){
59343         
59344         Roo.form.Signature.superclass.onRender.call(this, ct, position);
59345         
59346         this.wrap = this.el.wrap({
59347             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
59348         });
59349         
59350         this.createToolbar(this);
59351         this.signPanel = this.wrap.createChild({
59352                 tag: 'div',
59353                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
59354             }, this.el
59355         );
59356             
59357         this.svgID = Roo.id();
59358         this.svgEl = this.signPanel.createChild({
59359               xmlns : 'http://www.w3.org/2000/svg',
59360               tag : 'svg',
59361               id : this.svgID + "-svg",
59362               width: this.width,
59363               height: this.height,
59364               viewBox: '0 0 '+this.width+' '+this.height,
59365               cn : [
59366                 {
59367                     tag: "rect",
59368                     id: this.svgID + "-svg-r",
59369                     width: this.width,
59370                     height: this.height,
59371                     fill: "#ffa"
59372                 },
59373                 {
59374                     tag: "line",
59375                     id: this.svgID + "-svg-l",
59376                     x1: "0", // start
59377                     y1: (this.height*0.8), // start set the line in 80% of height
59378                     x2: this.width, // end
59379                     y2: (this.height*0.8), // end set the line in 80% of height
59380                     'stroke': "#666",
59381                     'stroke-width': "1",
59382                     'stroke-dasharray': "3",
59383                     'shape-rendering': "crispEdges",
59384                     'pointer-events': "none"
59385                 },
59386                 {
59387                     tag: "path",
59388                     id: this.svgID + "-svg-p",
59389                     'stroke': "navy",
59390                     'stroke-width': "3",
59391                     'fill': "none",
59392                     'pointer-events': 'none'
59393                 }
59394               ]
59395         });
59396         this.createSVG();
59397         this.svgBox = this.svgEl.dom.getScreenCTM();
59398     },
59399     createSVG : function(){ 
59400         var svg = this.signPanel;
59401         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
59402         var t = this;
59403
59404         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
59405         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
59406         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
59407         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
59408         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
59409         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
59410         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
59411         
59412     },
59413     isTouchEvent : function(e){
59414         return e.type.match(/^touch/);
59415     },
59416     getCoords : function (e) {
59417         var pt    = this.svgEl.dom.createSVGPoint();
59418         pt.x = e.clientX; 
59419         pt.y = e.clientY;
59420         if (this.isTouchEvent(e)) {
59421             pt.x =  e.targetTouches[0].clientX;
59422             pt.y = e.targetTouches[0].clientY;
59423         }
59424         var a = this.svgEl.dom.getScreenCTM();
59425         var b = a.inverse();
59426         var mx = pt.matrixTransform(b);
59427         return mx.x + ',' + mx.y;
59428     },
59429     //mouse event headler 
59430     down : function (e) {
59431         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
59432         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
59433         
59434         this.isMouseDown = true;
59435         
59436         e.preventDefault();
59437     },
59438     move : function (e) {
59439         if (this.isMouseDown) {
59440             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
59441             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
59442         }
59443         
59444         e.preventDefault();
59445     },
59446     up : function (e) {
59447         this.isMouseDown = false;
59448         var sp = this.signatureTmp.split(' ');
59449         
59450         if(sp.length > 1){
59451             if(!sp[sp.length-2].match(/^L/)){
59452                 sp.pop();
59453                 sp.pop();
59454                 sp.push("");
59455                 this.signatureTmp = sp.join(" ");
59456             }
59457         }
59458         if(this.getValue() != this.signatureTmp){
59459             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
59460             this.isConfirmed = false;
59461         }
59462         e.preventDefault();
59463     },
59464     
59465     /**
59466      * Protected method that will not generally be called directly. It
59467      * is called when the editor creates its toolbar. Override this method if you need to
59468      * add custom toolbar buttons.
59469      * @param {HtmlEditor} editor
59470      */
59471     createToolbar : function(editor){
59472          function btn(id, toggle, handler){
59473             var xid = fid + '-'+ id ;
59474             return {
59475                 id : xid,
59476                 cmd : id,
59477                 cls : 'x-btn-icon x-edit-'+id,
59478                 enableToggle:toggle !== false,
59479                 scope: editor, // was editor...
59480                 handler:handler||editor.relayBtnCmd,
59481                 clickEvent:'mousedown',
59482                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
59483                 tabIndex:-1
59484             };
59485         }
59486         
59487         
59488         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
59489         this.tb = tb;
59490         this.tb.add(
59491            {
59492                 cls : ' x-signature-btn x-signature-'+id,
59493                 scope: editor, // was editor...
59494                 handler: this.reset,
59495                 clickEvent:'mousedown',
59496                 text: this.labels.clear
59497             },
59498             {
59499                  xtype : 'Fill',
59500                  xns: Roo.Toolbar
59501             }, 
59502             {
59503                 cls : '  x-signature-btn x-signature-'+id,
59504                 scope: editor, // was editor...
59505                 handler: this.confirmHandler,
59506                 clickEvent:'mousedown',
59507                 text: this.labels.confirm
59508             }
59509         );
59510     
59511     },
59512     //public
59513     /**
59514      * when user is clicked confirm then show this image.....
59515      * 
59516      * @return {String} Image Data URI
59517      */
59518     getImageDataURI : function(){
59519         var svg = this.svgEl.dom.parentNode.innerHTML;
59520         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
59521         return src; 
59522     },
59523     /**
59524      * 
59525      * @return {Boolean} this.isConfirmed
59526      */
59527     getConfirmed : function(){
59528         return this.isConfirmed;
59529     },
59530     /**
59531      * 
59532      * @return {Number} this.width
59533      */
59534     getWidth : function(){
59535         return this.width;
59536     },
59537     /**
59538      * 
59539      * @return {Number} this.height
59540      */
59541     getHeight : function(){
59542         return this.height;
59543     },
59544     // private
59545     getSignature : function(){
59546         return this.signatureTmp;
59547     },
59548     // private
59549     reset : function(){
59550         this.signatureTmp = '';
59551         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
59552         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
59553         this.isConfirmed = false;
59554         Roo.form.Signature.superclass.reset.call(this);
59555     },
59556     setSignature : function(s){
59557         this.signatureTmp = s;
59558         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
59559         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
59560         this.setValue(s);
59561         this.isConfirmed = false;
59562         Roo.form.Signature.superclass.reset.call(this);
59563     }, 
59564     test : function(){
59565 //        Roo.log(this.signPanel.dom.contentWindow.up())
59566     },
59567     //private
59568     setConfirmed : function(){
59569         
59570         
59571         
59572 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
59573     },
59574     // private
59575     confirmHandler : function(){
59576         if(!this.getSignature()){
59577             return;
59578         }
59579         
59580         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
59581         this.setValue(this.getSignature());
59582         this.isConfirmed = true;
59583         
59584         this.fireEvent('confirm', this);
59585     },
59586     // private
59587     // Subclasses should provide the validation implementation by overriding this
59588     validateValue : function(value){
59589         if(this.allowBlank){
59590             return true;
59591         }
59592         
59593         if(this.isConfirmed){
59594             return true;
59595         }
59596         return false;
59597     }
59598 });/*
59599  * Based on:
59600  * Ext JS Library 1.1.1
59601  * Copyright(c) 2006-2007, Ext JS, LLC.
59602  *
59603  * Originally Released Under LGPL - original licence link has changed is not relivant.
59604  *
59605  * Fork - LGPL
59606  * <script type="text/javascript">
59607  */
59608  
59609
59610 /**
59611  * @class Roo.form.ComboBox
59612  * @extends Roo.form.TriggerField
59613  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
59614  * @constructor
59615  * Create a new ComboBox.
59616  * @param {Object} config Configuration options
59617  */
59618 Roo.form.Select = function(config){
59619     Roo.form.Select.superclass.constructor.call(this, config);
59620      
59621 };
59622
59623 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
59624     /**
59625      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
59626      */
59627     /**
59628      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
59629      * rendering into an Roo.Editor, defaults to false)
59630      */
59631     /**
59632      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
59633      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
59634      */
59635     /**
59636      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
59637      */
59638     /**
59639      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
59640      * the dropdown list (defaults to undefined, with no header element)
59641      */
59642
59643      /**
59644      * @cfg {String/Roo.Template} tpl The template to use to render the output
59645      */
59646      
59647     // private
59648     defaultAutoCreate : {tag: "select"  },
59649     /**
59650      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
59651      */
59652     listWidth: undefined,
59653     /**
59654      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
59655      * mode = 'remote' or 'text' if mode = 'local')
59656      */
59657     displayField: undefined,
59658     /**
59659      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
59660      * mode = 'remote' or 'value' if mode = 'local'). 
59661      * Note: use of a valueField requires the user make a selection
59662      * in order for a value to be mapped.
59663      */
59664     valueField: undefined,
59665     
59666     
59667     /**
59668      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
59669      * field's data value (defaults to the underlying DOM element's name)
59670      */
59671     hiddenName: undefined,
59672     /**
59673      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
59674      */
59675     listClass: '',
59676     /**
59677      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
59678      */
59679     selectedClass: 'x-combo-selected',
59680     /**
59681      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
59682      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
59683      * which displays a downward arrow icon).
59684      */
59685     triggerClass : 'x-form-arrow-trigger',
59686     /**
59687      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
59688      */
59689     shadow:'sides',
59690     /**
59691      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
59692      * anchor positions (defaults to 'tl-bl')
59693      */
59694     listAlign: 'tl-bl?',
59695     /**
59696      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
59697      */
59698     maxHeight: 300,
59699     /**
59700      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
59701      * query specified by the allQuery config option (defaults to 'query')
59702      */
59703     triggerAction: 'query',
59704     /**
59705      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
59706      * (defaults to 4, does not apply if editable = false)
59707      */
59708     minChars : 4,
59709     /**
59710      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
59711      * delay (typeAheadDelay) if it matches a known value (defaults to false)
59712      */
59713     typeAhead: false,
59714     /**
59715      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
59716      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
59717      */
59718     queryDelay: 500,
59719     /**
59720      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
59721      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
59722      */
59723     pageSize: 0,
59724     /**
59725      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
59726      * when editable = true (defaults to false)
59727      */
59728     selectOnFocus:false,
59729     /**
59730      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
59731      */
59732     queryParam: 'query',
59733     /**
59734      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
59735      * when mode = 'remote' (defaults to 'Loading...')
59736      */
59737     loadingText: 'Loading...',
59738     /**
59739      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
59740      */
59741     resizable: false,
59742     /**
59743      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
59744      */
59745     handleHeight : 8,
59746     /**
59747      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
59748      * traditional select (defaults to true)
59749      */
59750     editable: true,
59751     /**
59752      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
59753      */
59754     allQuery: '',
59755     /**
59756      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
59757      */
59758     mode: 'remote',
59759     /**
59760      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
59761      * listWidth has a higher value)
59762      */
59763     minListWidth : 70,
59764     /**
59765      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
59766      * allow the user to set arbitrary text into the field (defaults to false)
59767      */
59768     forceSelection:false,
59769     /**
59770      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
59771      * if typeAhead = true (defaults to 250)
59772      */
59773     typeAheadDelay : 250,
59774     /**
59775      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
59776      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
59777      */
59778     valueNotFoundText : undefined,
59779     
59780     /**
59781      * @cfg {String} defaultValue The value displayed after loading the store.
59782      */
59783     defaultValue: '',
59784     
59785     /**
59786      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
59787      */
59788     blockFocus : false,
59789     
59790     /**
59791      * @cfg {Boolean} disableClear Disable showing of clear button.
59792      */
59793     disableClear : false,
59794     /**
59795      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
59796      */
59797     alwaysQuery : false,
59798     
59799     //private
59800     addicon : false,
59801     editicon: false,
59802     
59803     // element that contains real text value.. (when hidden is used..)
59804      
59805     // private
59806     onRender : function(ct, position){
59807         Roo.form.Field.prototype.onRender.call(this, ct, position);
59808         
59809         if(this.store){
59810             this.store.on('beforeload', this.onBeforeLoad, this);
59811             this.store.on('load', this.onLoad, this);
59812             this.store.on('loadexception', this.onLoadException, this);
59813             this.store.load({});
59814         }
59815         
59816         
59817         
59818     },
59819
59820     // private
59821     initEvents : function(){
59822         //Roo.form.ComboBox.superclass.initEvents.call(this);
59823  
59824     },
59825
59826     onDestroy : function(){
59827        
59828         if(this.store){
59829             this.store.un('beforeload', this.onBeforeLoad, this);
59830             this.store.un('load', this.onLoad, this);
59831             this.store.un('loadexception', this.onLoadException, this);
59832         }
59833         //Roo.form.ComboBox.superclass.onDestroy.call(this);
59834     },
59835
59836     // private
59837     fireKey : function(e){
59838         if(e.isNavKeyPress() && !this.list.isVisible()){
59839             this.fireEvent("specialkey", this, e);
59840         }
59841     },
59842
59843     // private
59844     onResize: function(w, h){
59845         
59846         return; 
59847     
59848         
59849     },
59850
59851     /**
59852      * Allow or prevent the user from directly editing the field text.  If false is passed,
59853      * the user will only be able to select from the items defined in the dropdown list.  This method
59854      * is the runtime equivalent of setting the 'editable' config option at config time.
59855      * @param {Boolean} value True to allow the user to directly edit the field text
59856      */
59857     setEditable : function(value){
59858          
59859     },
59860
59861     // private
59862     onBeforeLoad : function(){
59863         
59864         Roo.log("Select before load");
59865         return;
59866     
59867         this.innerList.update(this.loadingText ?
59868                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
59869         //this.restrictHeight();
59870         this.selectedIndex = -1;
59871     },
59872
59873     // private
59874     onLoad : function(){
59875
59876     
59877         var dom = this.el.dom;
59878         dom.innerHTML = '';
59879          var od = dom.ownerDocument;
59880          
59881         if (this.emptyText) {
59882             var op = od.createElement('option');
59883             op.setAttribute('value', '');
59884             op.innerHTML = String.format('{0}', this.emptyText);
59885             dom.appendChild(op);
59886         }
59887         if(this.store.getCount() > 0){
59888            
59889             var vf = this.valueField;
59890             var df = this.displayField;
59891             this.store.data.each(function(r) {
59892                 // which colmsn to use... testing - cdoe / title..
59893                 var op = od.createElement('option');
59894                 op.setAttribute('value', r.data[vf]);
59895                 op.innerHTML = String.format('{0}', r.data[df]);
59896                 dom.appendChild(op);
59897             });
59898             if (typeof(this.defaultValue != 'undefined')) {
59899                 this.setValue(this.defaultValue);
59900             }
59901             
59902              
59903         }else{
59904             //this.onEmptyResults();
59905         }
59906         //this.el.focus();
59907     },
59908     // private
59909     onLoadException : function()
59910     {
59911         dom.innerHTML = '';
59912             
59913         Roo.log("Select on load exception");
59914         return;
59915     
59916         this.collapse();
59917         Roo.log(this.store.reader.jsonData);
59918         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
59919             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
59920         }
59921         
59922         
59923     },
59924     // private
59925     onTypeAhead : function(){
59926          
59927     },
59928
59929     // private
59930     onSelect : function(record, index){
59931         Roo.log('on select?');
59932         return;
59933         if(this.fireEvent('beforeselect', this, record, index) !== false){
59934             this.setFromData(index > -1 ? record.data : false);
59935             this.collapse();
59936             this.fireEvent('select', this, record, index);
59937         }
59938     },
59939
59940     /**
59941      * Returns the currently selected field value or empty string if no value is set.
59942      * @return {String} value The selected value
59943      */
59944     getValue : function(){
59945         var dom = this.el.dom;
59946         this.value = dom.options[dom.selectedIndex].value;
59947         return this.value;
59948         
59949     },
59950
59951     /**
59952      * Clears any text/value currently set in the field
59953      */
59954     clearValue : function(){
59955         this.value = '';
59956         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
59957         
59958     },
59959
59960     /**
59961      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
59962      * will be displayed in the field.  If the value does not match the data value of an existing item,
59963      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
59964      * Otherwise the field will be blank (although the value will still be set).
59965      * @param {String} value The value to match
59966      */
59967     setValue : function(v){
59968         var d = this.el.dom;
59969         for (var i =0; i < d.options.length;i++) {
59970             if (v == d.options[i].value) {
59971                 d.selectedIndex = i;
59972                 this.value = v;
59973                 return;
59974             }
59975         }
59976         this.clearValue();
59977     },
59978     /**
59979      * @property {Object} the last set data for the element
59980      */
59981     
59982     lastData : false,
59983     /**
59984      * Sets the value of the field based on a object which is related to the record format for the store.
59985      * @param {Object} value the value to set as. or false on reset?
59986      */
59987     setFromData : function(o){
59988         Roo.log('setfrom data?');
59989          
59990         
59991         
59992     },
59993     // private
59994     reset : function(){
59995         this.clearValue();
59996     },
59997     // private
59998     findRecord : function(prop, value){
59999         
60000         return false;
60001     
60002         var record;
60003         if(this.store.getCount() > 0){
60004             this.store.each(function(r){
60005                 if(r.data[prop] == value){
60006                     record = r;
60007                     return false;
60008                 }
60009                 return true;
60010             });
60011         }
60012         return record;
60013     },
60014     
60015     getName: function()
60016     {
60017         // returns hidden if it's set..
60018         if (!this.rendered) {return ''};
60019         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
60020         
60021     },
60022      
60023
60024     
60025
60026     // private
60027     onEmptyResults : function(){
60028         Roo.log('empty results');
60029         //this.collapse();
60030     },
60031
60032     /**
60033      * Returns true if the dropdown list is expanded, else false.
60034      */
60035     isExpanded : function(){
60036         return false;
60037     },
60038
60039     /**
60040      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
60041      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
60042      * @param {String} value The data value of the item to select
60043      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
60044      * selected item if it is not currently in view (defaults to true)
60045      * @return {Boolean} True if the value matched an item in the list, else false
60046      */
60047     selectByValue : function(v, scrollIntoView){
60048         Roo.log('select By Value');
60049         return false;
60050     
60051         if(v !== undefined && v !== null){
60052             var r = this.findRecord(this.valueField || this.displayField, v);
60053             if(r){
60054                 this.select(this.store.indexOf(r), scrollIntoView);
60055                 return true;
60056             }
60057         }
60058         return false;
60059     },
60060
60061     /**
60062      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
60063      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
60064      * @param {Number} index The zero-based index of the list item to select
60065      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
60066      * selected item if it is not currently in view (defaults to true)
60067      */
60068     select : function(index, scrollIntoView){
60069         Roo.log('select ');
60070         return  ;
60071         
60072         this.selectedIndex = index;
60073         this.view.select(index);
60074         if(scrollIntoView !== false){
60075             var el = this.view.getNode(index);
60076             if(el){
60077                 this.innerList.scrollChildIntoView(el, false);
60078             }
60079         }
60080     },
60081
60082       
60083
60084     // private
60085     validateBlur : function(){
60086         
60087         return;
60088         
60089     },
60090
60091     // private
60092     initQuery : function(){
60093         this.doQuery(this.getRawValue());
60094     },
60095
60096     // private
60097     doForce : function(){
60098         if(this.el.dom.value.length > 0){
60099             this.el.dom.value =
60100                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
60101              
60102         }
60103     },
60104
60105     /**
60106      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
60107      * query allowing the query action to be canceled if needed.
60108      * @param {String} query The SQL query to execute
60109      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
60110      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
60111      * saved in the current store (defaults to false)
60112      */
60113     doQuery : function(q, forceAll){
60114         
60115         Roo.log('doQuery?');
60116         if(q === undefined || q === null){
60117             q = '';
60118         }
60119         var qe = {
60120             query: q,
60121             forceAll: forceAll,
60122             combo: this,
60123             cancel:false
60124         };
60125         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
60126             return false;
60127         }
60128         q = qe.query;
60129         forceAll = qe.forceAll;
60130         if(forceAll === true || (q.length >= this.minChars)){
60131             if(this.lastQuery != q || this.alwaysQuery){
60132                 this.lastQuery = q;
60133                 if(this.mode == 'local'){
60134                     this.selectedIndex = -1;
60135                     if(forceAll){
60136                         this.store.clearFilter();
60137                     }else{
60138                         this.store.filter(this.displayField, q);
60139                     }
60140                     this.onLoad();
60141                 }else{
60142                     this.store.baseParams[this.queryParam] = q;
60143                     this.store.load({
60144                         params: this.getParams(q)
60145                     });
60146                     this.expand();
60147                 }
60148             }else{
60149                 this.selectedIndex = -1;
60150                 this.onLoad();   
60151             }
60152         }
60153     },
60154
60155     // private
60156     getParams : function(q){
60157         var p = {};
60158         //p[this.queryParam] = q;
60159         if(this.pageSize){
60160             p.start = 0;
60161             p.limit = this.pageSize;
60162         }
60163         return p;
60164     },
60165
60166     /**
60167      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
60168      */
60169     collapse : function(){
60170         
60171     },
60172
60173     // private
60174     collapseIf : function(e){
60175         
60176     },
60177
60178     /**
60179      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
60180      */
60181     expand : function(){
60182         
60183     } ,
60184
60185     // private
60186      
60187
60188     /** 
60189     * @cfg {Boolean} grow 
60190     * @hide 
60191     */
60192     /** 
60193     * @cfg {Number} growMin 
60194     * @hide 
60195     */
60196     /** 
60197     * @cfg {Number} growMax 
60198     * @hide 
60199     */
60200     /**
60201      * @hide
60202      * @method autoSize
60203      */
60204     
60205     setWidth : function()
60206     {
60207         
60208     },
60209     getResizeEl : function(){
60210         return this.el;
60211     }
60212 });//<script type="text/javasscript">
60213  
60214
60215 /**
60216  * @class Roo.DDView
60217  * A DnD enabled version of Roo.View.
60218  * @param {Element/String} container The Element in which to create the View.
60219  * @param {String} tpl The template string used to create the markup for each element of the View
60220  * @param {Object} config The configuration properties. These include all the config options of
60221  * {@link Roo.View} plus some specific to this class.<br>
60222  * <p>
60223  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
60224  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
60225  * <p>
60226  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
60227 .x-view-drag-insert-above {
60228         border-top:1px dotted #3366cc;
60229 }
60230 .x-view-drag-insert-below {
60231         border-bottom:1px dotted #3366cc;
60232 }
60233 </code></pre>
60234  * 
60235  */
60236  
60237 Roo.DDView = function(container, tpl, config) {
60238     Roo.DDView.superclass.constructor.apply(this, arguments);
60239     this.getEl().setStyle("outline", "0px none");
60240     this.getEl().unselectable();
60241     if (this.dragGroup) {
60242         this.setDraggable(this.dragGroup.split(","));
60243     }
60244     if (this.dropGroup) {
60245         this.setDroppable(this.dropGroup.split(","));
60246     }
60247     if (this.deletable) {
60248         this.setDeletable();
60249     }
60250     this.isDirtyFlag = false;
60251         this.addEvents({
60252                 "drop" : true
60253         });
60254 };
60255
60256 Roo.extend(Roo.DDView, Roo.View, {
60257 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
60258 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
60259 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
60260 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
60261
60262         isFormField: true,
60263
60264         reset: Roo.emptyFn,
60265         
60266         clearInvalid: Roo.form.Field.prototype.clearInvalid,
60267
60268         validate: function() {
60269                 return true;
60270         },
60271         
60272         destroy: function() {
60273                 this.purgeListeners();
60274                 this.getEl.removeAllListeners();
60275                 this.getEl().remove();
60276                 if (this.dragZone) {
60277                         if (this.dragZone.destroy) {
60278                                 this.dragZone.destroy();
60279                         }
60280                 }
60281                 if (this.dropZone) {
60282                         if (this.dropZone.destroy) {
60283                                 this.dropZone.destroy();
60284                         }
60285                 }
60286         },
60287
60288 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
60289         getName: function() {
60290                 return this.name;
60291         },
60292
60293 /**     Loads the View from a JSON string representing the Records to put into the Store. */
60294         setValue: function(v) {
60295                 if (!this.store) {
60296                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
60297                 }
60298                 var data = {};
60299                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
60300                 this.store.proxy = new Roo.data.MemoryProxy(data);
60301                 this.store.load();
60302         },
60303
60304 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
60305         getValue: function() {
60306                 var result = '(';
60307                 this.store.each(function(rec) {
60308                         result += rec.id + ',';
60309                 });
60310                 return result.substr(0, result.length - 1) + ')';
60311         },
60312         
60313         getIds: function() {
60314                 var i = 0, result = new Array(this.store.getCount());
60315                 this.store.each(function(rec) {
60316                         result[i++] = rec.id;
60317                 });
60318                 return result;
60319         },
60320         
60321         isDirty: function() {
60322                 return this.isDirtyFlag;
60323         },
60324
60325 /**
60326  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
60327  *      whole Element becomes the target, and this causes the drop gesture to append.
60328  */
60329     getTargetFromEvent : function(e) {
60330                 var target = e.getTarget();
60331                 while ((target !== null) && (target.parentNode != this.el.dom)) {
60332                 target = target.parentNode;
60333                 }
60334                 if (!target) {
60335                         target = this.el.dom.lastChild || this.el.dom;
60336                 }
60337                 return target;
60338     },
60339
60340 /**
60341  *      Create the drag data which consists of an object which has the property "ddel" as
60342  *      the drag proxy element. 
60343  */
60344     getDragData : function(e) {
60345         var target = this.findItemFromChild(e.getTarget());
60346                 if(target) {
60347                         this.handleSelection(e);
60348                         var selNodes = this.getSelectedNodes();
60349             var dragData = {
60350                 source: this,
60351                 copy: this.copy || (this.allowCopy && e.ctrlKey),
60352                 nodes: selNodes,
60353                 records: []
60354                         };
60355                         var selectedIndices = this.getSelectedIndexes();
60356                         for (var i = 0; i < selectedIndices.length; i++) {
60357                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
60358                         }
60359                         if (selNodes.length == 1) {
60360                                 dragData.ddel = target.cloneNode(true); // the div element
60361                         } else {
60362                                 var div = document.createElement('div'); // create the multi element drag "ghost"
60363                                 div.className = 'multi-proxy';
60364                                 for (var i = 0, len = selNodes.length; i < len; i++) {
60365                                         div.appendChild(selNodes[i].cloneNode(true));
60366                                 }
60367                                 dragData.ddel = div;
60368                         }
60369             //console.log(dragData)
60370             //console.log(dragData.ddel.innerHTML)
60371                         return dragData;
60372                 }
60373         //console.log('nodragData')
60374                 return false;
60375     },
60376     
60377 /**     Specify to which ddGroup items in this DDView may be dragged. */
60378     setDraggable: function(ddGroup) {
60379         if (ddGroup instanceof Array) {
60380                 Roo.each(ddGroup, this.setDraggable, this);
60381                 return;
60382         }
60383         if (this.dragZone) {
60384                 this.dragZone.addToGroup(ddGroup);
60385         } else {
60386                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
60387                                 containerScroll: true,
60388                                 ddGroup: ddGroup 
60389
60390                         });
60391 //                      Draggability implies selection. DragZone's mousedown selects the element.
60392                         if (!this.multiSelect) { this.singleSelect = true; }
60393
60394 //                      Wire the DragZone's handlers up to methods in *this*
60395                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
60396                 }
60397     },
60398
60399 /**     Specify from which ddGroup this DDView accepts drops. */
60400     setDroppable: function(ddGroup) {
60401         if (ddGroup instanceof Array) {
60402                 Roo.each(ddGroup, this.setDroppable, this);
60403                 return;
60404         }
60405         if (this.dropZone) {
60406                 this.dropZone.addToGroup(ddGroup);
60407         } else {
60408                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
60409                                 containerScroll: true,
60410                                 ddGroup: ddGroup
60411                         });
60412
60413 //                      Wire the DropZone's handlers up to methods in *this*
60414                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
60415                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
60416                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
60417                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
60418                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
60419                 }
60420     },
60421
60422 /**     Decide whether to drop above or below a View node. */
60423     getDropPoint : function(e, n, dd){
60424         if (n == this.el.dom) { return "above"; }
60425                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
60426                 var c = t + (b - t) / 2;
60427                 var y = Roo.lib.Event.getPageY(e);
60428                 if(y <= c) {
60429                         return "above";
60430                 }else{
60431                         return "below";
60432                 }
60433     },
60434
60435     onNodeEnter : function(n, dd, e, data){
60436                 return false;
60437     },
60438     
60439     onNodeOver : function(n, dd, e, data){
60440                 var pt = this.getDropPoint(e, n, dd);
60441                 // set the insert point style on the target node
60442                 var dragElClass = this.dropNotAllowed;
60443                 if (pt) {
60444                         var targetElClass;
60445                         if (pt == "above"){
60446                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
60447                                 targetElClass = "x-view-drag-insert-above";
60448                         } else {
60449                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
60450                                 targetElClass = "x-view-drag-insert-below";
60451                         }
60452                         if (this.lastInsertClass != targetElClass){
60453                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
60454                                 this.lastInsertClass = targetElClass;
60455                         }
60456                 }
60457                 return dragElClass;
60458         },
60459
60460     onNodeOut : function(n, dd, e, data){
60461                 this.removeDropIndicators(n);
60462     },
60463
60464     onNodeDrop : function(n, dd, e, data){
60465         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
60466                 return false;
60467         }
60468         var pt = this.getDropPoint(e, n, dd);
60469                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
60470                 if (pt == "below") { insertAt++; }
60471                 for (var i = 0; i < data.records.length; i++) {
60472                         var r = data.records[i];
60473                         var dup = this.store.getById(r.id);
60474                         if (dup && (dd != this.dragZone)) {
60475                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
60476                         } else {
60477                                 if (data.copy) {
60478                                         this.store.insert(insertAt++, r.copy());
60479                                 } else {
60480                                         data.source.isDirtyFlag = true;
60481                                         r.store.remove(r);
60482                                         this.store.insert(insertAt++, r);
60483                                 }
60484                                 this.isDirtyFlag = true;
60485                         }
60486                 }
60487                 this.dragZone.cachedTarget = null;
60488                 return true;
60489     },
60490
60491     removeDropIndicators : function(n){
60492                 if(n){
60493                         Roo.fly(n).removeClass([
60494                                 "x-view-drag-insert-above",
60495                                 "x-view-drag-insert-below"]);
60496                         this.lastInsertClass = "_noclass";
60497                 }
60498     },
60499
60500 /**
60501  *      Utility method. Add a delete option to the DDView's context menu.
60502  *      @param {String} imageUrl The URL of the "delete" icon image.
60503  */
60504         setDeletable: function(imageUrl) {
60505                 if (!this.singleSelect && !this.multiSelect) {
60506                         this.singleSelect = true;
60507                 }
60508                 var c = this.getContextMenu();
60509                 this.contextMenu.on("itemclick", function(item) {
60510                         switch (item.id) {
60511                                 case "delete":
60512                                         this.remove(this.getSelectedIndexes());
60513                                         break;
60514                         }
60515                 }, this);
60516                 this.contextMenu.add({
60517                         icon: imageUrl,
60518                         id: "delete",
60519                         text: 'Delete'
60520                 });
60521         },
60522         
60523 /**     Return the context menu for this DDView. */
60524         getContextMenu: function() {
60525                 if (!this.contextMenu) {
60526 //                      Create the View's context menu
60527                         this.contextMenu = new Roo.menu.Menu({
60528                                 id: this.id + "-contextmenu"
60529                         });
60530                         this.el.on("contextmenu", this.showContextMenu, this);
60531                 }
60532                 return this.contextMenu;
60533         },
60534         
60535         disableContextMenu: function() {
60536                 if (this.contextMenu) {
60537                         this.el.un("contextmenu", this.showContextMenu, this);
60538                 }
60539         },
60540
60541         showContextMenu: function(e, item) {
60542         item = this.findItemFromChild(e.getTarget());
60543                 if (item) {
60544                         e.stopEvent();
60545                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
60546                         this.contextMenu.showAt(e.getXY());
60547             }
60548     },
60549
60550 /**
60551  *      Remove {@link Roo.data.Record}s at the specified indices.
60552  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
60553  */
60554     remove: function(selectedIndices) {
60555                 selectedIndices = [].concat(selectedIndices);
60556                 for (var i = 0; i < selectedIndices.length; i++) {
60557                         var rec = this.store.getAt(selectedIndices[i]);
60558                         this.store.remove(rec);
60559                 }
60560     },
60561
60562 /**
60563  *      Double click fires the event, but also, if this is draggable, and there is only one other
60564  *      related DropZone, it transfers the selected node.
60565  */
60566     onDblClick : function(e){
60567         var item = this.findItemFromChild(e.getTarget());
60568         if(item){
60569             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
60570                 return false;
60571             }
60572             if (this.dragGroup) {
60573                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
60574                     while (targets.indexOf(this.dropZone) > -1) {
60575                             targets.remove(this.dropZone);
60576                                 }
60577                     if (targets.length == 1) {
60578                                         this.dragZone.cachedTarget = null;
60579                         var el = Roo.get(targets[0].getEl());
60580                         var box = el.getBox(true);
60581                         targets[0].onNodeDrop(el.dom, {
60582                                 target: el.dom,
60583                                 xy: [box.x, box.y + box.height - 1]
60584                         }, null, this.getDragData(e));
60585                     }
60586                 }
60587         }
60588     },
60589     
60590     handleSelection: function(e) {
60591                 this.dragZone.cachedTarget = null;
60592         var item = this.findItemFromChild(e.getTarget());
60593         if (!item) {
60594                 this.clearSelections(true);
60595                 return;
60596         }
60597                 if (item && (this.multiSelect || this.singleSelect)){
60598                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
60599                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
60600                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
60601                                 this.unselect(item);
60602                         } else {
60603                                 this.select(item, this.multiSelect && e.ctrlKey);
60604                                 this.lastSelection = item;
60605                         }
60606                 }
60607     },
60608
60609     onItemClick : function(item, index, e){
60610                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
60611                         return false;
60612                 }
60613                 return true;
60614     },
60615
60616     unselect : function(nodeInfo, suppressEvent){
60617                 var node = this.getNode(nodeInfo);
60618                 if(node && this.isSelected(node)){
60619                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
60620                                 Roo.fly(node).removeClass(this.selectedClass);
60621                                 this.selections.remove(node);
60622                                 if(!suppressEvent){
60623                                         this.fireEvent("selectionchange", this, this.selections);
60624                                 }
60625                         }
60626                 }
60627     }
60628 });
60629 Roo.layout = {};/*
60630  * Based on:
60631  * Ext JS Library 1.1.1
60632  * Copyright(c) 2006-2007, Ext JS, LLC.
60633  *
60634  * Originally Released Under LGPL - original licence link has changed is not relivant.
60635  *
60636  * Fork - LGPL
60637  * <script type="text/javascript">
60638  */
60639  
60640 /**
60641  * @class Roo.layout.Manager
60642  * @extends Roo.util.Observable
60643  * Base class for layout managers.
60644  */
60645 Roo.layout.Manager = function(container, config){
60646     Roo.layout.Manager.superclass.constructor.call(this);
60647     this.el = Roo.get(container);
60648     // ie scrollbar fix
60649     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
60650         document.body.scroll = "no";
60651     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
60652         this.el.position('relative');
60653     }
60654     this.id = this.el.id;
60655     this.el.addClass("x-layout-container");
60656     /** false to disable window resize monitoring @type Boolean */
60657     this.monitorWindowResize = true;
60658     this.regions = {};
60659     this.addEvents({
60660         /**
60661          * @event layout
60662          * Fires when a layout is performed. 
60663          * @param {Roo.layout.Manager} this
60664          */
60665         "layout" : true,
60666         /**
60667          * @event regionresized
60668          * Fires when the user resizes a region. 
60669          * @param {Roo.layout.Region} region The resized region
60670          * @param {Number} newSize The new size (width for east/west, height for north/south)
60671          */
60672         "regionresized" : true,
60673         /**
60674          * @event regioncollapsed
60675          * Fires when a region is collapsed. 
60676          * @param {Roo.layout.Region} region The collapsed region
60677          */
60678         "regioncollapsed" : true,
60679         /**
60680          * @event regionexpanded
60681          * Fires when a region is expanded.  
60682          * @param {Roo.layout.Region} region The expanded region
60683          */
60684         "regionexpanded" : true
60685     });
60686     this.updating = false;
60687     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
60688 };
60689
60690 Roo.extend(Roo.layout.Manager, Roo.util.Observable, {
60691     /**
60692      * Returns true if this layout is currently being updated
60693      * @return {Boolean}
60694      */
60695     isUpdating : function(){
60696         return this.updating; 
60697     },
60698     
60699     /**
60700      * Suspend the LayoutManager from doing auto-layouts while
60701      * making multiple add or remove calls
60702      */
60703     beginUpdate : function(){
60704         this.updating = true;    
60705     },
60706     
60707     /**
60708      * Restore auto-layouts and optionally disable the manager from performing a layout
60709      * @param {Boolean} noLayout true to disable a layout update 
60710      */
60711     endUpdate : function(noLayout){
60712         this.updating = false;
60713         if(!noLayout){
60714             this.layout();
60715         }    
60716     },
60717     
60718     layout: function(){
60719         
60720     },
60721     
60722     onRegionResized : function(region, newSize){
60723         this.fireEvent("regionresized", region, newSize);
60724         this.layout();
60725     },
60726     
60727     onRegionCollapsed : function(region){
60728         this.fireEvent("regioncollapsed", region);
60729     },
60730     
60731     onRegionExpanded : function(region){
60732         this.fireEvent("regionexpanded", region);
60733     },
60734         
60735     /**
60736      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
60737      * performs box-model adjustments.
60738      * @return {Object} The size as an object {width: (the width), height: (the height)}
60739      */
60740     getViewSize : function(){
60741         var size;
60742         if(this.el.dom != document.body){
60743             size = this.el.getSize();
60744         }else{
60745             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
60746         }
60747         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
60748         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
60749         return size;
60750     },
60751     
60752     /**
60753      * Returns the Element this layout is bound to.
60754      * @return {Roo.Element}
60755      */
60756     getEl : function(){
60757         return this.el;
60758     },
60759     
60760     /**
60761      * Returns the specified region.
60762      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
60763      * @return {Roo.layout.Region}
60764      */
60765     getRegion : function(target){
60766         return this.regions[target.toLowerCase()];
60767     },
60768     
60769     onWindowResize : function(){
60770         if(this.monitorWindowResize){
60771             this.layout();
60772         }
60773     }
60774 });/*
60775  * Based on:
60776  * Ext JS Library 1.1.1
60777  * Copyright(c) 2006-2007, Ext JS, LLC.
60778  *
60779  * Originally Released Under LGPL - original licence link has changed is not relivant.
60780  *
60781  * Fork - LGPL
60782  * <script type="text/javascript">
60783  */
60784 /**
60785  * @class Roo.layout.Border
60786  * @extends Roo.layout.Manager
60787  * @children Roo.panel.Content
60788  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
60789  * please see: <br><br>
60790  * <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>
60791  * <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>
60792  * Example:
60793  <pre><code>
60794  var layout = new Roo.layout.Border(document.body, {
60795     north: {
60796         initialSize: 25,
60797         titlebar: false
60798     },
60799     west: {
60800         split:true,
60801         initialSize: 200,
60802         minSize: 175,
60803         maxSize: 400,
60804         titlebar: true,
60805         collapsible: true
60806     },
60807     east: {
60808         split:true,
60809         initialSize: 202,
60810         minSize: 175,
60811         maxSize: 400,
60812         titlebar: true,
60813         collapsible: true
60814     },
60815     south: {
60816         split:true,
60817         initialSize: 100,
60818         minSize: 100,
60819         maxSize: 200,
60820         titlebar: true,
60821         collapsible: true
60822     },
60823     center: {
60824         titlebar: true,
60825         autoScroll:true,
60826         resizeTabs: true,
60827         minTabWidth: 50,
60828         preferredTabWidth: 150
60829     }
60830 });
60831
60832 // shorthand
60833 var CP = Roo.panel.Content;
60834
60835 layout.beginUpdate();
60836 layout.add("north", new CP("north", "North"));
60837 layout.add("south", new CP("south", {title: "South", closable: true}));
60838 layout.add("west", new CP("west", {title: "West"}));
60839 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
60840 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
60841 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
60842 layout.getRegion("center").showPanel("center1");
60843 layout.endUpdate();
60844 </code></pre>
60845
60846 <b>The container the layout is rendered into can be either the body element or any other element.
60847 If it is not the body element, the container needs to either be an absolute positioned element,
60848 or you will need to add "position:relative" to the css of the container.  You will also need to specify
60849 the container size if it is not the body element.</b>
60850
60851 * @constructor
60852 * Create a new BorderLayout
60853 * @param {String/HTMLElement/Element} container The container this layout is bound to
60854 * @param {Object} config Configuration options
60855  */
60856 Roo.layout.Border = function(container, config){
60857     config = config || {};
60858     Roo.layout.Border.superclass.constructor.call(this, container, config);
60859     this.factory = config.factory || Roo.layout.Border.RegionFactory;
60860     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
60861         var target = this.factory.validRegions[i];
60862         if(config[target]){
60863             this.addRegion(target, config[target]);
60864         }
60865     }
60866 };
60867
60868 Roo.extend(Roo.layout.Border, Roo.layout.Manager, {
60869         
60870         /**
60871          * @cfg {Roo.layout.Region} east
60872          */
60873         /**
60874          * @cfg {Roo.layout.Region} west
60875          */
60876         /**
60877          * @cfg {Roo.layout.Region} north
60878          */
60879         /**
60880          * @cfg {Roo.layout.Region} south
60881          */
60882         /**
60883          * @cfg {Roo.layout.Region} center
60884          */
60885     /**
60886      * Creates and adds a new region if it doesn't already exist.
60887      * @param {String} target The target region key (north, south, east, west or center).
60888      * @param {Object} config The regions config object
60889      * @return {BorderLayoutRegion} The new region
60890      */
60891     addRegion : function(target, config){
60892         if(!this.regions[target]){
60893             var r = this.factory.create(target, this, config);
60894             this.bindRegion(target, r);
60895         }
60896         return this.regions[target];
60897     },
60898
60899     // private (kinda)
60900     bindRegion : function(name, r){
60901         this.regions[name] = r;
60902         r.on("visibilitychange", this.layout, this);
60903         r.on("paneladded", this.layout, this);
60904         r.on("panelremoved", this.layout, this);
60905         r.on("invalidated", this.layout, this);
60906         r.on("resized", this.onRegionResized, this);
60907         r.on("collapsed", this.onRegionCollapsed, this);
60908         r.on("expanded", this.onRegionExpanded, this);
60909     },
60910
60911     /**
60912      * Performs a layout update.
60913      */
60914     layout : function(){
60915         if(this.updating) {
60916             return;
60917         }
60918         var size = this.getViewSize();
60919         var w = size.width;
60920         var h = size.height;
60921         var centerW = w;
60922         var centerH = h;
60923         var centerY = 0;
60924         var centerX = 0;
60925         //var x = 0, y = 0;
60926
60927         var rs = this.regions;
60928         var north = rs["north"];
60929         var south = rs["south"]; 
60930         var west = rs["west"];
60931         var east = rs["east"];
60932         var center = rs["center"];
60933         //if(this.hideOnLayout){ // not supported anymore
60934             //c.el.setStyle("display", "none");
60935         //}
60936         if(north && north.isVisible()){
60937             var b = north.getBox();
60938             var m = north.getMargins();
60939             b.width = w - (m.left+m.right);
60940             b.x = m.left;
60941             b.y = m.top;
60942             centerY = b.height + b.y + m.bottom;
60943             centerH -= centerY;
60944             north.updateBox(this.safeBox(b));
60945         }
60946         if(south && south.isVisible()){
60947             var b = south.getBox();
60948             var m = south.getMargins();
60949             b.width = w - (m.left+m.right);
60950             b.x = m.left;
60951             var totalHeight = (b.height + m.top + m.bottom);
60952             b.y = h - totalHeight + m.top;
60953             centerH -= totalHeight;
60954             south.updateBox(this.safeBox(b));
60955         }
60956         if(west && west.isVisible()){
60957             var b = west.getBox();
60958             var m = west.getMargins();
60959             b.height = centerH - (m.top+m.bottom);
60960             b.x = m.left;
60961             b.y = centerY + m.top;
60962             var totalWidth = (b.width + m.left + m.right);
60963             centerX += totalWidth;
60964             centerW -= totalWidth;
60965             west.updateBox(this.safeBox(b));
60966         }
60967         if(east && east.isVisible()){
60968             var b = east.getBox();
60969             var m = east.getMargins();
60970             b.height = centerH - (m.top+m.bottom);
60971             var totalWidth = (b.width + m.left + m.right);
60972             b.x = w - totalWidth + m.left;
60973             b.y = centerY + m.top;
60974             centerW -= totalWidth;
60975             east.updateBox(this.safeBox(b));
60976         }
60977         if(center){
60978             var m = center.getMargins();
60979             var centerBox = {
60980                 x: centerX + m.left,
60981                 y: centerY + m.top,
60982                 width: centerW - (m.left+m.right),
60983                 height: centerH - (m.top+m.bottom)
60984             };
60985             //if(this.hideOnLayout){
60986                 //center.el.setStyle("display", "block");
60987             //}
60988             center.updateBox(this.safeBox(centerBox));
60989         }
60990         this.el.repaint();
60991         this.fireEvent("layout", this);
60992     },
60993
60994     // private
60995     safeBox : function(box){
60996         box.width = Math.max(0, box.width);
60997         box.height = Math.max(0, box.height);
60998         return box;
60999     },
61000
61001     /**
61002      * Adds a ContentPanel (or subclass) to this layout.
61003      * @param {String} target The target region key (north, south, east, west or center).
61004      * @param {Roo.panel.Content} panel The panel to add
61005      * @return {Roo.panel.Content} The added panel
61006      */
61007     add : function(target, panel){
61008          
61009         target = target.toLowerCase();
61010         return this.regions[target].add(panel);
61011     },
61012
61013     /**
61014      * Remove a ContentPanel (or subclass) to this layout.
61015      * @param {String} target The target region key (north, south, east, west or center).
61016      * @param {Number/String/Roo.panel.Content} panel The index, id or panel to remove
61017      * @return {Roo.panel.Content} The removed panel
61018      */
61019     remove : function(target, panel){
61020         target = target.toLowerCase();
61021         return this.regions[target].remove(panel);
61022     },
61023
61024     /**
61025      * Searches all regions for a panel with the specified id
61026      * @param {String} panelId
61027      * @return {Roo.panel.Content} The panel or null if it wasn't found
61028      */
61029     findPanel : function(panelId){
61030         var rs = this.regions;
61031         for(var target in rs){
61032             if(typeof rs[target] != "function"){
61033                 var p = rs[target].getPanel(panelId);
61034                 if(p){
61035                     return p;
61036                 }
61037             }
61038         }
61039         return null;
61040     },
61041
61042     /**
61043      * Searches all regions for a panel with the specified id and activates (shows) it.
61044      * @param {String/panel.Content} panelId The panels id or the panel itself
61045      * @return {Roo.panel.Content} The shown panel or null
61046      */
61047     showPanel : function(panelId) {
61048       var rs = this.regions;
61049       for(var target in rs){
61050          var r = rs[target];
61051          if(typeof r != "function"){
61052             if(r.hasPanel(panelId)){
61053                return r.showPanel(panelId);
61054             }
61055          }
61056       }
61057       return null;
61058    },
61059
61060    /**
61061      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
61062      * @param {Roo.state.Provider} provider (optional) An alternate state provider
61063      */
61064     restoreState : function(provider){
61065         if(!provider){
61066             provider = Roo.state.Manager;
61067         }
61068         var sm = new Roo.layout.StateManager();
61069         sm.init(this, provider);
61070     },
61071
61072     /**
61073      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
61074      * object should contain properties for each region to add ContentPanels to, and each property's value should be
61075      * a valid ContentPanel config object.  Example:
61076      * <pre><code>
61077 // Create the main layout
61078 var layout = new Roo.layout.Border('main-ct', {
61079     west: {
61080         split:true,
61081         minSize: 175,
61082         titlebar: true
61083     },
61084     center: {
61085         title:'Components'
61086     }
61087 }, 'main-ct');
61088
61089 // Create and add multiple ContentPanels at once via configs
61090 layout.batchAdd({
61091    west: {
61092        id: 'source-files',
61093        autoCreate:true,
61094        title:'Ext Source Files',
61095        autoScroll:true,
61096        fitToFrame:true
61097    },
61098    center : {
61099        el: cview,
61100        autoScroll:true,
61101        fitToFrame:true,
61102        toolbar: tb,
61103        resizeEl:'cbody'
61104    }
61105 });
61106 </code></pre>
61107      * @param {Object} regions An object containing ContentPanel configs by region name
61108      */
61109     batchAdd : function(regions){
61110         this.beginUpdate();
61111         for(var rname in regions){
61112             var lr = this.regions[rname];
61113             if(lr){
61114                 this.addTypedPanels(lr, regions[rname]);
61115             }
61116         }
61117         this.endUpdate();
61118     },
61119
61120     // private
61121     addTypedPanels : function(lr, ps){
61122         if(typeof ps == 'string'){
61123             lr.add(new Roo.panel.Content(ps));
61124         }
61125         else if(ps instanceof Array){
61126             for(var i =0, len = ps.length; i < len; i++){
61127                 this.addTypedPanels(lr, ps[i]);
61128             }
61129         }
61130         else if(!ps.events){ // raw config?
61131             var el = ps.el;
61132             delete ps.el; // prevent conflict
61133             lr.add(new Roo.panel.Content(el || Roo.id(), ps));
61134         }
61135         else {  // panel object assumed!
61136             lr.add(ps);
61137         }
61138     },
61139     /**
61140      * Adds a xtype elements to the layout.
61141      * <pre><code>
61142
61143 layout.addxtype({
61144        xtype : 'ContentPanel',
61145        region: 'west',
61146        items: [ .... ]
61147    }
61148 );
61149
61150 layout.addxtype({
61151         xtype : 'NestedLayoutPanel',
61152         region: 'west',
61153         layout: {
61154            center: { },
61155            west: { }   
61156         },
61157         items : [ ... list of content panels or nested layout panels.. ]
61158    }
61159 );
61160 </code></pre>
61161      * @param {Object} cfg Xtype definition of item to add.
61162      */
61163     addxtype : function(cfg)
61164     {
61165         // basically accepts a pannel...
61166         // can accept a layout region..!?!?
61167         //Roo.log('Roo.layout.Border add ' + cfg.xtype)
61168         
61169         // if (!cfg.xtype.match(/Panel$/)) {
61170         //     return false;
61171         // }
61172         var ret = false;
61173
61174         if (typeof(cfg.region) == 'undefined') {
61175             Roo.log("Failed to add Panel, region was not set");
61176             Roo.log(cfg);
61177             return false;
61178         }
61179         var region = cfg.region;
61180         delete cfg.region;
61181         
61182           
61183         var xitems = [];
61184         if (cfg.items) {
61185             xitems = cfg.items;
61186             delete cfg.items;
61187         }
61188         var nb = false;
61189         
61190         switch(cfg.xtype) 
61191         {
61192             case 'Content':
61193                 if(cfg.autoCreate) {
61194                     ret = new Roo.panel[cfg.xtype](cfg); // new panel!!!!!
61195                 } else {
61196                     var el = this.el.createChild();
61197                     ret = new Roo.panel[cfg.xtype](el, cfg); // new panel!!!!!
61198                 }
61199                 
61200                 this.add(region, ret);
61201                 break;
61202             case 'Grid':
61203                 // needs grid and region
61204                 
61205                 //var el = this.getRegion(region).el.createChild();
61206                 var el = this.el.createChild();
61207                 // create the grid first...
61208                 
61209                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
61210                 delete cfg.grid;
61211                 if (region == 'center' && this.active ) {
61212                     cfg.background = false;
61213                 }
61214                 ret = new Roo.panel[cfg.xtype](grid, cfg); // new panel!!!!!
61215                 
61216                 this.add(region, ret);
61217                 if (cfg.background) {
61218                     ret.on('activate', function(gp) {
61219                         if (!gp.grid.rendered) {
61220                             gp.grid.render();
61221                         }
61222                     });
61223                 } else {
61224                     grid.render();
61225                 }
61226                 break;
61227             case 'NestedLayout': 
61228                 // create a new Layout (which is  a Border Layout...
61229                 var el = this.el.createChild();
61230                 var clayout = cfg.layout;
61231                 delete cfg.layout;
61232                 clayout.items   = clayout.items  || [];
61233                 // replace this exitems with the clayout ones..
61234                 xitems = clayout.items;
61235                  
61236                 
61237                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
61238                     cfg.background = false;
61239                 }
61240                 var layout = new Roo.layout.Border(el, clayout);
61241                 
61242                 ret = new Roo.panel[cfg.xtype](layout, cfg); // new panel!!!!!
61243                 //console.log('adding nested layout panel '  + cfg.toSource());
61244                 this.add(region, ret);
61245                 nb = {}; /// find first...
61246                 break;
61247                 
61248             case 'Calendar':
61249                 ret = new Roo.panel[cfg.xtype](cfg); // new panel!!!!!
61250                 this.add(region, ret);
61251                 break;
61252             case 'Tree': // our new panel!
61253                 cfg.el = this.el.createChild();
61254                 ret = new Roo.panel[cfg.xtype](cfg); // new panel!!!!!
61255                 this.add(region, ret);
61256                 break;
61257             case 'ContentPanel':
61258             case 'ScrollPanel':  // ContentPanel (el, cfg)
61259             case 'ViewPanel': 
61260                 if(cfg.autoCreate) {
61261                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
61262                 } else {
61263                     var el = this.el.createChild();
61264                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
61265                 }
61266                 
61267                 this.add(region, ret);
61268                 break;
61269             
61270             
61271             case 'TreePanel': // our new panel!
61272                 cfg.el = this.el.createChild();
61273                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
61274                 this.add(region, ret);
61275                 break;
61276             
61277             case 'NestedLayoutPanel': 
61278                 // create a new Layout (which is  a Border Layout...
61279                 var el = this.el.createChild();
61280                 var clayout = cfg.layout;
61281                 delete cfg.layout;
61282                 clayout.items   = clayout.items  || [];
61283                 // replace this exitems with the clayout ones..
61284                 xitems = clayout.items;
61285                  
61286                 
61287                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
61288                     cfg.background = false;
61289                 }
61290                 var layout = new Roo.layout.Border(el, clayout);
61291                 
61292                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
61293                 //console.log('adding nested layout panel '  + cfg.toSource());
61294                 this.add(region, ret);
61295                 nb = {}; /// find first...
61296                 break;
61297                 
61298             case 'GridPanel': 
61299             
61300                 // needs grid and region
61301                 
61302                 //var el = this.getRegion(region).el.createChild();
61303                 var el = this.el.createChild();
61304                 // create the grid first...
61305                 
61306                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
61307                 delete cfg.grid;
61308                 if (region == 'center' && this.active ) {
61309                     cfg.background = false;
61310                 }
61311                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
61312                 
61313                 this.add(region, ret);
61314                 if (cfg.background) {
61315                     ret.on('activate', function(gp) {
61316                         if (!gp.grid.rendered) {
61317                             gp.grid.render();
61318                         }
61319                     });
61320                 } else {
61321                     grid.render();
61322                 }
61323                 break;
61324            
61325            
61326            
61327                 
61328                 
61329                 
61330             default:
61331                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
61332                     
61333                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
61334                     this.add(region, ret);
61335                 } else {
61336                 
61337                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
61338                     return null;
61339                 }
61340                 
61341              // GridPanel (grid, cfg)
61342             
61343         }
61344         this.beginUpdate();
61345         // add children..
61346         var region = '';
61347         var abn = {};
61348         Roo.each(xitems, function(i)  {
61349             region = nb && i.region ? i.region : false;
61350             
61351             var add = ret.addxtype(i);
61352            
61353             if (region) {
61354                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
61355                 if (!i.background) {
61356                     abn[region] = nb[region] ;
61357                 }
61358             }
61359             
61360         });
61361         this.endUpdate();
61362
61363         // make the last non-background panel active..
61364         //if (nb) { Roo.log(abn); }
61365         if (nb) {
61366             
61367             for(var r in abn) {
61368                 region = this.getRegion(r);
61369                 if (region) {
61370                     // tried using nb[r], but it does not work..
61371                      
61372                     region.showPanel(abn[r]);
61373                    
61374                 }
61375             }
61376         }
61377         return ret;
61378         
61379     }
61380 });
61381
61382 /**
61383  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
61384  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
61385  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
61386  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
61387  * <pre><code>
61388 // shorthand
61389 var CP = Roo.ContentPanel;
61390
61391 var layout = Roo.layout.Border.create({
61392     north: {
61393         initialSize: 25,
61394         titlebar: false,
61395         panels: [new CP("north", "North")]
61396     },
61397     west: {
61398         split:true,
61399         initialSize: 200,
61400         minSize: 175,
61401         maxSize: 400,
61402         titlebar: true,
61403         collapsible: true,
61404         panels: [new CP("west", {title: "West"})]
61405     },
61406     east: {
61407         split:true,
61408         initialSize: 202,
61409         minSize: 175,
61410         maxSize: 400,
61411         titlebar: true,
61412         collapsible: true,
61413         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
61414     },
61415     south: {
61416         split:true,
61417         initialSize: 100,
61418         minSize: 100,
61419         maxSize: 200,
61420         titlebar: true,
61421         collapsible: true,
61422         panels: [new CP("south", {title: "South", closable: true})]
61423     },
61424     center: {
61425         titlebar: true,
61426         autoScroll:true,
61427         resizeTabs: true,
61428         minTabWidth: 50,
61429         preferredTabWidth: 150,
61430         panels: [
61431             new CP("center1", {title: "Close Me", closable: true}),
61432             new CP("center2", {title: "Center Panel", closable: false})
61433         ]
61434     }
61435 }, document.body);
61436
61437 layout.getRegion("center").showPanel("center1");
61438 </code></pre>
61439  * @param config
61440  * @param targetEl
61441  */
61442 Roo.layout.Border.create = function(config, targetEl){
61443     var layout = new Roo.layout.Border(targetEl || document.body, config);
61444     layout.beginUpdate();
61445     var regions = Roo.layout.Border.RegionFactory.validRegions;
61446     for(var j = 0, jlen = regions.length; j < jlen; j++){
61447         var lr = regions[j];
61448         if(layout.regions[lr] && config[lr].panels){
61449             var r = layout.regions[lr];
61450             var ps = config[lr].panels;
61451             layout.addTypedPanels(r, ps);
61452         }
61453     }
61454     layout.endUpdate();
61455     return layout;
61456 };
61457
61458 // private
61459 Roo.layout.Border.RegionFactory = {
61460     // private
61461     validRegions : ["north","south","east","west","center"],
61462
61463     // private
61464     create : function(target, mgr, config){
61465         target = target.toLowerCase();
61466         if(config.lightweight || config.basic){
61467             return new Roo.layout.BasicRegion(mgr, config, target);
61468         }
61469                 var cn = target.charAt(0).toUpperCase() + target.slice(1);
61470                 if (typeof (Roo.layout[cn]) == 'undefined') {
61471                         throw 'Layout region "'+target+'" not supported.';
61472                 }
61473                 return new Roo.layout[cn](mgr, config);
61474          
61475         
61476     }
61477 };/*
61478  * Based on:
61479  * Ext JS Library 1.1.1
61480  * Copyright(c) 2006-2007, Ext JS, LLC.
61481  *
61482  * Originally Released Under LGPL - original licence link has changed is not relivant.
61483  *
61484  * Fork - LGPL
61485  * <script type="text/javascript">
61486  */
61487  
61488 /**
61489  * @class Roo.layout.BasicRegion
61490  * @extends Roo.util.Observable
61491  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
61492  * and does not have a titlebar, tabs or any other features. All it does is size and position 
61493  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
61494  */
61495 Roo.layout.BasicRegion= function(mgr, config, pos, skipConfig){
61496     this.mgr = mgr;
61497     this.position  = pos;
61498     this.events = {
61499         /**
61500          * @scope Roo.layout.BasicRegion
61501          */
61502         
61503         /**
61504          * @event beforeremove
61505          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
61506          * @param {Roo.layout.Region} this
61507          * @param {Roo.panel.Content} panel The panel
61508          * @param {Object} e The cancel event object
61509          */
61510         "beforeremove" : true,
61511         /**
61512          * @event invalidated
61513          * Fires when the layout for this region is changed.
61514          * @param {Roo.layout.Region} this
61515          */
61516         "invalidated" : true,
61517         /**
61518          * @event visibilitychange
61519          * Fires when this region is shown or hidden 
61520          * @param {Roo.layout.Region} this
61521          * @param {Boolean} visibility true or false
61522          */
61523         "visibilitychange" : true,
61524         /**
61525          * @event paneladded
61526          * Fires when a panel is added. 
61527          * @param {Roo.layout.Region} this
61528          * @param {Roo.panel.Content} panel The panel
61529          */
61530         "paneladded" : true,
61531         /**
61532          * @event panelremoved
61533          * Fires when a panel is removed. 
61534          * @param {Roo.layout.Region} this
61535          * @param {Roo.panel.Content} panel The panel
61536          */
61537         "panelremoved" : true,
61538         /**
61539          * @event beforecollapse
61540          * Fires when this region before collapse.
61541          * @param {Roo.layout.Region} this
61542          */
61543         "beforecollapse" : true,
61544         /**
61545          * @event collapsed
61546          * Fires when this region is collapsed.
61547          * @param {Roo.layout.Region} this
61548          */
61549         "collapsed" : true,
61550         /**
61551          * @event expanded
61552          * Fires when this region is expanded.
61553          * @param {Roo.layout.Region} this
61554          */
61555         "expanded" : true,
61556         /**
61557          * @event slideshow
61558          * Fires when this region is slid into view.
61559          * @param {Roo.layout.Region} this
61560          */
61561         "slideshow" : true,
61562         /**
61563          * @event slidehide
61564          * Fires when this region slides out of view. 
61565          * @param {Roo.layout.Region} this
61566          */
61567         "slidehide" : true,
61568         /**
61569          * @event panelactivated
61570          * Fires when a panel is activated. 
61571          * @param {Roo.layout.Region} this
61572          * @param {Roo.panel.Content} panel The activated panel
61573          */
61574         "panelactivated" : true,
61575         /**
61576          * @event resized
61577          * Fires when the user resizes this region. 
61578          * @param {Roo.layout.Region} this
61579          * @param {Number} newSize The new size (width for east/west, height for north/south)
61580          */
61581         "resized" : true
61582     };
61583     /** A collection of panels in this region. @type Roo.util.MixedCollection */
61584     this.panels = new Roo.util.MixedCollection();
61585     this.panels.getKey = this.getPanelId.createDelegate(this);
61586     this.box = null;
61587     this.activePanel = null;
61588     // ensure listeners are added...
61589     
61590     if (config.listeners || config.events) {
61591         Roo.layout.BasicRegion.superclass.constructor.call(this, {
61592             listeners : config.listeners || {},
61593             events : config.events || {}
61594         });
61595     }
61596     
61597     if(skipConfig !== true){
61598         this.applyConfig(config);
61599     }
61600 };
61601
61602 Roo.extend(Roo.layout.BasicRegion, Roo.util.Observable, {
61603     getPanelId : function(p){
61604         return p.getId();
61605     },
61606     
61607     applyConfig : function(config){
61608         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
61609         this.config = config;
61610         
61611     },
61612     
61613     /**
61614      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
61615      * the width, for horizontal (north, south) the height.
61616      * @param {Number} newSize The new width or height
61617      */
61618     resizeTo : function(newSize){
61619         var el = this.el ? this.el :
61620                  (this.activePanel ? this.activePanel.getEl() : null);
61621         if(el){
61622             switch(this.position){
61623                 case "east":
61624                 case "west":
61625                     el.setWidth(newSize);
61626                     this.fireEvent("resized", this, newSize);
61627                 break;
61628                 case "north":
61629                 case "south":
61630                     el.setHeight(newSize);
61631                     this.fireEvent("resized", this, newSize);
61632                 break;                
61633             }
61634         }
61635     },
61636     
61637     getBox : function(){
61638         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
61639     },
61640     
61641     getMargins : function(){
61642         return this.margins;
61643     },
61644     
61645     updateBox : function(box){
61646         this.box = box;
61647         var el = this.activePanel.getEl();
61648         el.dom.style.left = box.x + "px";
61649         el.dom.style.top = box.y + "px";
61650         this.activePanel.setSize(box.width, box.height);
61651     },
61652     
61653     /**
61654      * Returns the container element for this region.
61655      * @return {Roo.Element}
61656      */
61657     getEl : function(){
61658         return this.activePanel;
61659     },
61660     
61661     /**
61662      * Returns true if this region is currently visible.
61663      * @return {Boolean}
61664      */
61665     isVisible : function(){
61666         return this.activePanel ? true : false;
61667     },
61668     
61669     setActivePanel : function(panel){
61670         panel = this.getPanel(panel);
61671         if(this.activePanel && this.activePanel != panel){
61672             this.activePanel.setActiveState(false);
61673             this.activePanel.getEl().setLeftTop(-10000,-10000);
61674         }
61675         this.activePanel = panel;
61676         panel.setActiveState(true);
61677         if(this.box){
61678             panel.setSize(this.box.width, this.box.height);
61679         }
61680         this.fireEvent("panelactivated", this, panel);
61681         this.fireEvent("invalidated");
61682     },
61683     
61684     /**
61685      * Show the specified panel.
61686      * @param {Number/String/panel.Content} panelId The panels index, id or the panel itself
61687      * @return {Roo.panel.Content} The shown panel or null
61688      */
61689     showPanel : function(panel){
61690         if(panel = this.getPanel(panel)){
61691             this.setActivePanel(panel);
61692         }
61693         return panel;
61694     },
61695     
61696     /**
61697      * Get the active panel for this region.
61698      * @return {Roo.panel.Content} The active panel or null
61699      */
61700     getActivePanel : function(){
61701         return this.activePanel;
61702     },
61703     
61704     /**
61705      * Add the passed ContentPanel(s)
61706      * @param {panel.Content...} panel The ContentPanel(s) to add (you can pass more than one)
61707      * @return {Roo.panel.Content} The panel added (if only one was added)
61708      */
61709     add : function(panel){
61710         if(arguments.length > 1){
61711             for(var i = 0, len = arguments.length; i < len; i++) {
61712                 this.add(arguments[i]);
61713             }
61714             return null;
61715         }
61716         if(this.hasPanel(panel)){
61717             this.showPanel(panel);
61718             return panel;
61719         }
61720         var el = panel.getEl();
61721         if(el.dom.parentNode != this.mgr.el.dom){
61722             this.mgr.el.dom.appendChild(el.dom);
61723         }
61724         if(panel.setRegion){
61725             panel.setRegion(this);
61726         }
61727         this.panels.add(panel);
61728         el.setStyle("position", "absolute");
61729         if(!panel.background){
61730             this.setActivePanel(panel);
61731             if(this.config.initialSize && this.panels.getCount()==1){
61732                 this.resizeTo(this.config.initialSize);
61733             }
61734         }
61735         this.fireEvent("paneladded", this, panel);
61736         return panel;
61737     },
61738     
61739     /**
61740      * Returns true if the panel is in this region.
61741      * @param {Number/String/panel.Content} panel The panels index, id or the panel itself
61742      * @return {Boolean}
61743      */
61744     hasPanel : function(panel){
61745         if(typeof panel == "object"){ // must be panel obj
61746             panel = panel.getId();
61747         }
61748         return this.getPanel(panel) ? true : false;
61749     },
61750     
61751     /**
61752      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
61753      * @param {Number/String/panel.Content} panel The panels index, id or the panel itself
61754      * @param {Boolean} preservePanel Overrides the config preservePanel option
61755      * @return {Roo.panel.Content} The panel that was removed
61756      */
61757     remove : function(panel, preservePanel){
61758         panel = this.getPanel(panel);
61759         if(!panel){
61760             return null;
61761         }
61762         var e = {};
61763         this.fireEvent("beforeremove", this, panel, e);
61764         if(e.cancel === true){
61765             return null;
61766         }
61767         var panelId = panel.getId();
61768         this.panels.removeKey(panelId);
61769         return panel;
61770     },
61771     
61772     /**
61773      * Returns the panel specified or null if it's not in this region.
61774      * @param {Number/String/panel.Content} panel The panels index, id or the panel itself
61775      * @return {Roo.panel.Content}
61776      */
61777     getPanel : function(id){
61778         if(typeof id == "object"){ // must be panel obj
61779             return id;
61780         }
61781         return this.panels.get(id);
61782     },
61783     
61784     /**
61785      * Returns this regions position (north/south/east/west/center).
61786      * @return {String} 
61787      */
61788     getPosition: function(){
61789         return this.position;    
61790     }
61791 });/*
61792  * Based on:
61793  * Ext JS Library 1.1.1
61794  * Copyright(c) 2006-2007, Ext JS, LLC.
61795  *
61796  * Originally Released Under LGPL - original licence link has changed is not relivant.
61797  *
61798  * Fork - LGPL
61799  * <script type="text/javascript">
61800  */
61801  
61802 /**
61803  * @class Roo.layout.Region
61804  * @extends Roo.layout.BasicRegion
61805  * This class represents a region in a layout manager.
61806  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
61807  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
61808  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
61809  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
61810  * @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})
61811  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
61812  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
61813  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
61814  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
61815  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
61816  * @cfg {String}    title           The title for the region (overrides panel titles)
61817  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
61818  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
61819  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
61820  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
61821  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
61822  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
61823  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
61824  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
61825  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
61826  * @cfg {Boolean}   showPin         True to show a pin button
61827  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
61828  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
61829  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
61830  * @cfg {Number}    width           For East/West panels
61831  * @cfg {Number}    height          For North/South panels
61832  * @cfg {Boolean}   split           To show the splitter
61833  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
61834  */
61835 Roo.layout.Region = function(mgr, config, pos){
61836     Roo.layout.Region.superclass.constructor.call(this, mgr, config, pos, true);
61837     var dh = Roo.DomHelper;
61838     /** This region's container element 
61839     * @type Roo.Element */
61840     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
61841     /** This region's title element 
61842     * @type Roo.Element */
61843
61844     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
61845         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
61846         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
61847     ]}, true);
61848     this.titleEl.enableDisplayMode();
61849     /** This region's title text element 
61850     * @type HTMLElement */
61851     this.titleTextEl = this.titleEl.dom.firstChild;
61852     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
61853     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
61854     this.closeBtn.enableDisplayMode();
61855     this.closeBtn.on("click", this.closeClicked, this);
61856     this.closeBtn.hide();
61857
61858     this.createBody(config);
61859     this.visible = true;
61860     this.collapsed = false;
61861
61862     if(config.hideWhenEmpty){
61863         this.hide();
61864         this.on("paneladded", this.validateVisibility, this);
61865         this.on("panelremoved", this.validateVisibility, this);
61866     }
61867     this.applyConfig(config);
61868 };
61869
61870 Roo.extend(Roo.layout.Region, Roo.layout.BasicRegion, {
61871
61872     createBody : function(){
61873         /** This region's body element 
61874         * @type Roo.Element */
61875         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
61876     },
61877
61878     applyConfig : function(c){
61879         if(c.collapsible && this.position != "center" && !this.collapsedEl){
61880             var dh = Roo.DomHelper;
61881             if(c.titlebar !== false){
61882                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
61883                 this.collapseBtn.on("click", this.collapse, this);
61884                 this.collapseBtn.enableDisplayMode();
61885
61886                 if(c.showPin === true || this.showPin){
61887                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
61888                     this.stickBtn.enableDisplayMode();
61889                     this.stickBtn.on("click", this.expand, this);
61890                     this.stickBtn.hide();
61891                 }
61892             }
61893             /** This region's collapsed element
61894             * @type Roo.Element */
61895             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
61896                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
61897             ]}, true);
61898             if(c.floatable !== false){
61899                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
61900                this.collapsedEl.on("click", this.collapseClick, this);
61901             }
61902
61903             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
61904                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
61905                    id: "message", unselectable: "on", style:{"float":"left"}});
61906                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
61907              }
61908             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
61909             this.expandBtn.on("click", this.expand, this);
61910         }
61911         if(this.collapseBtn){
61912             this.collapseBtn.setVisible(c.collapsible == true);
61913         }
61914         this.cmargins = c.cmargins || this.cmargins ||
61915                          (this.position == "west" || this.position == "east" ?
61916                              {top: 0, left: 2, right:2, bottom: 0} :
61917                              {top: 2, left: 0, right:0, bottom: 2});
61918         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
61919         this.bottomTabs = c.tabPosition != "top";
61920         this.autoScroll = c.autoScroll || false;
61921         if(this.autoScroll){
61922             this.bodyEl.setStyle("overflow", "auto");
61923         }else{
61924             this.bodyEl.setStyle("overflow", "hidden");
61925         }
61926         //if(c.titlebar !== false){
61927             if((!c.titlebar && !c.title) || c.titlebar === false){
61928                 this.titleEl.hide();
61929             }else{
61930                 this.titleEl.show();
61931                 if(c.title){
61932                     this.titleTextEl.innerHTML = c.title;
61933                 }
61934             }
61935         //}
61936         this.duration = c.duration || .30;
61937         this.slideDuration = c.slideDuration || .45;
61938         this.config = c;
61939         if(c.collapsed){
61940             this.collapse(true);
61941         }
61942         if(c.hidden){
61943             this.hide();
61944         }
61945     },
61946     /**
61947      * Returns true if this region is currently visible.
61948      * @return {Boolean}
61949      */
61950     isVisible : function(){
61951         return this.visible;
61952     },
61953
61954     /**
61955      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
61956      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
61957      */
61958     setCollapsedTitle : function(title){
61959         title = title || "&#160;";
61960         if(this.collapsedTitleTextEl){
61961             this.collapsedTitleTextEl.innerHTML = title;
61962         }
61963     },
61964
61965     getBox : function(){
61966         var b;
61967         if(!this.collapsed){
61968             b = this.el.getBox(false, true);
61969         }else{
61970             b = this.collapsedEl.getBox(false, true);
61971         }
61972         return b;
61973     },
61974
61975     getMargins : function(){
61976         return this.collapsed ? this.cmargins : this.margins;
61977     },
61978
61979     highlight : function(){
61980         this.el.addClass("x-layout-panel-dragover");
61981     },
61982
61983     unhighlight : function(){
61984         this.el.removeClass("x-layout-panel-dragover");
61985     },
61986
61987     updateBox : function(box){
61988         this.box = box;
61989         if(!this.collapsed){
61990             this.el.dom.style.left = box.x + "px";
61991             this.el.dom.style.top = box.y + "px";
61992             this.updateBody(box.width, box.height);
61993         }else{
61994             this.collapsedEl.dom.style.left = box.x + "px";
61995             this.collapsedEl.dom.style.top = box.y + "px";
61996             this.collapsedEl.setSize(box.width, box.height);
61997         }
61998         if(this.tabs){
61999             this.tabs.autoSizeTabs();
62000         }
62001     },
62002
62003     updateBody : function(w, h){
62004         if(w !== null){
62005             this.el.setWidth(w);
62006             w -= this.el.getBorderWidth("rl");
62007             if(this.config.adjustments){
62008                 w += this.config.adjustments[0];
62009             }
62010         }
62011         if(h !== null){
62012             this.el.setHeight(h);
62013             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
62014             h -= this.el.getBorderWidth("tb");
62015             if(this.config.adjustments){
62016                 h += this.config.adjustments[1];
62017             }
62018             this.bodyEl.setHeight(h);
62019             if(this.tabs){
62020                 h = this.tabs.syncHeight(h);
62021             }
62022         }
62023         if(this.panelSize){
62024             w = w !== null ? w : this.panelSize.width;
62025             h = h !== null ? h : this.panelSize.height;
62026         }
62027         if(this.activePanel){
62028             var el = this.activePanel.getEl();
62029             w = w !== null ? w : el.getWidth();
62030             h = h !== null ? h : el.getHeight();
62031             this.panelSize = {width: w, height: h};
62032             this.activePanel.setSize(w, h);
62033         }
62034         if(Roo.isIE && this.tabs){
62035             this.tabs.el.repaint();
62036         }
62037     },
62038
62039     /**
62040      * Returns the container element for this region.
62041      * @return {Roo.Element}
62042      */
62043     getEl : function(){
62044         return this.el;
62045     },
62046
62047     /**
62048      * Hides this region.
62049      */
62050     hide : function(){
62051         if(!this.collapsed){
62052             this.el.dom.style.left = "-2000px";
62053             this.el.hide();
62054         }else{
62055             this.collapsedEl.dom.style.left = "-2000px";
62056             this.collapsedEl.hide();
62057         }
62058         this.visible = false;
62059         this.fireEvent("visibilitychange", this, false);
62060     },
62061
62062     /**
62063      * Shows this region if it was previously hidden.
62064      */
62065     show : function(){
62066         if(!this.collapsed){
62067             this.el.show();
62068         }else{
62069             this.collapsedEl.show();
62070         }
62071         this.visible = true;
62072         this.fireEvent("visibilitychange", this, true);
62073     },
62074
62075     closeClicked : function(){
62076         if(this.activePanel){
62077             this.remove(this.activePanel);
62078         }
62079     },
62080
62081     collapseClick : function(e){
62082         if(this.isSlid){
62083            e.stopPropagation();
62084            this.slideIn();
62085         }else{
62086            e.stopPropagation();
62087            this.slideOut();
62088         }
62089     },
62090
62091     /**
62092      * Collapses this region.
62093      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
62094      */
62095     collapse : function(skipAnim, skipCheck){
62096         if(this.collapsed) {
62097             return;
62098         }
62099         
62100         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
62101             
62102             this.collapsed = true;
62103             if(this.split){
62104                 this.split.el.hide();
62105             }
62106             if(this.config.animate && skipAnim !== true){
62107                 this.fireEvent("invalidated", this);
62108                 this.animateCollapse();
62109             }else{
62110                 this.el.setLocation(-20000,-20000);
62111                 this.el.hide();
62112                 this.collapsedEl.show();
62113                 this.fireEvent("collapsed", this);
62114                 this.fireEvent("invalidated", this);
62115             }
62116         }
62117         
62118     },
62119
62120     animateCollapse : function(){
62121         // overridden
62122     },
62123
62124     /**
62125      * Expands this region if it was previously collapsed.
62126      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
62127      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
62128      */
62129     expand : function(e, skipAnim){
62130         if(e) {
62131             e.stopPropagation();
62132         }
62133         if(!this.collapsed || this.el.hasActiveFx()) {
62134             return;
62135         }
62136         if(this.isSlid){
62137             this.afterSlideIn();
62138             skipAnim = true;
62139         }
62140         this.collapsed = false;
62141         if(this.config.animate && skipAnim !== true){
62142             this.animateExpand();
62143         }else{
62144             this.el.show();
62145             if(this.split){
62146                 this.split.el.show();
62147             }
62148             this.collapsedEl.setLocation(-2000,-2000);
62149             this.collapsedEl.hide();
62150             this.fireEvent("invalidated", this);
62151             this.fireEvent("expanded", this);
62152         }
62153     },
62154
62155     animateExpand : function(){
62156         // overridden
62157     },
62158
62159     initTabs : function()
62160     {
62161         this.bodyEl.setStyle("overflow", "hidden");
62162         var ts = new Roo.panel.Tab(
62163                 this.bodyEl.dom,
62164                 {
62165                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
62166                     disableTooltips: this.config.disableTabTips,
62167                     toolbar : this.config.toolbar
62168                 }
62169         );
62170         if(this.config.hideTabs){
62171             ts.stripWrap.setDisplayed(false);
62172         }
62173         this.tabs = ts;
62174         ts.resizeTabs = this.config.resizeTabs === true;
62175         ts.minTabWidth = this.config.minTabWidth || 40;
62176         ts.maxTabWidth = this.config.maxTabWidth || 250;
62177         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
62178         ts.monitorResize = false;
62179         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
62180         ts.bodyEl.addClass('x-layout-tabs-body');
62181         this.panels.each(this.initPanelAsTab, this);
62182     },
62183
62184     initPanelAsTab : function(panel){
62185         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
62186                     this.config.closeOnTab && panel.isClosable());
62187         if(panel.tabTip !== undefined){
62188             ti.setTooltip(panel.tabTip);
62189         }
62190         ti.on("activate", function(){
62191               this.setActivePanel(panel);
62192         }, this);
62193         if(this.config.closeOnTab){
62194             ti.on("beforeclose", function(t, e){
62195                 e.cancel = true;
62196                 this.remove(panel);
62197             }, this);
62198         }
62199         return ti;
62200     },
62201
62202     updatePanelTitle : function(panel, title){
62203         if(this.activePanel == panel){
62204             this.updateTitle(title);
62205         }
62206         if(this.tabs){
62207             var ti = this.tabs.getTab(panel.getEl().id);
62208             ti.setText(title);
62209             if(panel.tabTip !== undefined){
62210                 ti.setTooltip(panel.tabTip);
62211             }
62212         }
62213     },
62214
62215     updateTitle : function(title){
62216         if(this.titleTextEl && !this.config.title){
62217             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
62218         }
62219     },
62220
62221     setActivePanel : function(panel){
62222         panel = this.getPanel(panel);
62223         if(this.activePanel && this.activePanel != panel){
62224             this.activePanel.setActiveState(false);
62225         }
62226         this.activePanel = panel;
62227         panel.setActiveState(true);
62228         if(this.panelSize){
62229             panel.setSize(this.panelSize.width, this.panelSize.height);
62230         }
62231         if(this.closeBtn){
62232             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
62233         }
62234         this.updateTitle(panel.getTitle());
62235         if(this.tabs){
62236             this.fireEvent("invalidated", this);
62237         }
62238         this.fireEvent("panelactivated", this, panel);
62239     },
62240
62241     /**
62242      * Shows the specified panel.
62243      * @param {Number/String/panel.Content} panelId The panel's index, id or the panel itself
62244      * @return {Roo.panel.Content} The shown panel, or null if a panel could not be found from panelId
62245      */
62246     showPanel : function(panel)
62247     {
62248         panel = this.getPanel(panel);
62249         if(panel){
62250             if(this.tabs){
62251                 var tab = this.tabs.getTab(panel.getEl().id);
62252                 if(tab.isHidden()){
62253                     this.tabs.unhideTab(tab.id);
62254                 }
62255                 tab.activate();
62256             }else{
62257                 this.setActivePanel(panel);
62258             }
62259         }
62260         return panel;
62261     },
62262
62263     /**
62264      * Get the active panel for this region.
62265      * @return {Roo.panel.Content} The active panel or null
62266      */
62267     getActivePanel : function(){
62268         return this.activePanel;
62269     },
62270
62271     validateVisibility : function(){
62272         if(this.panels.getCount() < 1){
62273             this.updateTitle("&#160;");
62274             this.closeBtn.hide();
62275             this.hide();
62276         }else{
62277             if(!this.isVisible()){
62278                 this.show();
62279             }
62280         }
62281     },
62282
62283     /**
62284      * Adds the passed ContentPanel(s) to this region.
62285      * @param {panel.Content...} panel The ContentPanel(s) to add (you can pass more than one)
62286      * @return {Roo.panel.Content} The panel added (if only one was added; null otherwise)
62287      */
62288     add : function(panel){
62289         if(arguments.length > 1){
62290             for(var i = 0, len = arguments.length; i < len; i++) {
62291                 this.add(arguments[i]);
62292             }
62293             return null;
62294         }
62295         if(this.hasPanel(panel)){
62296             this.showPanel(panel);
62297             return panel;
62298         }
62299         panel.setRegion(this);
62300         this.panels.add(panel);
62301         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
62302             this.bodyEl.dom.appendChild(panel.getEl().dom);
62303             if(panel.background !== true){
62304                 this.setActivePanel(panel);
62305             }
62306             this.fireEvent("paneladded", this, panel);
62307             return panel;
62308         }
62309         if(!this.tabs){
62310             this.initTabs();
62311         }else{
62312             this.initPanelAsTab(panel);
62313         }
62314         if(panel.background !== true){
62315             this.tabs.activate(panel.getEl().id);
62316         }
62317         this.fireEvent("paneladded", this, panel);
62318         return panel;
62319     },
62320
62321     /**
62322      * Hides the tab for the specified panel.
62323      * @param {Number/String/panel.Content} panel The panel's index, id or the panel itself
62324      */
62325     hidePanel : function(panel){
62326         if(this.tabs && (panel = this.getPanel(panel))){
62327             this.tabs.hideTab(panel.getEl().id);
62328         }
62329     },
62330
62331     /**
62332      * Unhides the tab for a previously hidden panel.
62333      * @param {Number/String/panel.Content} panel The panel's index, id or the panel itself
62334      */
62335     unhidePanel : function(panel){
62336         if(this.tabs && (panel = this.getPanel(panel))){
62337             this.tabs.unhideTab(panel.getEl().id);
62338         }
62339     },
62340
62341     clearPanels : function(){
62342         while(this.panels.getCount() > 0){
62343              this.remove(this.panels.first());
62344         }
62345     },
62346
62347     /**
62348      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
62349      * @param {Number/String/panel.Content} panel The panel's index, id or the panel itself
62350      * @param {Boolean} preservePanel Overrides the config preservePanel option
62351      * @return {Roo.panel.Content} The panel that was removed
62352      */
62353     remove : function(panel, preservePanel){
62354         panel = this.getPanel(panel);
62355         if(!panel){
62356             return null;
62357         }
62358         var e = {};
62359         this.fireEvent("beforeremove", this, panel, e);
62360         if(e.cancel === true){
62361             return null;
62362         }
62363         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
62364         var panelId = panel.getId();
62365         this.panels.removeKey(panelId);
62366         if(preservePanel){
62367             document.body.appendChild(panel.getEl().dom);
62368         }
62369         if(this.tabs){
62370             this.tabs.removeTab(panel.getEl().id);
62371         }else if (!preservePanel){
62372             this.bodyEl.dom.removeChild(panel.getEl().dom);
62373         }
62374         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
62375             var p = this.panels.first();
62376             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
62377             tempEl.appendChild(p.getEl().dom);
62378             this.bodyEl.update("");
62379             this.bodyEl.dom.appendChild(p.getEl().dom);
62380             tempEl = null;
62381             this.updateTitle(p.getTitle());
62382             this.tabs = null;
62383             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
62384             this.setActivePanel(p);
62385         }
62386         panel.setRegion(null);
62387         if(this.activePanel == panel){
62388             this.activePanel = null;
62389         }
62390         if(this.config.autoDestroy !== false && preservePanel !== true){
62391             try{panel.destroy();}catch(e){}
62392         }
62393         this.fireEvent("panelremoved", this, panel);
62394         return panel;
62395     },
62396
62397     /**
62398      * Returns the TabPanel component used by this region
62399      * @return {Roo.panel.Tab}
62400      */
62401     getTabs : function(){
62402         return this.tabs;
62403     },
62404
62405     createTool : function(parentEl, className){
62406         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
62407             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
62408         btn.addClassOnOver("x-layout-tools-button-over");
62409         return btn;
62410     }
62411 });/*
62412  * Based on:
62413  * Ext JS Library 1.1.1
62414  * Copyright(c) 2006-2007, Ext JS, LLC.
62415  *
62416  * Originally Released Under LGPL - original licence link has changed is not relivant.
62417  *
62418  * Fork - LGPL
62419  * <script type="text/javascript">
62420  */
62421  
62422
62423
62424 /**
62425  * @class Roo.layout.SplitRegion
62426  * @extends Roo.layout.Region
62427  * Adds a splitbar and other (private) useful functionality to a {@link Roo.layout.Region}.
62428  */
62429 Roo.layout.SplitRegion = function(mgr, config, pos, cursor){
62430     this.cursor = cursor;
62431     Roo.layout.SplitRegion.superclass.constructor.call(this, mgr, config, pos);
62432 };
62433
62434 Roo.extend(Roo.layout.SplitRegion, Roo.layout.Region, {
62435     splitTip : "Drag to resize.",
62436     collapsibleSplitTip : "Drag to resize. Double click to hide.",
62437     useSplitTips : false,
62438
62439     applyConfig : function(config){
62440         Roo.layout.SplitRegion.superclass.applyConfig.call(this, config);
62441         if(config.split){
62442             if(!this.split){
62443                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
62444                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
62445                 /** The SplitBar for this region 
62446                 * @type Roo.SplitBar */
62447                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
62448                 this.split.on("moved", this.onSplitMove, this);
62449                 this.split.useShim = config.useShim === true;
62450                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
62451                 if(this.useSplitTips){
62452                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
62453                 }
62454                 if(config.collapsible){
62455                     this.split.el.on("dblclick", this.collapse,  this);
62456                 }
62457             }
62458             if(typeof config.minSize != "undefined"){
62459                 this.split.minSize = config.minSize;
62460             }
62461             if(typeof config.maxSize != "undefined"){
62462                 this.split.maxSize = config.maxSize;
62463             }
62464             if(config.hideWhenEmpty || config.hidden || config.collapsed){
62465                 this.hideSplitter();
62466             }
62467         }
62468     },
62469
62470     getHMaxSize : function(){
62471          var cmax = this.config.maxSize || 10000;
62472          var center = this.mgr.getRegion("center");
62473          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
62474     },
62475
62476     getVMaxSize : function(){
62477          var cmax = this.config.maxSize || 10000;
62478          var center = this.mgr.getRegion("center");
62479          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
62480     },
62481
62482     onSplitMove : function(split, newSize){
62483         this.fireEvent("resized", this, newSize);
62484     },
62485     
62486     /** 
62487      * Returns the {@link Roo.SplitBar} for this region.
62488      * @return {Roo.SplitBar}
62489      */
62490     getSplitBar : function(){
62491         return this.split;
62492     },
62493     
62494     hide : function(){
62495         this.hideSplitter();
62496         Roo.layout.SplitRegion.superclass.hide.call(this);
62497     },
62498
62499     hideSplitter : function(){
62500         if(this.split){
62501             this.split.el.setLocation(-2000,-2000);
62502             this.split.el.hide();
62503         }
62504     },
62505
62506     show : function(){
62507         if(this.split){
62508             this.split.el.show();
62509         }
62510         Roo.layout.SplitRegion.superclass.show.call(this);
62511     },
62512     
62513     beforeSlide: function(){
62514         if(Roo.isGecko){// firefox overflow auto bug workaround
62515             this.bodyEl.clip();
62516             if(this.tabs) {
62517                 this.tabs.bodyEl.clip();
62518             }
62519             if(this.activePanel){
62520                 this.activePanel.getEl().clip();
62521                 
62522                 if(this.activePanel.beforeSlide){
62523                     this.activePanel.beforeSlide();
62524                 }
62525             }
62526         }
62527     },
62528     
62529     afterSlide : function(){
62530         if(Roo.isGecko){// firefox overflow auto bug workaround
62531             this.bodyEl.unclip();
62532             if(this.tabs) {
62533                 this.tabs.bodyEl.unclip();
62534             }
62535             if(this.activePanel){
62536                 this.activePanel.getEl().unclip();
62537                 if(this.activePanel.afterSlide){
62538                     this.activePanel.afterSlide();
62539                 }
62540             }
62541         }
62542     },
62543
62544     initAutoHide : function(){
62545         if(this.autoHide !== false){
62546             if(!this.autoHideHd){
62547                 var st = new Roo.util.DelayedTask(this.slideIn, this);
62548                 this.autoHideHd = {
62549                     "mouseout": function(e){
62550                         if(!e.within(this.el, true)){
62551                             st.delay(500);
62552                         }
62553                     },
62554                     "mouseover" : function(e){
62555                         st.cancel();
62556                     },
62557                     scope : this
62558                 };
62559             }
62560             this.el.on(this.autoHideHd);
62561         }
62562     },
62563
62564     clearAutoHide : function(){
62565         if(this.autoHide !== false){
62566             this.el.un("mouseout", this.autoHideHd.mouseout);
62567             this.el.un("mouseover", this.autoHideHd.mouseover);
62568         }
62569     },
62570
62571     clearMonitor : function(){
62572         Roo.get(document).un("click", this.slideInIf, this);
62573     },
62574
62575     // these names are backwards but not changed for compat
62576     slideOut : function(){
62577         if(this.isSlid || this.el.hasActiveFx()){
62578             return;
62579         }
62580         this.isSlid = true;
62581         if(this.collapseBtn){
62582             this.collapseBtn.hide();
62583         }
62584         this.closeBtnState = this.closeBtn.getStyle('display');
62585         this.closeBtn.hide();
62586         if(this.stickBtn){
62587             this.stickBtn.show();
62588         }
62589         this.el.show();
62590         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
62591         this.beforeSlide();
62592         this.el.setStyle("z-index", 10001);
62593         this.el.slideIn(this.getSlideAnchor(), {
62594             callback: function(){
62595                 this.afterSlide();
62596                 this.initAutoHide();
62597                 Roo.get(document).on("click", this.slideInIf, this);
62598                 this.fireEvent("slideshow", this);
62599             },
62600             scope: this,
62601             block: true
62602         });
62603     },
62604
62605     afterSlideIn : function(){
62606         this.clearAutoHide();
62607         this.isSlid = false;
62608         this.clearMonitor();
62609         this.el.setStyle("z-index", "");
62610         if(this.collapseBtn){
62611             this.collapseBtn.show();
62612         }
62613         this.closeBtn.setStyle('display', this.closeBtnState);
62614         if(this.stickBtn){
62615             this.stickBtn.hide();
62616         }
62617         this.fireEvent("slidehide", this);
62618     },
62619
62620     slideIn : function(cb){
62621         if(!this.isSlid || this.el.hasActiveFx()){
62622             Roo.callback(cb);
62623             return;
62624         }
62625         this.isSlid = false;
62626         this.beforeSlide();
62627         this.el.slideOut(this.getSlideAnchor(), {
62628             callback: function(){
62629                 this.el.setLeftTop(-10000, -10000);
62630                 this.afterSlide();
62631                 this.afterSlideIn();
62632                 Roo.callback(cb);
62633             },
62634             scope: this,
62635             block: true
62636         });
62637     },
62638     
62639     slideInIf : function(e){
62640         if(!e.within(this.el)){
62641             this.slideIn();
62642         }
62643     },
62644
62645     animateCollapse : function(){
62646         this.beforeSlide();
62647         this.el.setStyle("z-index", 20000);
62648         var anchor = this.getSlideAnchor();
62649         this.el.slideOut(anchor, {
62650             callback : function(){
62651                 this.el.setStyle("z-index", "");
62652                 this.collapsedEl.slideIn(anchor, {duration:.3});
62653                 this.afterSlide();
62654                 this.el.setLocation(-10000,-10000);
62655                 this.el.hide();
62656                 this.fireEvent("collapsed", this);
62657             },
62658             scope: this,
62659             block: true
62660         });
62661     },
62662
62663     animateExpand : function(){
62664         this.beforeSlide();
62665         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
62666         this.el.setStyle("z-index", 20000);
62667         this.collapsedEl.hide({
62668             duration:.1
62669         });
62670         this.el.slideIn(this.getSlideAnchor(), {
62671             callback : function(){
62672                 this.el.setStyle("z-index", "");
62673                 this.afterSlide();
62674                 if(this.split){
62675                     this.split.el.show();
62676                 }
62677                 this.fireEvent("invalidated", this);
62678                 this.fireEvent("expanded", this);
62679             },
62680             scope: this,
62681             block: true
62682         });
62683     },
62684
62685     anchors : {
62686         "west" : "left",
62687         "east" : "right",
62688         "north" : "top",
62689         "south" : "bottom"
62690     },
62691
62692     sanchors : {
62693         "west" : "l",
62694         "east" : "r",
62695         "north" : "t",
62696         "south" : "b"
62697     },
62698
62699     canchors : {
62700         "west" : "tl-tr",
62701         "east" : "tr-tl",
62702         "north" : "tl-bl",
62703         "south" : "bl-tl"
62704     },
62705
62706     getAnchor : function(){
62707         return this.anchors[this.position];
62708     },
62709
62710     getCollapseAnchor : function(){
62711         return this.canchors[this.position];
62712     },
62713
62714     getSlideAnchor : function(){
62715         return this.sanchors[this.position];
62716     },
62717
62718     getAlignAdj : function(){
62719         var cm = this.cmargins;
62720         switch(this.position){
62721             case "west":
62722                 return [0, 0];
62723             break;
62724             case "east":
62725                 return [0, 0];
62726             break;
62727             case "north":
62728                 return [0, 0];
62729             break;
62730             case "south":
62731                 return [0, 0];
62732             break;
62733         }
62734     },
62735
62736     getExpandAdj : function(){
62737         var c = this.collapsedEl, cm = this.cmargins;
62738         switch(this.position){
62739             case "west":
62740                 return [-(cm.right+c.getWidth()+cm.left), 0];
62741             break;
62742             case "east":
62743                 return [cm.right+c.getWidth()+cm.left, 0];
62744             break;
62745             case "north":
62746                 return [0, -(cm.top+cm.bottom+c.getHeight())];
62747             break;
62748             case "south":
62749                 return [0, cm.top+cm.bottom+c.getHeight()];
62750             break;
62751         }
62752     }
62753 });/*
62754  * Based on:
62755  * Ext JS Library 1.1.1
62756  * Copyright(c) 2006-2007, Ext JS, LLC.
62757  *
62758  * Originally Released Under LGPL - original licence link has changed is not relivant.
62759  *
62760  * Fork - LGPL
62761  * <script type="text/javascript">
62762  */
62763 /*
62764  * These classes are private internal classes
62765  */
62766 Roo.layout.Center = function(mgr, config){
62767     Roo.layout.Region.call(this, mgr, config, "center");
62768     this.visible = true;
62769     this.minWidth = config.minWidth || 20;
62770     this.minHeight = config.minHeight || 20;
62771 };
62772
62773 Roo.extend(Roo.layout.Center, Roo.layout.Region, {
62774     hide : function(){
62775         // center panel can't be hidden
62776     },
62777     
62778     show : function(){
62779         // center panel can't be hidden
62780     },
62781     
62782     getMinWidth: function(){
62783         return this.minWidth;
62784     },
62785     
62786     getMinHeight: function(){
62787         return this.minHeight;
62788     }
62789 });
62790 Roo.layout.West = function(mgr, config){
62791     Roo.layout.SplitRegion.call(this, mgr, config, "west", "w-resize");
62792     if(this.split){
62793         this.split.placement = Roo.SplitBar.LEFT;
62794         this.split.orientation = Roo.SplitBar.HORIZONTAL;
62795         this.split.el.addClass("x-layout-split-h");
62796     }
62797     var size = config.initialSize || config.width;
62798     if(typeof size != "undefined"){
62799         this.el.setWidth(size);
62800     }
62801 };
62802 Roo.extend(Roo.layout.West, Roo.layout.SplitRegion, {
62803     orientation: Roo.SplitBar.HORIZONTAL,
62804     getBox : function(){
62805         if(this.collapsed){
62806             return this.collapsedEl.getBox();
62807         }
62808         var box = this.el.getBox();
62809         if(this.split){
62810             box.width += this.split.el.getWidth();
62811         }
62812         return box;
62813     },
62814     
62815     updateBox : function(box){
62816         if(this.split && !this.collapsed){
62817             var sw = this.split.el.getWidth();
62818             box.width -= sw;
62819             this.split.el.setLeft(box.x+box.width);
62820             this.split.el.setTop(box.y);
62821             this.split.el.setHeight(box.height);
62822         }
62823         if(this.collapsed){
62824             this.updateBody(null, box.height);
62825         }
62826         Roo.layout.Region.prototype.updateBox.call(this, box);
62827     }
62828 });
62829 Roo.layout.East = function(mgr, config){
62830     Roo.layout.SplitRegion.call(this, mgr, config, "east", "e-resize");
62831     if(this.split){
62832         this.split.placement = Roo.SplitBar.RIGHT;
62833         this.split.orientation = Roo.SplitBar.HORIZONTAL;
62834         this.split.el.addClass("x-layout-split-h");
62835     }
62836     var size = config.initialSize || config.width;
62837     if(typeof size != "undefined"){
62838         this.el.setWidth(size);
62839     }
62840 };
62841 Roo.extend(Roo.layout.East, Roo.layout.SplitRegion, {
62842     orientation: Roo.SplitBar.HORIZONTAL,
62843     getBox : function(){
62844         if(this.collapsed){
62845             return this.collapsedEl.getBox();
62846         }
62847         var box = this.el.getBox();
62848         if(this.split){
62849             var sw = this.split.el.getWidth();
62850             box.width += sw;
62851             box.x -= sw;
62852         }
62853         return box;
62854     },
62855
62856     updateBox : function(box){
62857         if(this.split && !this.collapsed){
62858             var sw = this.split.el.getWidth();
62859             box.width -= sw;
62860             this.split.el.setLeft(box.x);
62861             this.split.el.setTop(box.y);
62862             this.split.el.setHeight(box.height);
62863             box.x += sw;
62864         }
62865         if(this.collapsed){
62866             this.updateBody(null, box.height);
62867         }
62868         Roo.layout.Region.prototype.updateBox.call(this, box);
62869     }
62870 });Roo.layout.South = function(mgr, config){
62871     Roo.layout.SplitRegion.call(this, mgr, config, "south", "s-resize");
62872     if(this.split){
62873         this.split.placement = Roo.SplitBar.BOTTOM;
62874         this.split.orientation = Roo.SplitBar.VERTICAL;
62875         this.split.el.addClass("x-layout-split-v");
62876     }
62877     var size = config.initialSize || config.height;
62878     if(typeof size != "undefined"){
62879         this.el.setHeight(size);
62880     }
62881 };
62882 Roo.extend(Roo.layout.South, Roo.layout.SplitRegion, {
62883     orientation: Roo.SplitBar.VERTICAL,
62884     getBox : function(){
62885         if(this.collapsed){
62886             return this.collapsedEl.getBox();
62887         }
62888         var box = this.el.getBox();
62889         if(this.split){
62890             var sh = this.split.el.getHeight();
62891             box.height += sh;
62892             box.y -= sh;
62893         }
62894         return box;
62895     },
62896     
62897     updateBox : function(box){
62898         if(this.split && !this.collapsed){
62899             var sh = this.split.el.getHeight();
62900             box.height -= sh;
62901             box.y += sh;
62902             this.split.el.setLeft(box.x);
62903             this.split.el.setTop(box.y-sh);
62904             this.split.el.setWidth(box.width);
62905         }
62906         if(this.collapsed){
62907             this.updateBody(box.width, null);
62908         }
62909         Roo.layout.Region.prototype.updateBox.call(this, box);
62910     }
62911 });
62912
62913
62914 Roo.layout.North = function(mgr, config){
62915     Roo.layout.Region.call(this, mgr, config, "north", "n-resize");
62916     if(this.split){
62917         this.split.placement = Roo.SplitBar.TOP;
62918         this.split.orientation = Roo.SplitBar.VERTICAL;
62919         this.split.el.addClass("x-layout-split-v");
62920     }
62921     var size = config.initialSize || config.height;
62922     if(typeof size != "undefined"){
62923         this.el.setHeight(size);
62924     }
62925 };
62926 Roo.extend(Roo.layout.North, Roo.layout.SplitRegion, {
62927     orientation: Roo.SplitBar.VERTICAL,
62928     getBox : function(){
62929         if(this.collapsed){
62930             return this.collapsedEl.getBox();
62931         }
62932         var box = this.el.getBox();
62933         if(this.split){
62934             box.height += this.split.el.getHeight();
62935         }
62936         return box;
62937     },
62938     
62939     updateBox : function(box){
62940         if(this.split && !this.collapsed){
62941             box.height -= this.split.el.getHeight();
62942             this.split.el.setLeft(box.x);
62943             this.split.el.setTop(box.y+box.height);
62944             this.split.el.setWidth(box.width);
62945         }
62946         if(this.collapsed){
62947             this.updateBody(box.width, null);
62948         }
62949         Roo.layout.Region.prototype.updateBox.call(this, box);
62950     }
62951 });/*
62952  * Based on:
62953  * Ext JS Library 1.1.1
62954  * Copyright(c) 2006-2007, Ext JS, LLC.
62955  *
62956  * Originally Released Under LGPL - original licence link has changed is not relivant.
62957  *
62958  * Fork - LGPL
62959  * <script type="text/javascript">
62960  */
62961  
62962  
62963 /*
62964  * Private internal class for reading and applying state
62965  */
62966 Roo.layout.StateManager = function(layout){
62967      // default empty state
62968      this.state = {
62969         north: {},
62970         south: {},
62971         east: {},
62972         west: {}       
62973     };
62974 };
62975
62976 Roo.layout.StateManager.prototype = {
62977     init : function(layout, provider){
62978         this.provider = provider;
62979         var state = provider.get(layout.id+"-layout-state");
62980         if(state){
62981             var wasUpdating = layout.isUpdating();
62982             if(!wasUpdating){
62983                 layout.beginUpdate();
62984             }
62985             for(var key in state){
62986                 if(typeof state[key] != "function"){
62987                     var rstate = state[key];
62988                     var r = layout.getRegion(key);
62989                     if(r && rstate){
62990                         if(rstate.size){
62991                             r.resizeTo(rstate.size);
62992                         }
62993                         if(rstate.collapsed == true){
62994                             r.collapse(true);
62995                         }else{
62996                             r.expand(null, true);
62997                         }
62998                     }
62999                 }
63000             }
63001             if(!wasUpdating){
63002                 layout.endUpdate();
63003             }
63004             this.state = state; 
63005         }
63006         this.layout = layout;
63007         layout.on("regionresized", this.onRegionResized, this);
63008         layout.on("regioncollapsed", this.onRegionCollapsed, this);
63009         layout.on("regionexpanded", this.onRegionExpanded, this);
63010     },
63011     
63012     storeState : function(){
63013         this.provider.set(this.layout.id+"-layout-state", this.state);
63014     },
63015     
63016     onRegionResized : function(region, newSize){
63017         this.state[region.getPosition()].size = newSize;
63018         this.storeState();
63019     },
63020     
63021     onRegionCollapsed : function(region){
63022         this.state[region.getPosition()].collapsed = true;
63023         this.storeState();
63024     },
63025     
63026     onRegionExpanded : function(region){
63027         this.state[region.getPosition()].collapsed = false;
63028         this.storeState();
63029     }
63030 };/*
63031  * Based on:
63032  * Ext JS Library 1.1.1
63033  * Copyright(c) 2006-2007, Ext JS, LLC.
63034  *
63035  * Originally Released Under LGPL - original licence link has changed is not relivant.
63036  *
63037  * Fork - LGPL
63038  * <script type="text/javascript">
63039  */
63040 /**
63041  * @class Roo.panel.Content
63042  * @extends Roo.util.Observable
63043  * @children Roo.form.Form Roo.JsonView Roo.View
63044  * @parent Roo.layout.Border Roo.LayoutDialog builder
63045  * A basic Content Panel element.
63046  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
63047  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
63048  * @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
63049  * @cfg {Boolean}   closable      True if the panel can be closed/removed
63050  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
63051  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
63052  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
63053  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
63054  * @cfg {String} title          The title for this panel
63055  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
63056  * @cfg {String} url            Calls {@link #setUrl} with this value
63057  * @cfg {String} region (center|north|south|east|west) [required] which region to put this panel on (when used with xtype constructors)
63058  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
63059  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
63060  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
63061  * @cfg {String}    style  Extra style to add to the content panel
63062  * @cfg {Roo.menu.Menu} menu  popup menu
63063
63064  * @constructor
63065  * Create a new Content Panel.
63066  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
63067  * @param {String/Object} config A string to set only the title or a config object
63068  * @param {String} content (optional) Set the HTML content for this panel
63069  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
63070  */
63071 Roo.panel.Content = function(el, config, content){
63072     
63073     /*
63074     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
63075         config = el;
63076         el = Roo.id();
63077     }
63078     if (config && config.parentLayout) { 
63079         el = config.parentLayout.el.createChild(); 
63080     }
63081     */
63082     if(el.autoCreate){ // xtype is available if this is called from factory
63083         config = el;
63084         el = Roo.id();
63085     }
63086     this.el = Roo.get(el);
63087     if(!this.el && config && config.autoCreate){
63088         if(typeof config.autoCreate == "object"){
63089             if(!config.autoCreate.id){
63090                 config.autoCreate.id = config.id||el;
63091             }
63092             this.el = Roo.DomHelper.append(document.body,
63093                         config.autoCreate, true);
63094         }else{
63095             this.el = Roo.DomHelper.append(document.body,
63096                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
63097         }
63098     }
63099     
63100     
63101     this.closable = false;
63102     this.loaded = false;
63103     this.active = false;
63104     if(typeof config == "string"){
63105         this.title = config;
63106     }else{
63107         Roo.apply(this, config);
63108     }
63109     
63110     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
63111         this.wrapEl = this.el.wrap();
63112         this.toolbar.container = this.el.insertSibling(false, 'before');
63113         this.toolbar = new Roo.Toolbar(this.toolbar);
63114     }
63115     
63116     // xtype created footer. - not sure if will work as we normally have to render first..
63117     if (this.footer && !this.footer.el && this.footer.xtype) {
63118         if (!this.wrapEl) {
63119             this.wrapEl = this.el.wrap();
63120         }
63121     
63122         this.footer.container = this.wrapEl.createChild();
63123          
63124         this.footer = Roo.factory(this.footer, Roo);
63125         
63126     }
63127     
63128     if(this.resizeEl){
63129         this.resizeEl = Roo.get(this.resizeEl, true);
63130     }else{
63131         this.resizeEl = this.el;
63132     }
63133     // handle view.xtype
63134     
63135  
63136     
63137     
63138     this.addEvents({
63139         /**
63140          * @event activate
63141          * Fires when this panel is activated. 
63142          * @param {Roo.panel.Content} this
63143          */
63144         "activate" : true,
63145         /**
63146          * @event deactivate
63147          * Fires when this panel is activated. 
63148          * @param {Roo.panel.Content} this
63149          */
63150         "deactivate" : true,
63151
63152         /**
63153          * @event resize
63154          * Fires when this panel is resized if fitToFrame is true.
63155          * @param {Roo.panel.Content} this
63156          * @param {Number} width The width after any component adjustments
63157          * @param {Number} height The height after any component adjustments
63158          */
63159         "resize" : true,
63160         
63161          /**
63162          * @event render
63163          * Fires when this tab is created
63164          * @param {Roo.panel.Content} this
63165          */
63166         "render" : true
63167          
63168         
63169     });
63170     
63171
63172     
63173     
63174     if(this.autoScroll){
63175         this.resizeEl.setStyle("overflow", "auto");
63176     } else {
63177         // fix randome scrolling
63178         this.el.on('scroll', function() {
63179             Roo.log('fix random scolling');
63180             this.scrollTo('top',0); 
63181         });
63182     }
63183     content = content || this.content;
63184     if(content){
63185         this.setContent(content);
63186     }
63187     if(config && config.url){
63188         this.setUrl(this.url, this.params, this.loadOnce);
63189     }
63190     
63191     
63192     
63193     Roo.panel.Content.superclass.constructor.call(this);
63194     
63195     if (this.view && typeof(this.view.xtype) != 'undefined') {
63196         this.view.el = this.el.appendChild(document.createElement("div"));
63197         this.view = Roo.factory(this.view); 
63198         this.view.render  &&  this.view.render(false, '');  
63199     }
63200     
63201     
63202     this.fireEvent('render', this);
63203 };
63204
63205 Roo.extend(Roo.panel.Content, Roo.util.Observable, {
63206     tabTip:'',
63207     setRegion : function(region){
63208         this.region = region;
63209         if(region){
63210            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
63211         }else{
63212            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
63213         } 
63214     },
63215     
63216     /**
63217      * Returns the toolbar for this Panel if one was configured. 
63218      * @return {Roo.Toolbar} 
63219      */
63220     getToolbar : function(){
63221         return this.toolbar;
63222     },
63223     
63224     setActiveState : function(active){
63225         this.active = active;
63226         if(!active){
63227             this.fireEvent("deactivate", this);
63228         }else{
63229             this.fireEvent("activate", this);
63230         }
63231     },
63232     /**
63233      * Updates this panel's element
63234      * @param {String} content The new content
63235      * @param {Boolean} loadScripts (optional) true to look for and process scripts
63236     */
63237     setContent : function(content, loadScripts){
63238         this.el.update(content, loadScripts);
63239     },
63240
63241     ignoreResize : function(w, h){
63242         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
63243             return true;
63244         }else{
63245             this.lastSize = {width: w, height: h};
63246             return false;
63247         }
63248     },
63249     /**
63250      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
63251      * @return {Roo.UpdateManager} The UpdateManager
63252      */
63253     getUpdateManager : function(){
63254         return this.el.getUpdateManager();
63255     },
63256      /**
63257      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
63258      * @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:
63259 <pre><code>
63260 panel.load({
63261     url: "your-url.php",
63262     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
63263     callback: yourFunction,
63264     scope: yourObject, //(optional scope)
63265     discardUrl: false,
63266     nocache: false,
63267     text: "Loading...",
63268     timeout: 30,
63269     scripts: false
63270 });
63271 </code></pre>
63272      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
63273      * 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.
63274      * @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}
63275      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
63276      * @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.
63277      * @return {Roo.panel.Content} this
63278      */
63279     load : function(){
63280         var um = this.el.getUpdateManager();
63281         um.update.apply(um, arguments);
63282         return this;
63283     },
63284
63285
63286     /**
63287      * 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.
63288      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
63289      * @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)
63290      * @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)
63291      * @return {Roo.UpdateManager} The UpdateManager
63292      */
63293     setUrl : function(url, params, loadOnce){
63294         if(this.refreshDelegate){
63295             this.removeListener("activate", this.refreshDelegate);
63296         }
63297         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
63298         this.on("activate", this.refreshDelegate);
63299         return this.el.getUpdateManager();
63300     },
63301     
63302     _handleRefresh : function(url, params, loadOnce){
63303         if(!loadOnce || !this.loaded){
63304             var updater = this.el.getUpdateManager();
63305             updater.update(url, params, this._setLoaded.createDelegate(this));
63306         }
63307     },
63308     
63309     _setLoaded : function(){
63310         this.loaded = true;
63311     }, 
63312     
63313     /**
63314      * Returns this panel's id
63315      * @return {String} 
63316      */
63317     getId : function(){
63318         return this.el.id;
63319     },
63320     
63321     /** 
63322      * Returns this panel's element - used by regiosn to add.
63323      * @return {Roo.Element} 
63324      */
63325     getEl : function(){
63326         return this.wrapEl || this.el;
63327     },
63328     
63329     adjustForComponents : function(width, height)
63330     {
63331         //Roo.log('adjustForComponents ');
63332         if(this.resizeEl != this.el){
63333             width -= this.el.getFrameWidth('lr');
63334             height -= this.el.getFrameWidth('tb');
63335         }
63336         if(this.toolbar){
63337             var te = this.toolbar.getEl();
63338             height -= te.getHeight();
63339             te.setWidth(width);
63340         }
63341         if(this.footer){
63342             var te = this.footer.getEl();
63343             //Roo.log("footer:" + te.getHeight());
63344             
63345             height -= te.getHeight();
63346             te.setWidth(width);
63347         }
63348         
63349         
63350         if(this.adjustments){
63351             width += this.adjustments[0];
63352             height += this.adjustments[1];
63353         }
63354         return {"width": width, "height": height};
63355     },
63356     
63357     setSize : function(width, height){
63358         if(this.fitToFrame && !this.ignoreResize(width, height)){
63359             if(this.fitContainer && this.resizeEl != this.el){
63360                 this.el.setSize(width, height);
63361             }
63362             var size = this.adjustForComponents(width, height);
63363             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
63364             this.fireEvent('resize', this, size.width, size.height);
63365         }
63366     },
63367     
63368     /**
63369      * Returns this panel's title
63370      * @return {String} 
63371      */
63372     getTitle : function(){
63373         return this.title;
63374     },
63375     
63376     /**
63377      * Set this panel's title
63378      * @param {String} title
63379      */
63380     setTitle : function(title){
63381         this.title = title;
63382         if(this.region){
63383             this.region.updatePanelTitle(this, title);
63384         }
63385     },
63386     
63387     /**
63388      * Returns true is this panel was configured to be closable
63389      * @return {Boolean} 
63390      */
63391     isClosable : function(){
63392         return this.closable;
63393     },
63394     
63395     beforeSlide : function(){
63396         this.el.clip();
63397         this.resizeEl.clip();
63398     },
63399     
63400     afterSlide : function(){
63401         this.el.unclip();
63402         this.resizeEl.unclip();
63403     },
63404     
63405     /**
63406      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
63407      *   Will fail silently if the {@link #setUrl} method has not been called.
63408      *   This does not activate the panel, just updates its content.
63409      */
63410     refresh : function(){
63411         if(this.refreshDelegate){
63412            this.loaded = false;
63413            this.refreshDelegate();
63414         }
63415     },
63416     
63417     /**
63418      * Destroys this panel
63419      */
63420     destroy : function(){
63421         this.el.removeAllListeners();
63422         var tempEl = document.createElement("span");
63423         tempEl.appendChild(this.el.dom);
63424         tempEl.innerHTML = "";
63425         this.el.remove();
63426         this.el = null;
63427     },
63428     
63429     /**
63430      * form - if the content panel contains a form - this is a reference to it.
63431      * @type {Roo.form.Form}
63432      */
63433     form : false,
63434     /**
63435      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
63436      *    This contains a reference to it.
63437      * @type {Roo.View}
63438      */
63439     view : false,
63440     
63441       /**
63442      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
63443      * <pre><code>
63444
63445 layout.addxtype({
63446        xtype : 'Form',
63447        items: [ .... ]
63448    }
63449 );
63450
63451 </code></pre>
63452      * @param {Object} cfg Xtype definition of item to add.
63453      */
63454     
63455     addxtype : function(cfg) {
63456         if(cfg.xtype.match(/^Cropbox$/)) {
63457
63458             this.cropbox = new Roo.factory(cfg);
63459
63460             this.cropbox.render(this.el);
63461
63462             return this.cropbox;
63463         }
63464         // add form..
63465         if (cfg.xtype.match(/^Form$/)) {
63466             
63467             var el;
63468             //if (this.footer) {
63469             //    el = this.footer.container.insertSibling(false, 'before');
63470             //} else {
63471                 el = this.el.createChild();
63472             //}
63473
63474             this.form = new  Roo.form.Form(cfg);
63475             
63476             
63477             if ( this.form.allItems.length) {
63478                 this.form.render(el.dom);
63479             }
63480             return this.form;
63481         }
63482         // should only have one of theses..
63483         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
63484             // views.. should not be just added - used named prop 'view''
63485             
63486             cfg.el = this.el.appendChild(document.createElement("div"));
63487             // factory?
63488             
63489             var ret = new Roo.factory(cfg);
63490              
63491              ret.render && ret.render(false, ''); // render blank..
63492             this.view = ret;
63493             return ret;
63494         }
63495         return false;
63496     }
63497 });
63498
63499
63500
63501
63502
63503
63504
63505
63506
63507
63508
63509
63510 /**
63511  * @class Roo.panel.Grid
63512  * @extends Roo.panel.Content
63513  * @parent Roo.layout.Border Roo.LayoutDialog builder
63514  * @constructor
63515  * Create a new GridPanel.
63516  * @cfg {Roo.grid.Grid} grid The grid for this panel
63517  */
63518 Roo.panel.Grid = function(grid, config){
63519     
63520     // universal ctor...
63521     if (typeof(grid.grid) != 'undefined') {
63522         config = grid;
63523         grid = config.grid;
63524     }
63525     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
63526         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
63527         
63528     this.wrapper.dom.appendChild(grid.getGridEl().dom);
63529     
63530     Roo.panel.Grid.superclass.constructor.call(this, this.wrapper, config);
63531     
63532     if(this.toolbar){
63533         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
63534     }
63535     // xtype created footer. - not sure if will work as we normally have to render first..
63536     if (this.footer && !this.footer.el && this.footer.xtype) {
63537         
63538         this.footer.container = this.grid.getView().getFooterPanel(true);
63539         this.footer.dataSource = this.grid.dataSource;
63540         this.footer = Roo.factory(this.footer, Roo);
63541         
63542     }
63543     
63544     grid.monitorWindowResize = false; // turn off autosizing
63545     grid.autoHeight = false;
63546     grid.autoWidth = false;
63547     this.grid = grid;
63548     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
63549 };
63550
63551 Roo.extend(Roo.panel.Grid, Roo.panel.Content, {
63552     getId : function(){
63553         return this.grid.id;
63554     },
63555     
63556     /**
63557      * Returns the grid for this panel
63558      * @return {Roo.grid.Grid} 
63559      */
63560     getGrid : function(){
63561         return this.grid;    
63562     },
63563     
63564     setSize : function(width, height){
63565         if(!this.ignoreResize(width, height)){
63566             var grid = this.grid;
63567             var size = this.adjustForComponents(width, height);
63568             grid.getGridEl().setSize(size.width, size.height);
63569             grid.autoSize();
63570         }
63571     },
63572     
63573     beforeSlide : function(){
63574         this.grid.getView().scroller.clip();
63575     },
63576     
63577     afterSlide : function(){
63578         this.grid.getView().scroller.unclip();
63579     },
63580     
63581     destroy : function(){
63582         this.grid.destroy();
63583         delete this.grid;
63584         Roo.panel.Grid.superclass.destroy.call(this); 
63585     }
63586 });
63587
63588
63589 /**
63590  * @class Roo.panel.NestedLayout
63591  * @extends Roo.panel.Content
63592  * @parent Roo.layout.Border Roo.LayoutDialog builder
63593  * @cfg {Roo.layout.Border} layout   [required] The layout for this panel
63594  *
63595  * 
63596  * @constructor
63597  * Create a new NestedLayoutPanel.
63598  * 
63599  * 
63600  * @param {Roo.layout.Border} layout [required] The layout for this panel
63601  * @param {String/Object} config A string to set only the title or a config object
63602  */
63603 Roo.panel.NestedLayout = function(layout, config)
63604 {
63605     // construct with only one argument..
63606     /* FIXME - implement nicer consturctors
63607     if (layout.layout) {
63608         config = layout;
63609         layout = config.layout;
63610         delete config.layout;
63611     }
63612     if (layout.xtype && !layout.getEl) {
63613         // then layout needs constructing..
63614         layout = Roo.factory(layout, Roo);
63615     }
63616     */
63617     
63618     
63619     Roo.panel.NestedLayout.superclass.constructor.call(this, layout.getEl(), config);
63620     
63621     layout.monitorWindowResize = false; // turn off autosizing
63622     this.layout = layout;
63623     this.layout.getEl().addClass("x-layout-nested-layout");
63624     
63625     
63626     
63627     
63628 };
63629
63630 Roo.extend(Roo.panel.NestedLayout, Roo.panel.Content, {
63631
63632     layout : false,
63633
63634     setSize : function(width, height){
63635         if(!this.ignoreResize(width, height)){
63636             var size = this.adjustForComponents(width, height);
63637             var el = this.layout.getEl();
63638             el.setSize(size.width, size.height);
63639             var touch = el.dom.offsetWidth;
63640             this.layout.layout();
63641             // ie requires a double layout on the first pass
63642             if(Roo.isIE && !this.initialized){
63643                 this.initialized = true;
63644                 this.layout.layout();
63645             }
63646         }
63647     },
63648     
63649     // activate all subpanels if not currently active..
63650     
63651     setActiveState : function(active){
63652         this.active = active;
63653         if(!active){
63654             this.fireEvent("deactivate", this);
63655             return;
63656         }
63657         
63658         this.fireEvent("activate", this);
63659         // not sure if this should happen before or after..
63660         if (!this.layout) {
63661             return; // should not happen..
63662         }
63663         var reg = false;
63664         for (var r in this.layout.regions) {
63665             reg = this.layout.getRegion(r);
63666             if (reg.getActivePanel()) {
63667                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
63668                 reg.setActivePanel(reg.getActivePanel());
63669                 continue;
63670             }
63671             if (!reg.panels.length) {
63672                 continue;
63673             }
63674             reg.showPanel(reg.getPanel(0));
63675         }
63676         
63677         
63678         
63679         
63680     },
63681     
63682     /**
63683      * Returns the nested BorderLayout for this panel
63684      * @return {Roo.layout.Border}
63685      */
63686     getLayout : function(){
63687         return this.layout;
63688     },
63689     
63690      /**
63691      * Adds a xtype elements to the layout of the nested panel
63692      * <pre><code>
63693
63694 panel.addxtype({
63695        xtype : 'ContentPanel',
63696        region: 'west',
63697        items: [ .... ]
63698    }
63699 );
63700
63701 panel.addxtype({
63702         xtype : 'panel.NestedLayout',
63703         region: 'west',
63704         layout: {
63705            center: { },
63706            west: { }   
63707         },
63708         items : [ ... list of content panels or nested layout panels.. ]
63709    }
63710 );
63711 </code></pre>
63712      * @param {Object} cfg Xtype definition of item to add.
63713      */
63714     addxtype : function(cfg) {
63715         return this.layout.addxtype(cfg);
63716     
63717     }
63718 });
63719
63720 Roo.panel.Scroll = function(el, config, content){
63721     config = config || {};
63722     config.fitToFrame = true;
63723     Roo.panel.Scroll.superclass.constructor.call(this, el, config, content);
63724     
63725     this.el.dom.style.overflow = "hidden";
63726     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
63727     this.el.removeClass("x-layout-inactive-content");
63728     this.el.on("mousewheel", this.onWheel, this);
63729
63730     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
63731     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
63732     up.unselectable(); down.unselectable();
63733     up.on("click", this.scrollUp, this);
63734     down.on("click", this.scrollDown, this);
63735     up.addClassOnOver("x-scroller-btn-over");
63736     down.addClassOnOver("x-scroller-btn-over");
63737     up.addClassOnClick("x-scroller-btn-click");
63738     down.addClassOnClick("x-scroller-btn-click");
63739     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
63740
63741     this.resizeEl = this.el;
63742     this.el = wrap; this.up = up; this.down = down;
63743 };
63744
63745 Roo.extend(Roo.panel.Scroll, Roo.panel.Content, {
63746     increment : 100,
63747     wheelIncrement : 5,
63748     scrollUp : function(){
63749         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
63750     },
63751
63752     scrollDown : function(){
63753         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
63754     },
63755
63756     afterScroll : function(){
63757         var el = this.resizeEl;
63758         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
63759         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
63760         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
63761     },
63762
63763     setSize : function(){
63764         Roo.panel.Scroll.superclass.setSize.apply(this, arguments);
63765         this.afterScroll();
63766     },
63767
63768     onWheel : function(e){
63769         var d = e.getWheelDelta();
63770         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
63771         this.afterScroll();
63772         e.stopEvent();
63773     },
63774
63775     setContent : function(content, loadScripts){
63776         this.resizeEl.update(content, loadScripts);
63777     }
63778
63779 });
63780
63781
63782
63783 /**
63784  * @class Roo.panel.Tree
63785  * @extends Roo.panel.Content
63786  * @parent Roo.layout.Border Roo.LayoutDialog builder
63787  * Treepanel component
63788  * 
63789  * @constructor
63790  * Create a new TreePanel. - defaults to fit/scoll contents.
63791  * @param {String/Object} config A string to set only the panel's title, or a config object
63792  */
63793 Roo.panel.Tree = function(config){
63794     var el = config.el;
63795     var tree = config.tree;
63796     delete config.tree; 
63797     delete config.el; // hopefull!
63798     
63799     // wrapper for IE7 strict & safari scroll issue
63800     
63801     var treeEl = el.createChild();
63802     config.resizeEl = treeEl;
63803     
63804     
63805     
63806     Roo.panel.Tree.superclass.constructor.call(this, el, config);
63807  
63808  
63809     this.tree = new Roo.tree.TreePanel(treeEl , tree);
63810     //console.log(tree);
63811     this.on('activate', function()
63812     {
63813         if (this.tree.rendered) {
63814             return;
63815         }
63816         //console.log('render tree');
63817         this.tree.render();
63818     });
63819     // this should not be needed.. - it's actually the 'el' that resizes?
63820     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
63821     
63822     //this.on('resize',  function (cp, w, h) {
63823     //        this.tree.innerCt.setWidth(w);
63824     //        this.tree.innerCt.setHeight(h);
63825     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
63826     //});
63827
63828         
63829     
63830 };
63831
63832 Roo.extend(Roo.panel.Tree, Roo.panel.Content, {   
63833     fitToFrame : true,
63834     autoScroll : true,
63835     /*
63836      * @cfg {Roo.tree.panel.Tree} tree [required] The tree TreePanel, with config etc.
63837      */
63838     tree : false
63839
63840 });
63841 /*
63842  * Based on:
63843  * Ext JS Library 1.1.1
63844  * Copyright(c) 2006-2007, Ext JS, LLC.
63845  *
63846  * Originally Released Under LGPL - original licence link has changed is not relivant.
63847  *
63848  * Fork - LGPL
63849  * <script type="text/javascript">
63850  */
63851  
63852
63853 /**
63854  * @class Roo.layout.Reader
63855  * @extends Roo.layout.Border
63856  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
63857  * center region containing two nested regions (a top one for a list view and one for item preview below),
63858  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
63859  * The setup and configuration work exactly the same as it does for a {@link Roo.layout.Border} - this class simply
63860  * expedites the setup of the overall layout and regions for this common application style.
63861  * Example:
63862  <pre><code>
63863 var reader = new Roo.layout.Reader();
63864 var CP = Roo.panel.Content;  // shortcut for adding
63865
63866 reader.beginUpdate();
63867 reader.add("north", new CP("north", "North"));
63868 reader.add("west", new CP("west", {title: "West"}));
63869 reader.add("east", new CP("east", {title: "East"}));
63870
63871 reader.regions.listView.add(new CP("listView", "List"));
63872 reader.regions.preview.add(new CP("preview", "Preview"));
63873 reader.endUpdate();
63874 </code></pre>
63875 * @constructor
63876 * Create a new ReaderLayout
63877 * @param {Object} config Configuration options
63878 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
63879 * document.body if omitted)
63880 */
63881 Roo.layout.Reader = function(config, renderTo){
63882     var c = config || {size:{}};
63883     Roo.layout.Reader.superclass.constructor.call(this, renderTo || document.body, {
63884         north: c.north !== false ? Roo.apply({
63885             split:false,
63886             initialSize: 32,
63887             titlebar: false
63888         }, c.north) : false,
63889         west: c.west !== false ? Roo.apply({
63890             split:true,
63891             initialSize: 200,
63892             minSize: 175,
63893             maxSize: 400,
63894             titlebar: true,
63895             collapsible: true,
63896             animate: true,
63897             margins:{left:5,right:0,bottom:5,top:5},
63898             cmargins:{left:5,right:5,bottom:5,top:5}
63899         }, c.west) : false,
63900         east: c.east !== false ? Roo.apply({
63901             split:true,
63902             initialSize: 200,
63903             minSize: 175,
63904             maxSize: 400,
63905             titlebar: true,
63906             collapsible: true,
63907             animate: true,
63908             margins:{left:0,right:5,bottom:5,top:5},
63909             cmargins:{left:5,right:5,bottom:5,top:5}
63910         }, c.east) : false,
63911         center: Roo.apply({
63912             tabPosition: 'top',
63913             autoScroll:false,
63914             closeOnTab: true,
63915             titlebar:false,
63916             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
63917         }, c.center)
63918     });
63919
63920     this.el.addClass('x-reader');
63921
63922     this.beginUpdate();
63923
63924     var inner = new Roo.layout.Border(Roo.get(document.body).createChild(), {
63925         south: c.preview !== false ? Roo.apply({
63926             split:true,
63927             initialSize: 200,
63928             minSize: 100,
63929             autoScroll:true,
63930             collapsible:true,
63931             titlebar: true,
63932             cmargins:{top:5,left:0, right:0, bottom:0}
63933         }, c.preview) : false,
63934         center: Roo.apply({
63935             autoScroll:false,
63936             titlebar:false,
63937             minHeight:200
63938         }, c.listView)
63939     });
63940     this.add('center', new Roo.panel.NestedLayout(inner,
63941             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
63942
63943     this.endUpdate();
63944
63945     this.regions.preview = inner.getRegion('south');
63946     this.regions.listView = inner.getRegion('center');
63947 };
63948
63949 Roo.extend(Roo.layout.Reader, Roo.layout.Border);/*
63950  * Based on:
63951  * Ext JS Library 1.1.1
63952  * Copyright(c) 2006-2007, Ext JS, LLC.
63953  *
63954  * Originally Released Under LGPL - original licence link has changed is not relivant.
63955  *
63956  * Fork - LGPL
63957  * <script type="text/javascript">
63958  */
63959  
63960 /**
63961  * @class Roo.grid.Grid
63962  * @extends Roo.util.Observable
63963  * This class represents the primary interface of a component based grid control.
63964  * <br><br>Usage:<pre><code>
63965  var grid = new Roo.grid.Grid("my-container-id", {
63966      ds: myDataStore,
63967      cm: myColModel,
63968      selModel: mySelectionModel,
63969      autoSizeColumns: true,
63970      monitorWindowResize: false,
63971      trackMouseOver: true
63972  });
63973  // set any options
63974  grid.render();
63975  * </code></pre>
63976  * <b>Common Problems:</b><br/>
63977  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
63978  * element will correct this<br/>
63979  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
63980  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
63981  * are unpredictable.<br/>
63982  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
63983  * grid to calculate dimensions/offsets.<br/>
63984   * @constructor
63985  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
63986  * The container MUST have some type of size defined for the grid to fill. The container will be
63987  * automatically set to position relative if it isn't already.
63988  * @param {Object} config A config object that sets properties on this grid.
63989  */
63990 Roo.grid.Grid = function(container, config){
63991         // initialize the container
63992         this.container = Roo.get(container);
63993         this.container.update("");
63994         this.container.setStyle("overflow", "hidden");
63995     this.container.addClass('x-grid-container');
63996
63997     this.id = this.container.id;
63998
63999     Roo.apply(this, config);
64000     // check and correct shorthanded configs
64001     if(this.ds){
64002         this.dataSource = this.ds;
64003         delete this.ds;
64004     }
64005     if(this.cm){
64006         this.colModel = this.cm;
64007         delete this.cm;
64008     }
64009     if(this.sm){
64010         this.selModel = this.sm;
64011         delete this.sm;
64012     }
64013
64014     if (this.selModel) {
64015         this.selModel = Roo.factory(this.selModel, Roo.grid);
64016         this.sm = this.selModel;
64017         this.sm.xmodule = this.xmodule || false;
64018     }
64019     if (typeof(this.colModel.config) == 'undefined') {
64020         this.colModel = new Roo.grid.ColumnModel(this.colModel);
64021         this.cm = this.colModel;
64022         this.cm.xmodule = this.xmodule || false;
64023     }
64024     if (this.dataSource) {
64025         this.dataSource= Roo.factory(this.dataSource, Roo.data);
64026         this.ds = this.dataSource;
64027         this.ds.xmodule = this.xmodule || false;
64028          
64029     }
64030     
64031     
64032     
64033     if(this.width){
64034         this.container.setWidth(this.width);
64035     }
64036
64037     if(this.height){
64038         this.container.setHeight(this.height);
64039     }
64040     /** @private */
64041         this.addEvents({
64042         // raw events
64043         /**
64044          * @event click
64045          * The raw click event for the entire grid.
64046          * @param {Roo.EventObject} e
64047          */
64048         "click" : true,
64049         /**
64050          * @event dblclick
64051          * The raw dblclick event for the entire grid.
64052          * @param {Roo.EventObject} e
64053          */
64054         "dblclick" : true,
64055         /**
64056          * @event contextmenu
64057          * The raw contextmenu event for the entire grid.
64058          * @param {Roo.EventObject} e
64059          */
64060         "contextmenu" : true,
64061         /**
64062          * @event mousedown
64063          * The raw mousedown event for the entire grid.
64064          * @param {Roo.EventObject} e
64065          */
64066         "mousedown" : true,
64067         /**
64068          * @event mouseup
64069          * The raw mouseup event for the entire grid.
64070          * @param {Roo.EventObject} e
64071          */
64072         "mouseup" : true,
64073         /**
64074          * @event mouseover
64075          * The raw mouseover event for the entire grid.
64076          * @param {Roo.EventObject} e
64077          */
64078         "mouseover" : true,
64079         /**
64080          * @event mouseout
64081          * The raw mouseout event for the entire grid.
64082          * @param {Roo.EventObject} e
64083          */
64084         "mouseout" : true,
64085         /**
64086          * @event keypress
64087          * The raw keypress event for the entire grid.
64088          * @param {Roo.EventObject} e
64089          */
64090         "keypress" : true,
64091         /**
64092          * @event keydown
64093          * The raw keydown event for the entire grid.
64094          * @param {Roo.EventObject} e
64095          */
64096         "keydown" : true,
64097
64098         // custom events
64099
64100         /**
64101          * @event cellclick
64102          * Fires when a cell is clicked
64103          * @param {Grid} this
64104          * @param {Number} rowIndex
64105          * @param {Number} columnIndex
64106          * @param {Roo.EventObject} e
64107          */
64108         "cellclick" : true,
64109         /**
64110          * @event celldblclick
64111          * Fires when a cell is double clicked
64112          * @param {Grid} this
64113          * @param {Number} rowIndex
64114          * @param {Number} columnIndex
64115          * @param {Roo.EventObject} e
64116          */
64117         "celldblclick" : true,
64118         /**
64119          * @event rowclick
64120          * Fires when a row is clicked
64121          * @param {Grid} this
64122          * @param {Number} rowIndex
64123          * @param {Roo.EventObject} e
64124          */
64125         "rowclick" : true,
64126         /**
64127          * @event rowdblclick
64128          * Fires when a row is double clicked
64129          * @param {Grid} this
64130          * @param {Number} rowIndex
64131          * @param {Roo.EventObject} e
64132          */
64133         "rowdblclick" : true,
64134         /**
64135          * @event headerclick
64136          * Fires when a header is clicked
64137          * @param {Grid} this
64138          * @param {Number} columnIndex
64139          * @param {Roo.EventObject} e
64140          */
64141         "headerclick" : true,
64142         /**
64143          * @event headerdblclick
64144          * Fires when a header cell is double clicked
64145          * @param {Grid} this
64146          * @param {Number} columnIndex
64147          * @param {Roo.EventObject} e
64148          */
64149         "headerdblclick" : true,
64150         /**
64151          * @event rowcontextmenu
64152          * Fires when a row is right clicked
64153          * @param {Grid} this
64154          * @param {Number} rowIndex
64155          * @param {Roo.EventObject} e
64156          */
64157         "rowcontextmenu" : true,
64158         /**
64159          * @event cellcontextmenu
64160          * Fires when a cell is right clicked
64161          * @param {Grid} this
64162          * @param {Number} rowIndex
64163          * @param {Number} cellIndex
64164          * @param {Roo.EventObject} e
64165          */
64166          "cellcontextmenu" : true,
64167         /**
64168          * @event headercontextmenu
64169          * Fires when a header is right clicked
64170          * @param {Grid} this
64171          * @param {Number} columnIndex
64172          * @param {Roo.EventObject} e
64173          */
64174         "headercontextmenu" : true,
64175         /**
64176          * @event bodyscroll
64177          * Fires when the body element is scrolled
64178          * @param {Number} scrollLeft
64179          * @param {Number} scrollTop
64180          */
64181         "bodyscroll" : true,
64182         /**
64183          * @event columnresize
64184          * Fires when the user resizes a column
64185          * @param {Number} columnIndex
64186          * @param {Number} newSize
64187          */
64188         "columnresize" : true,
64189         /**
64190          * @event columnmove
64191          * Fires when the user moves a column
64192          * @param {Number} oldIndex
64193          * @param {Number} newIndex
64194          */
64195         "columnmove" : true,
64196         /**
64197          * @event startdrag
64198          * Fires when row(s) start being dragged
64199          * @param {Grid} this
64200          * @param {Roo.GridDD} dd The drag drop object
64201          * @param {event} e The raw browser event
64202          */
64203         "startdrag" : true,
64204         /**
64205          * @event enddrag
64206          * Fires when a drag operation is complete
64207          * @param {Grid} this
64208          * @param {Roo.GridDD} dd The drag drop object
64209          * @param {event} e The raw browser event
64210          */
64211         "enddrag" : true,
64212         /**
64213          * @event dragdrop
64214          * Fires when dragged row(s) are dropped on a valid DD target
64215          * @param {Grid} this
64216          * @param {Roo.GridDD} dd The drag drop object
64217          * @param {String} targetId The target drag drop object
64218          * @param {event} e The raw browser event
64219          */
64220         "dragdrop" : true,
64221         /**
64222          * @event dragover
64223          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
64224          * @param {Grid} this
64225          * @param {Roo.GridDD} dd The drag drop object
64226          * @param {String} targetId The target drag drop object
64227          * @param {event} e The raw browser event
64228          */
64229         "dragover" : true,
64230         /**
64231          * @event dragenter
64232          *  Fires when the dragged row(s) first cross another DD target while being dragged
64233          * @param {Grid} this
64234          * @param {Roo.GridDD} dd The drag drop object
64235          * @param {String} targetId The target drag drop object
64236          * @param {event} e The raw browser event
64237          */
64238         "dragenter" : true,
64239         /**
64240          * @event dragout
64241          * Fires when the dragged row(s) leave another DD target while being dragged
64242          * @param {Grid} this
64243          * @param {Roo.GridDD} dd The drag drop object
64244          * @param {String} targetId The target drag drop object
64245          * @param {event} e The raw browser event
64246          */
64247         "dragout" : true,
64248         /**
64249          * @event rowclass
64250          * Fires when a row is rendered, so you can change add a style to it.
64251          * @param {GridView} gridview   The grid view
64252          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
64253          */
64254         'rowclass' : true,
64255
64256         /**
64257          * @event render
64258          * Fires when the grid is rendered
64259          * @param {Grid} grid
64260          */
64261         'render' : true
64262     });
64263
64264     Roo.grid.Grid.superclass.constructor.call(this);
64265 };
64266 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
64267     
64268     /**
64269          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
64270          */
64271         /**
64272          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
64273          */
64274         /**
64275          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
64276          */
64277         /**
64278          * @cfg {Roo.data.Store} ds The data store for the grid
64279          */
64280         /**
64281          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
64282          */
64283          
64284          /**
64285          * @cfg {Roo.PagingToolbar} footer the paging toolbar
64286          */
64287         
64288         /**
64289      * @cfg {String} ddGroup - drag drop group.
64290      */
64291       /**
64292      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
64293      */
64294
64295     /**
64296      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
64297      */
64298     minColumnWidth : 25,
64299
64300     /**
64301      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
64302      * <b>on initial render.</b> It is more efficient to explicitly size the columns
64303      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
64304      */
64305     autoSizeColumns : false,
64306
64307     /**
64308      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
64309      */
64310     autoSizeHeaders : true,
64311
64312     /**
64313      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
64314      */
64315     monitorWindowResize : true,
64316
64317     /**
64318      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
64319      * rows measured to get a columns size. Default is 0 (all rows).
64320      */
64321     maxRowsToMeasure : 0,
64322
64323     /**
64324      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
64325      */
64326     trackMouseOver : true,
64327
64328     /**
64329     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
64330     */
64331       /**
64332     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
64333     */
64334     
64335     /**
64336     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
64337     */
64338     enableDragDrop : false,
64339     
64340     /**
64341     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
64342     */
64343     enableColumnMove : true,
64344     
64345     /**
64346     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
64347     */
64348     enableColumnHide : true,
64349     
64350     /**
64351     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
64352     */
64353     enableRowHeightSync : false,
64354     
64355     /**
64356     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
64357     */
64358     stripeRows : true,
64359     
64360     /**
64361     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
64362     */
64363     autoHeight : false,
64364
64365     /**
64366      * @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.
64367      */
64368     autoExpandColumn : false,
64369
64370     /**
64371     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
64372     * Default is 50.
64373     */
64374     autoExpandMin : 50,
64375
64376     /**
64377     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
64378     */
64379     autoExpandMax : 1000,
64380
64381     /**
64382     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
64383     */
64384     view : null,
64385
64386     /**
64387     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
64388     */
64389     loadMask : false,
64390     /**
64391     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
64392     */
64393     dropTarget: false,
64394      /**
64395     * @cfg {boolean} sortColMenu Sort the column order menu when it shows (usefull for long lists..) default false
64396     */ 
64397     sortColMenu : false,
64398     
64399     // private
64400     rendered : false,
64401
64402     /**
64403     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
64404     * of a fixed width. Default is false.
64405     */
64406     /**
64407     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
64408     */
64409     
64410     
64411     /**
64412     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
64413     * %0 is replaced with the number of selected rows.
64414     */
64415     ddText : "{0} selected row{1}",
64416     
64417     
64418     /**
64419      * Called once after all setup has been completed and the grid is ready to be rendered.
64420      * @return {Roo.grid.Grid} this
64421      */
64422     render : function()
64423     {
64424         var c = this.container;
64425         // try to detect autoHeight/width mode
64426         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
64427             this.autoHeight = true;
64428         }
64429         var view = this.getView();
64430         view.init(this);
64431
64432         c.on("click", this.onClick, this);
64433         c.on("dblclick", this.onDblClick, this);
64434         c.on("contextmenu", this.onContextMenu, this);
64435         c.on("keydown", this.onKeyDown, this);
64436         if (Roo.isTouch) {
64437             c.on("touchstart", this.onTouchStart, this);
64438         }
64439
64440         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
64441
64442         this.getSelectionModel().init(this);
64443
64444         view.render();
64445
64446         if(this.loadMask){
64447             this.loadMask = new Roo.LoadMask(this.container,
64448                     Roo.apply({store:this.dataSource}, this.loadMask));
64449         }
64450         
64451         
64452         if (this.toolbar && this.toolbar.xtype) {
64453             this.toolbar.container = this.getView().getHeaderPanel(true);
64454             this.toolbar = new Roo.Toolbar(this.toolbar);
64455         }
64456         if (this.footer && this.footer.xtype) {
64457             this.footer.dataSource = this.getDataSource();
64458             this.footer.container = this.getView().getFooterPanel(true);
64459             this.footer = Roo.factory(this.footer, Roo);
64460         }
64461         if (this.dropTarget && this.dropTarget.xtype) {
64462             delete this.dropTarget.xtype;
64463             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
64464         }
64465         
64466         
64467         this.rendered = true;
64468         this.fireEvent('render', this);
64469         return this;
64470     },
64471
64472     /**
64473      * Reconfigures the grid to use a different Store and Column Model.
64474      * The View will be bound to the new objects and refreshed.
64475      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
64476      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
64477      */
64478     reconfigure : function(dataSource, colModel){
64479         if(this.loadMask){
64480             this.loadMask.destroy();
64481             this.loadMask = new Roo.LoadMask(this.container,
64482                     Roo.apply({store:dataSource}, this.loadMask));
64483         }
64484         this.view.bind(dataSource, colModel);
64485         this.dataSource = dataSource;
64486         this.colModel = colModel;
64487         this.view.refresh(true);
64488     },
64489     /**
64490      * addColumns
64491      * Add's a column, default at the end..
64492      
64493      * @param {int} position to add (default end)
64494      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
64495      */
64496     addColumns : function(pos, ar)
64497     {
64498         
64499         for (var i =0;i< ar.length;i++) {
64500             var cfg = ar[i];
64501             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
64502             this.cm.lookup[cfg.id] = cfg;
64503         }
64504         
64505         
64506         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
64507             pos = this.cm.config.length; //this.cm.config.push(cfg);
64508         } 
64509         pos = Math.max(0,pos);
64510         ar.unshift(0);
64511         ar.unshift(pos);
64512         this.cm.config.splice.apply(this.cm.config, ar);
64513         
64514         
64515         
64516         this.view.generateRules(this.cm);
64517         this.view.refresh(true);
64518         
64519     },
64520     
64521     
64522     
64523     
64524     // private
64525     onKeyDown : function(e){
64526         this.fireEvent("keydown", e);
64527     },
64528
64529     /**
64530      * Destroy this grid.
64531      * @param {Boolean} removeEl True to remove the element
64532      */
64533     destroy : function(removeEl, keepListeners){
64534         if(this.loadMask){
64535             this.loadMask.destroy();
64536         }
64537         var c = this.container;
64538         c.removeAllListeners();
64539         this.view.destroy();
64540         this.colModel.purgeListeners();
64541         if(!keepListeners){
64542             this.purgeListeners();
64543         }
64544         c.update("");
64545         if(removeEl === true){
64546             c.remove();
64547         }
64548     },
64549
64550     // private
64551     processEvent : function(name, e){
64552         // does this fire select???
64553         //Roo.log('grid:processEvent '  + name);
64554         
64555         if (name != 'touchstart' ) {
64556             this.fireEvent(name, e);    
64557         }
64558         
64559         var t = e.getTarget();
64560         var v = this.view;
64561         var header = v.findHeaderIndex(t);
64562         if(header !== false){
64563             var ename = name == 'touchstart' ? 'click' : name;
64564              
64565             this.fireEvent("header" + ename, this, header, e);
64566         }else{
64567             var row = v.findRowIndex(t);
64568             var cell = v.findCellIndex(t);
64569             if (name == 'touchstart') {
64570                 // first touch is always a click.
64571                 // hopefull this happens after selection is updated.?
64572                 name = false;
64573                 
64574                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
64575                     var cs = this.selModel.getSelectedCell();
64576                     if (row == cs[0] && cell == cs[1]){
64577                         name = 'dblclick';
64578                     }
64579                 }
64580                 if (typeof(this.selModel.getSelections) != 'undefined') {
64581                     var cs = this.selModel.getSelections();
64582                     var ds = this.dataSource;
64583                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
64584                         name = 'dblclick';
64585                     }
64586                 }
64587                 if (!name) {
64588                     return;
64589                 }
64590             }
64591             
64592             
64593             if(row !== false){
64594                 this.fireEvent("row" + name, this, row, e);
64595                 if(cell !== false){
64596                     this.fireEvent("cell" + name, this, row, cell, e);
64597                 }
64598             }
64599         }
64600     },
64601
64602     // private
64603     onClick : function(e){
64604         this.processEvent("click", e);
64605     },
64606    // private
64607     onTouchStart : function(e){
64608         this.processEvent("touchstart", e);
64609     },
64610
64611     // private
64612     onContextMenu : function(e, t){
64613         this.processEvent("contextmenu", e);
64614     },
64615
64616     // private
64617     onDblClick : function(e){
64618         this.processEvent("dblclick", e);
64619     },
64620
64621     // private
64622     walkCells : function(row, col, step, fn, scope){
64623         var cm = this.colModel, clen = cm.getColumnCount();
64624         var ds = this.dataSource, rlen = ds.getCount(), first = true;
64625         if(step < 0){
64626             if(col < 0){
64627                 row--;
64628                 first = false;
64629             }
64630             while(row >= 0){
64631                 if(!first){
64632                     col = clen-1;
64633                 }
64634                 first = false;
64635                 while(col >= 0){
64636                     if(fn.call(scope || this, row, col, cm) === true){
64637                         return [row, col];
64638                     }
64639                     col--;
64640                 }
64641                 row--;
64642             }
64643         } else {
64644             if(col >= clen){
64645                 row++;
64646                 first = false;
64647             }
64648             while(row < rlen){
64649                 if(!first){
64650                     col = 0;
64651                 }
64652                 first = false;
64653                 while(col < clen){
64654                     if(fn.call(scope || this, row, col, cm) === true){
64655                         return [row, col];
64656                     }
64657                     col++;
64658                 }
64659                 row++;
64660             }
64661         }
64662         return null;
64663     },
64664
64665     // private
64666     getSelections : function(){
64667         return this.selModel.getSelections();
64668     },
64669
64670     /**
64671      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
64672      * but if manual update is required this method will initiate it.
64673      */
64674     autoSize : function(){
64675         if(this.rendered){
64676             this.view.layout();
64677             if(this.view.adjustForScroll){
64678                 this.view.adjustForScroll();
64679             }
64680         }
64681     },
64682
64683     /**
64684      * Returns the grid's underlying element.
64685      * @return {Element} The element
64686      */
64687     getGridEl : function(){
64688         return this.container;
64689     },
64690
64691     // private for compatibility, overridden by editor grid
64692     stopEditing : function(){},
64693
64694     /**
64695      * Returns the grid's SelectionModel.
64696      * @return {SelectionModel}
64697      */
64698     getSelectionModel : function(){
64699         if(!this.selModel){
64700             this.selModel = new Roo.grid.RowSelectionModel();
64701         }
64702         return this.selModel;
64703     },
64704
64705     /**
64706      * Returns the grid's DataSource.
64707      * @return {DataSource}
64708      */
64709     getDataSource : function(){
64710         return this.dataSource;
64711     },
64712
64713     /**
64714      * Returns the grid's ColumnModel.
64715      * @return {ColumnModel}
64716      */
64717     getColumnModel : function(){
64718         return this.colModel;
64719     },
64720
64721     /**
64722      * Returns the grid's GridView object.
64723      * @return {GridView}
64724      */
64725     getView : function(){
64726         if(!this.view){
64727             this.view = new Roo.grid.GridView(this.viewConfig);
64728             this.relayEvents(this.view, [
64729                 "beforerowremoved", "beforerowsinserted",
64730                 "beforerefresh", "rowremoved",
64731                 "rowsinserted", "rowupdated" ,"refresh"
64732             ]);
64733         }
64734         return this.view;
64735     },
64736     /**
64737      * Called to get grid's drag proxy text, by default returns this.ddText.
64738      * Override this to put something different in the dragged text.
64739      * @return {String}
64740      */
64741     getDragDropText : function(){
64742         var count = this.selModel.getCount();
64743         return String.format(this.ddText, count, count == 1 ? '' : 's');
64744     }
64745 });
64746 /*
64747  * Based on:
64748  * Ext JS Library 1.1.1
64749  * Copyright(c) 2006-2007, Ext JS, LLC.
64750  *
64751  * Originally Released Under LGPL - original licence link has changed is not relivant.
64752  *
64753  * Fork - LGPL
64754  * <script type="text/javascript">
64755  */
64756  /**
64757  * @class Roo.grid.AbstractGridView
64758  * @extends Roo.util.Observable
64759  * @abstract
64760  * Abstract base class for grid Views
64761  * @constructor
64762  */
64763 Roo.grid.AbstractGridView = function(){
64764         this.grid = null;
64765         
64766         this.events = {
64767             "beforerowremoved" : true,
64768             "beforerowsinserted" : true,
64769             "beforerefresh" : true,
64770             "rowremoved" : true,
64771             "rowsinserted" : true,
64772             "rowupdated" : true,
64773             "refresh" : true
64774         };
64775     Roo.grid.AbstractGridView.superclass.constructor.call(this);
64776 };
64777
64778 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
64779     rowClass : "x-grid-row",
64780     cellClass : "x-grid-cell",
64781     tdClass : "x-grid-td",
64782     hdClass : "x-grid-hd",
64783     splitClass : "x-grid-hd-split",
64784     
64785     init: function(grid){
64786         this.grid = grid;
64787                 var cid = this.grid.getGridEl().id;
64788         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
64789         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
64790         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
64791         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
64792         },
64793         
64794     getColumnRenderers : function(){
64795         var renderers = [];
64796         var cm = this.grid.colModel;
64797         var colCount = cm.getColumnCount();
64798         for(var i = 0; i < colCount; i++){
64799             renderers[i] = cm.getRenderer(i);
64800         }
64801         return renderers;
64802     },
64803     
64804     getColumnIds : function(){
64805         var ids = [];
64806         var cm = this.grid.colModel;
64807         var colCount = cm.getColumnCount();
64808         for(var i = 0; i < colCount; i++){
64809             ids[i] = cm.getColumnId(i);
64810         }
64811         return ids;
64812     },
64813     
64814     getDataIndexes : function(){
64815         if(!this.indexMap){
64816             this.indexMap = this.buildIndexMap();
64817         }
64818         return this.indexMap.colToData;
64819     },
64820     
64821     getColumnIndexByDataIndex : function(dataIndex){
64822         if(!this.indexMap){
64823             this.indexMap = this.buildIndexMap();
64824         }
64825         return this.indexMap.dataToCol[dataIndex];
64826     },
64827     
64828     /**
64829      * Set a css style for a column dynamically. 
64830      * @param {Number} colIndex The index of the column
64831      * @param {String} name The css property name
64832      * @param {String} value The css value
64833      */
64834     setCSSStyle : function(colIndex, name, value){
64835         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
64836         Roo.util.CSS.updateRule(selector, name, value);
64837     },
64838     
64839     generateRules : function(cm){
64840         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
64841         Roo.util.CSS.removeStyleSheet(rulesId);
64842         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
64843             var cid = cm.getColumnId(i);
64844             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
64845                          this.tdSelector, cid, " {\n}\n",
64846                          this.hdSelector, cid, " {\n}\n",
64847                          this.splitSelector, cid, " {\n}\n");
64848         }
64849         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
64850     }
64851 });/*
64852  * Based on:
64853  * Ext JS Library 1.1.1
64854  * Copyright(c) 2006-2007, Ext JS, LLC.
64855  *
64856  * Originally Released Under LGPL - original licence link has changed is not relivant.
64857  *
64858  * Fork - LGPL
64859  * <script type="text/javascript">
64860  */
64861
64862 // private
64863 // This is a support class used internally by the Grid components
64864 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
64865     this.grid = grid;
64866     this.view = grid.getView();
64867     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
64868     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
64869     if(hd2){
64870         this.setHandleElId(Roo.id(hd));
64871         this.setOuterHandleElId(Roo.id(hd2));
64872     }
64873     this.scroll = false;
64874 };
64875 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
64876     maxDragWidth: 120,
64877     getDragData : function(e){
64878         var t = Roo.lib.Event.getTarget(e);
64879         var h = this.view.findHeaderCell(t);
64880         if(h){
64881             return {ddel: h.firstChild, header:h};
64882         }
64883         return false;
64884     },
64885
64886     onInitDrag : function(e){
64887         this.view.headersDisabled = true;
64888         var clone = this.dragData.ddel.cloneNode(true);
64889         clone.id = Roo.id();
64890         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
64891         this.proxy.update(clone);
64892         return true;
64893     },
64894
64895     afterValidDrop : function(){
64896         var v = this.view;
64897         setTimeout(function(){
64898             v.headersDisabled = false;
64899         }, 50);
64900     },
64901
64902     afterInvalidDrop : function(){
64903         var v = this.view;
64904         setTimeout(function(){
64905             v.headersDisabled = false;
64906         }, 50);
64907     }
64908 });
64909 /*
64910  * Based on:
64911  * Ext JS Library 1.1.1
64912  * Copyright(c) 2006-2007, Ext JS, LLC.
64913  *
64914  * Originally Released Under LGPL - original licence link has changed is not relivant.
64915  *
64916  * Fork - LGPL
64917  * <script type="text/javascript">
64918  */
64919 // private
64920 // This is a support class used internally by the Grid components
64921 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
64922     this.grid = grid;
64923     this.view = grid.getView();
64924     // split the proxies so they don't interfere with mouse events
64925     this.proxyTop = Roo.DomHelper.append(document.body, {
64926         cls:"col-move-top", html:"&#160;"
64927     }, true);
64928     this.proxyBottom = Roo.DomHelper.append(document.body, {
64929         cls:"col-move-bottom", html:"&#160;"
64930     }, true);
64931     this.proxyTop.hide = this.proxyBottom.hide = function(){
64932         this.setLeftTop(-100,-100);
64933         this.setStyle("visibility", "hidden");
64934     };
64935     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
64936     // temporarily disabled
64937     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
64938     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
64939 };
64940 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
64941     proxyOffsets : [-4, -9],
64942     fly: Roo.Element.fly,
64943
64944     getTargetFromEvent : function(e){
64945         var t = Roo.lib.Event.getTarget(e);
64946         var cindex = this.view.findCellIndex(t);
64947         if(cindex !== false){
64948             return this.view.getHeaderCell(cindex);
64949         }
64950         return null;
64951     },
64952
64953     nextVisible : function(h){
64954         var v = this.view, cm = this.grid.colModel;
64955         h = h.nextSibling;
64956         while(h){
64957             if(!cm.isHidden(v.getCellIndex(h))){
64958                 return h;
64959             }
64960             h = h.nextSibling;
64961         }
64962         return null;
64963     },
64964
64965     prevVisible : function(h){
64966         var v = this.view, cm = this.grid.colModel;
64967         h = h.prevSibling;
64968         while(h){
64969             if(!cm.isHidden(v.getCellIndex(h))){
64970                 return h;
64971             }
64972             h = h.prevSibling;
64973         }
64974         return null;
64975     },
64976
64977     positionIndicator : function(h, n, e){
64978         var x = Roo.lib.Event.getPageX(e);
64979         var r = Roo.lib.Dom.getRegion(n.firstChild);
64980         var px, pt, py = r.top + this.proxyOffsets[1];
64981         if((r.right - x) <= (r.right-r.left)/2){
64982             px = r.right+this.view.borderWidth;
64983             pt = "after";
64984         }else{
64985             px = r.left;
64986             pt = "before";
64987         }
64988         var oldIndex = this.view.getCellIndex(h);
64989         var newIndex = this.view.getCellIndex(n);
64990
64991         if(this.grid.colModel.isFixed(newIndex)){
64992             return false;
64993         }
64994
64995         var locked = this.grid.colModel.isLocked(newIndex);
64996
64997         if(pt == "after"){
64998             newIndex++;
64999         }
65000         if(oldIndex < newIndex){
65001             newIndex--;
65002         }
65003         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
65004             return false;
65005         }
65006         px +=  this.proxyOffsets[0];
65007         this.proxyTop.setLeftTop(px, py);
65008         this.proxyTop.show();
65009         if(!this.bottomOffset){
65010             this.bottomOffset = this.view.mainHd.getHeight();
65011         }
65012         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
65013         this.proxyBottom.show();
65014         return pt;
65015     },
65016
65017     onNodeEnter : function(n, dd, e, data){
65018         if(data.header != n){
65019             this.positionIndicator(data.header, n, e);
65020         }
65021     },
65022
65023     onNodeOver : function(n, dd, e, data){
65024         var result = false;
65025         if(data.header != n){
65026             result = this.positionIndicator(data.header, n, e);
65027         }
65028         if(!result){
65029             this.proxyTop.hide();
65030             this.proxyBottom.hide();
65031         }
65032         return result ? this.dropAllowed : this.dropNotAllowed;
65033     },
65034
65035     onNodeOut : function(n, dd, e, data){
65036         this.proxyTop.hide();
65037         this.proxyBottom.hide();
65038     },
65039
65040     onNodeDrop : function(n, dd, e, data){
65041         var h = data.header;
65042         if(h != n){
65043             var cm = this.grid.colModel;
65044             var x = Roo.lib.Event.getPageX(e);
65045             var r = Roo.lib.Dom.getRegion(n.firstChild);
65046             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
65047             var oldIndex = this.view.getCellIndex(h);
65048             var newIndex = this.view.getCellIndex(n);
65049             var locked = cm.isLocked(newIndex);
65050             if(pt == "after"){
65051                 newIndex++;
65052             }
65053             if(oldIndex < newIndex){
65054                 newIndex--;
65055             }
65056             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
65057                 return false;
65058             }
65059             cm.setLocked(oldIndex, locked, true);
65060             cm.moveColumn(oldIndex, newIndex);
65061             this.grid.fireEvent("columnmove", oldIndex, newIndex);
65062             return true;
65063         }
65064         return false;
65065     }
65066 });
65067 /*
65068  * Based on:
65069  * Ext JS Library 1.1.1
65070  * Copyright(c) 2006-2007, Ext JS, LLC.
65071  *
65072  * Originally Released Under LGPL - original licence link has changed is not relivant.
65073  *
65074  * Fork - LGPL
65075  * <script type="text/javascript">
65076  */
65077   
65078 /**
65079  * @class Roo.grid.GridView
65080  * @extends Roo.util.Observable
65081  *
65082  * @constructor
65083  * @param {Object} config
65084  */
65085 Roo.grid.GridView = function(config){
65086     Roo.grid.GridView.superclass.constructor.call(this);
65087     this.el = null;
65088
65089     Roo.apply(this, config);
65090 };
65091
65092 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
65093
65094     unselectable :  'unselectable="on"',
65095     unselectableCls :  'x-unselectable',
65096     
65097     
65098     rowClass : "x-grid-row",
65099
65100     cellClass : "x-grid-col",
65101
65102     tdClass : "x-grid-td",
65103
65104     hdClass : "x-grid-hd",
65105
65106     splitClass : "x-grid-split",
65107
65108     sortClasses : ["sort-asc", "sort-desc"],
65109
65110     enableMoveAnim : false,
65111
65112     hlColor: "C3DAF9",
65113
65114     dh : Roo.DomHelper,
65115
65116     fly : Roo.Element.fly,
65117
65118     css : Roo.util.CSS,
65119
65120     borderWidth: 1,
65121
65122     splitOffset: 3,
65123
65124     scrollIncrement : 22,
65125
65126     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
65127
65128     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
65129
65130     bind : function(ds, cm){
65131         if(this.ds){
65132             this.ds.un("load", this.onLoad, this);
65133             this.ds.un("datachanged", this.onDataChange, this);
65134             this.ds.un("add", this.onAdd, this);
65135             this.ds.un("remove", this.onRemove, this);
65136             this.ds.un("update", this.onUpdate, this);
65137             this.ds.un("clear", this.onClear, this);
65138         }
65139         if(ds){
65140             ds.on("load", this.onLoad, this);
65141             ds.on("datachanged", this.onDataChange, this);
65142             ds.on("add", this.onAdd, this);
65143             ds.on("remove", this.onRemove, this);
65144             ds.on("update", this.onUpdate, this);
65145             ds.on("clear", this.onClear, this);
65146         }
65147         this.ds = ds;
65148
65149         if(this.cm){
65150             this.cm.un("widthchange", this.onColWidthChange, this);
65151             this.cm.un("headerchange", this.onHeaderChange, this);
65152             this.cm.un("hiddenchange", this.onHiddenChange, this);
65153             this.cm.un("columnmoved", this.onColumnMove, this);
65154             this.cm.un("columnlockchange", this.onColumnLock, this);
65155         }
65156         if(cm){
65157             this.generateRules(cm);
65158             cm.on("widthchange", this.onColWidthChange, this);
65159             cm.on("headerchange", this.onHeaderChange, this);
65160             cm.on("hiddenchange", this.onHiddenChange, this);
65161             cm.on("columnmoved", this.onColumnMove, this);
65162             cm.on("columnlockchange", this.onColumnLock, this);
65163         }
65164         this.cm = cm;
65165     },
65166
65167     init: function(grid){
65168         Roo.grid.GridView.superclass.init.call(this, grid);
65169
65170         this.bind(grid.dataSource, grid.colModel);
65171
65172         grid.on("headerclick", this.handleHeaderClick, this);
65173
65174         if(grid.trackMouseOver){
65175             grid.on("mouseover", this.onRowOver, this);
65176             grid.on("mouseout", this.onRowOut, this);
65177         }
65178         grid.cancelTextSelection = function(){};
65179         this.gridId = grid.id;
65180
65181         var tpls = this.templates || {};
65182
65183         if(!tpls.master){
65184             tpls.master = new Roo.Template(
65185                '<div class="x-grid" hidefocus="true">',
65186                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
65187                   '<div class="x-grid-topbar"></div>',
65188                   '<div class="x-grid-scroller"><div></div></div>',
65189                   '<div class="x-grid-locked">',
65190                       '<div class="x-grid-header">{lockedHeader}</div>',
65191                       '<div class="x-grid-body">{lockedBody}</div>',
65192                   "</div>",
65193                   '<div class="x-grid-viewport">',
65194                       '<div class="x-grid-header">{header}</div>',
65195                       '<div class="x-grid-body">{body}</div>',
65196                   "</div>",
65197                   '<div class="x-grid-bottombar"></div>',
65198                  
65199                   '<div class="x-grid-resize-proxy">&#160;</div>',
65200                "</div>"
65201             );
65202             tpls.master.disableformats = true;
65203         }
65204
65205         if(!tpls.header){
65206             tpls.header = new Roo.Template(
65207                '<table border="0" cellspacing="0" cellpadding="0">',
65208                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
65209                "</table>{splits}"
65210             );
65211             tpls.header.disableformats = true;
65212         }
65213         tpls.header.compile();
65214
65215         if(!tpls.hcell){
65216             tpls.hcell = new Roo.Template(
65217                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
65218                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
65219                 "</div></td>"
65220              );
65221              tpls.hcell.disableFormats = true;
65222         }
65223         tpls.hcell.compile();
65224
65225         if(!tpls.hsplit){
65226             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
65227                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
65228             tpls.hsplit.disableFormats = true;
65229         }
65230         tpls.hsplit.compile();
65231
65232         if(!tpls.body){
65233             tpls.body = new Roo.Template(
65234                '<table border="0" cellspacing="0" cellpadding="0">',
65235                "<tbody>{rows}</tbody>",
65236                "</table>"
65237             );
65238             tpls.body.disableFormats = true;
65239         }
65240         tpls.body.compile();
65241
65242         if(!tpls.row){
65243             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
65244             tpls.row.disableFormats = true;
65245         }
65246         tpls.row.compile();
65247
65248         if(!tpls.cell){
65249             tpls.cell = new Roo.Template(
65250                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
65251                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
65252                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
65253                 "</td>"
65254             );
65255             tpls.cell.disableFormats = true;
65256         }
65257         tpls.cell.compile();
65258
65259         this.templates = tpls;
65260     },
65261
65262     // remap these for backwards compat
65263     onColWidthChange : function(){
65264         this.updateColumns.apply(this, arguments);
65265     },
65266     onHeaderChange : function(){
65267         this.updateHeaders.apply(this, arguments);
65268     }, 
65269     onHiddenChange : function(){
65270         this.handleHiddenChange.apply(this, arguments);
65271     },
65272     onColumnMove : function(){
65273         this.handleColumnMove.apply(this, arguments);
65274     },
65275     onColumnLock : function(){
65276         this.handleLockChange.apply(this, arguments);
65277     },
65278
65279     onDataChange : function(){
65280         this.refresh();
65281         this.updateHeaderSortState();
65282     },
65283
65284     onClear : function(){
65285         this.refresh();
65286     },
65287
65288     onUpdate : function(ds, record){
65289         this.refreshRow(record);
65290     },
65291
65292     refreshRow : function(record){
65293         var ds = this.ds, index;
65294         if(typeof record == 'number'){
65295             index = record;
65296             record = ds.getAt(index);
65297         }else{
65298             index = ds.indexOf(record);
65299         }
65300         this.insertRows(ds, index, index, true);
65301         this.onRemove(ds, record, index+1, true);
65302         this.syncRowHeights(index, index);
65303         this.layout();
65304         this.fireEvent("rowupdated", this, index, record);
65305     },
65306
65307     onAdd : function(ds, records, index){
65308         this.insertRows(ds, index, index + (records.length-1));
65309     },
65310
65311     onRemove : function(ds, record, index, isUpdate){
65312         if(isUpdate !== true){
65313             this.fireEvent("beforerowremoved", this, index, record);
65314         }
65315         var bt = this.getBodyTable(), lt = this.getLockedTable();
65316         if(bt.rows[index]){
65317             bt.firstChild.removeChild(bt.rows[index]);
65318         }
65319         if(lt.rows[index]){
65320             lt.firstChild.removeChild(lt.rows[index]);
65321         }
65322         if(isUpdate !== true){
65323             this.stripeRows(index);
65324             this.syncRowHeights(index, index);
65325             this.layout();
65326             this.fireEvent("rowremoved", this, index, record);
65327         }
65328     },
65329
65330     onLoad : function(){
65331         this.scrollToTop();
65332     },
65333
65334     /**
65335      * Scrolls the grid to the top
65336      */
65337     scrollToTop : function(){
65338         if(this.scroller){
65339             this.scroller.dom.scrollTop = 0;
65340             this.syncScroll();
65341         }
65342     },
65343
65344     /**
65345      * Gets a panel in the header of the grid that can be used for toolbars etc.
65346      * After modifying the contents of this panel a call to grid.autoSize() may be
65347      * required to register any changes in size.
65348      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
65349      * @return Roo.Element
65350      */
65351     getHeaderPanel : function(doShow){
65352         if(doShow){
65353             this.headerPanel.show();
65354         }
65355         return this.headerPanel;
65356     },
65357
65358     /**
65359      * Gets a panel in the footer of the grid that can be used for toolbars etc.
65360      * After modifying the contents of this panel a call to grid.autoSize() may be
65361      * required to register any changes in size.
65362      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
65363      * @return Roo.Element
65364      */
65365     getFooterPanel : function(doShow){
65366         if(doShow){
65367             this.footerPanel.show();
65368         }
65369         return this.footerPanel;
65370     },
65371
65372     initElements : function(){
65373         var E = Roo.Element;
65374         var el = this.grid.getGridEl().dom.firstChild;
65375         var cs = el.childNodes;
65376
65377         this.el = new E(el);
65378         
65379          this.focusEl = new E(el.firstChild);
65380         this.focusEl.swallowEvent("click", true);
65381         
65382         this.headerPanel = new E(cs[1]);
65383         this.headerPanel.enableDisplayMode("block");
65384
65385         this.scroller = new E(cs[2]);
65386         this.scrollSizer = new E(this.scroller.dom.firstChild);
65387
65388         this.lockedWrap = new E(cs[3]);
65389         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
65390         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
65391
65392         this.mainWrap = new E(cs[4]);
65393         this.mainHd = new E(this.mainWrap.dom.firstChild);
65394         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
65395
65396         this.footerPanel = new E(cs[5]);
65397         this.footerPanel.enableDisplayMode("block");
65398
65399         this.resizeProxy = new E(cs[6]);
65400
65401         this.headerSelector = String.format(
65402            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
65403            this.lockedHd.id, this.mainHd.id
65404         );
65405
65406         this.splitterSelector = String.format(
65407            '#{0} div.x-grid-split, #{1} div.x-grid-split',
65408            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
65409         );
65410     },
65411     idToCssName : function(s)
65412     {
65413         return s.replace(/[^a-z0-9]+/ig, '-');
65414     },
65415
65416     getHeaderCell : function(index){
65417         return Roo.DomQuery.select(this.headerSelector)[index];
65418     },
65419
65420     getHeaderCellMeasure : function(index){
65421         return this.getHeaderCell(index).firstChild;
65422     },
65423
65424     getHeaderCellText : function(index){
65425         return this.getHeaderCell(index).firstChild.firstChild;
65426     },
65427
65428     getLockedTable : function(){
65429         return this.lockedBody.dom.firstChild;
65430     },
65431
65432     getBodyTable : function(){
65433         return this.mainBody.dom.firstChild;
65434     },
65435
65436     getLockedRow : function(index){
65437         return this.getLockedTable().rows[index];
65438     },
65439
65440     getRow : function(index){
65441         return this.getBodyTable().rows[index];
65442     },
65443
65444     getRowComposite : function(index){
65445         if(!this.rowEl){
65446             this.rowEl = new Roo.CompositeElementLite();
65447         }
65448         var els = [], lrow, mrow;
65449         if(lrow = this.getLockedRow(index)){
65450             els.push(lrow);
65451         }
65452         if(mrow = this.getRow(index)){
65453             els.push(mrow);
65454         }
65455         this.rowEl.elements = els;
65456         return this.rowEl;
65457     },
65458     /**
65459      * Gets the 'td' of the cell
65460      * 
65461      * @param {Integer} rowIndex row to select
65462      * @param {Integer} colIndex column to select
65463      * 
65464      * @return {Object} 
65465      */
65466     getCell : function(rowIndex, colIndex){
65467         var locked = this.cm.getLockedCount();
65468         var source;
65469         if(colIndex < locked){
65470             source = this.lockedBody.dom.firstChild;
65471         }else{
65472             source = this.mainBody.dom.firstChild;
65473             colIndex -= locked;
65474         }
65475         return source.rows[rowIndex].childNodes[colIndex];
65476     },
65477
65478     getCellText : function(rowIndex, colIndex){
65479         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
65480     },
65481
65482     getCellBox : function(cell){
65483         var b = this.fly(cell).getBox();
65484         if(Roo.isOpera){ // opera fails to report the Y
65485             b.y = cell.offsetTop + this.mainBody.getY();
65486         }
65487         return b;
65488     },
65489
65490     getCellIndex : function(cell){
65491         var id = String(cell.className).match(this.cellRE);
65492         if(id){
65493             return parseInt(id[1], 10);
65494         }
65495         return 0;
65496     },
65497
65498     findHeaderIndex : function(n){
65499         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
65500         return r ? this.getCellIndex(r) : false;
65501     },
65502
65503     findHeaderCell : function(n){
65504         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
65505         return r ? r : false;
65506     },
65507
65508     findRowIndex : function(n){
65509         if(!n){
65510             return false;
65511         }
65512         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
65513         return r ? r.rowIndex : false;
65514     },
65515
65516     findCellIndex : function(node){
65517         var stop = this.el.dom;
65518         while(node && node != stop){
65519             if(this.findRE.test(node.className)){
65520                 return this.getCellIndex(node);
65521             }
65522             node = node.parentNode;
65523         }
65524         return false;
65525     },
65526
65527     getColumnId : function(index){
65528         return this.cm.getColumnId(index);
65529     },
65530
65531     getSplitters : function()
65532     {
65533         if(this.splitterSelector){
65534            return Roo.DomQuery.select(this.splitterSelector);
65535         }else{
65536             return null;
65537       }
65538     },
65539
65540     getSplitter : function(index){
65541         return this.getSplitters()[index];
65542     },
65543
65544     onRowOver : function(e, t){
65545         var row;
65546         if((row = this.findRowIndex(t)) !== false){
65547             this.getRowComposite(row).addClass("x-grid-row-over");
65548         }
65549     },
65550
65551     onRowOut : function(e, t){
65552         var row;
65553         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
65554             this.getRowComposite(row).removeClass("x-grid-row-over");
65555         }
65556     },
65557
65558     renderHeaders : function(){
65559         var cm = this.cm;
65560         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
65561         var cb = [], lb = [], sb = [], lsb = [], p = {};
65562         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
65563             p.cellId = "x-grid-hd-0-" + i;
65564             p.splitId = "x-grid-csplit-0-" + i;
65565             p.id = cm.getColumnId(i);
65566             p.value = cm.getColumnHeader(i) || "";
65567             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
65568             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
65569             if(!cm.isLocked(i)){
65570                 cb[cb.length] = ct.apply(p);
65571                 sb[sb.length] = st.apply(p);
65572             }else{
65573                 lb[lb.length] = ct.apply(p);
65574                 lsb[lsb.length] = st.apply(p);
65575             }
65576         }
65577         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
65578                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
65579     },
65580
65581     updateHeaders : function(){
65582         var html = this.renderHeaders();
65583         this.lockedHd.update(html[0]);
65584         this.mainHd.update(html[1]);
65585     },
65586
65587     /**
65588      * Focuses the specified row.
65589      * @param {Number} row The row index
65590      */
65591     focusRow : function(row)
65592     {
65593         //Roo.log('GridView.focusRow');
65594         var x = this.scroller.dom.scrollLeft;
65595         this.focusCell(row, 0, false);
65596         this.scroller.dom.scrollLeft = x;
65597     },
65598
65599     /**
65600      * Focuses the specified cell.
65601      * @param {Number} row The row index
65602      * @param {Number} col The column index
65603      * @param {Boolean} hscroll false to disable horizontal scrolling
65604      */
65605     focusCell : function(row, col, hscroll)
65606     {
65607         //Roo.log('GridView.focusCell');
65608         var el = this.ensureVisible(row, col, hscroll);
65609         this.focusEl.alignTo(el, "tl-tl");
65610         if(Roo.isGecko){
65611             this.focusEl.focus();
65612         }else{
65613             this.focusEl.focus.defer(1, this.focusEl);
65614         }
65615     },
65616
65617     /**
65618      * Scrolls the specified cell into view
65619      * @param {Number} row The row index
65620      * @param {Number} col The column index
65621      * @param {Boolean} hscroll false to disable horizontal scrolling
65622      */
65623     ensureVisible : function(row, col, hscroll)
65624     {
65625         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
65626         //return null; //disable for testing.
65627         if(typeof row != "number"){
65628             row = row.rowIndex;
65629         }
65630         if(row < 0 && row >= this.ds.getCount()){
65631             return  null;
65632         }
65633         col = (col !== undefined ? col : 0);
65634         var cm = this.grid.colModel;
65635         while(cm.isHidden(col)){
65636             col++;
65637         }
65638
65639         var el = this.getCell(row, col);
65640         if(!el){
65641             return null;
65642         }
65643         var c = this.scroller.dom;
65644
65645         var ctop = parseInt(el.offsetTop, 10);
65646         var cleft = parseInt(el.offsetLeft, 10);
65647         var cbot = ctop + el.offsetHeight;
65648         var cright = cleft + el.offsetWidth;
65649         
65650         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
65651         var stop = parseInt(c.scrollTop, 10);
65652         var sleft = parseInt(c.scrollLeft, 10);
65653         var sbot = stop + ch;
65654         var sright = sleft + c.clientWidth;
65655         /*
65656         Roo.log('GridView.ensureVisible:' +
65657                 ' ctop:' + ctop +
65658                 ' c.clientHeight:' + c.clientHeight +
65659                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
65660                 ' stop:' + stop +
65661                 ' cbot:' + cbot +
65662                 ' sbot:' + sbot +
65663                 ' ch:' + ch  
65664                 );
65665         */
65666         if(ctop < stop){
65667             c.scrollTop = ctop;
65668             //Roo.log("set scrolltop to ctop DISABLE?");
65669         }else if(cbot > sbot){
65670             //Roo.log("set scrolltop to cbot-ch");
65671             c.scrollTop = cbot-ch;
65672         }
65673         
65674         if(hscroll !== false){
65675             if(cleft < sleft){
65676                 c.scrollLeft = cleft;
65677             }else if(cright > sright){
65678                 c.scrollLeft = cright-c.clientWidth;
65679             }
65680         }
65681          
65682         return el;
65683     },
65684
65685     updateColumns : function(){
65686         this.grid.stopEditing();
65687         var cm = this.grid.colModel, colIds = this.getColumnIds();
65688         //var totalWidth = cm.getTotalWidth();
65689         var pos = 0;
65690         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
65691             //if(cm.isHidden(i)) continue;
65692             var w = cm.getColumnWidth(i);
65693             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
65694             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
65695         }
65696         this.updateSplitters();
65697     },
65698
65699     generateRules : function(cm){
65700         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
65701         Roo.util.CSS.removeStyleSheet(rulesId);
65702         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
65703             var cid = cm.getColumnId(i);
65704             var align = '';
65705             if(cm.config[i].align){
65706                 align = 'text-align:'+cm.config[i].align+';';
65707             }
65708             var hidden = '';
65709             if(cm.isHidden(i)){
65710                 hidden = 'display:none;';
65711             }
65712             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
65713             ruleBuf.push(
65714                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
65715                     this.hdSelector, cid, " {\n", align, width, "}\n",
65716                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
65717                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
65718         }
65719         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
65720     },
65721
65722     updateSplitters : function(){
65723         var cm = this.cm, s = this.getSplitters();
65724         if(s){ // splitters not created yet
65725             var pos = 0, locked = true;
65726             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
65727                 if(cm.isHidden(i)) {
65728                     continue;
65729                 }
65730                 var w = cm.getColumnWidth(i); // make sure it's a number
65731                 if(!cm.isLocked(i) && locked){
65732                     pos = 0;
65733                     locked = false;
65734                 }
65735                 pos += w;
65736                 s[i].style.left = (pos-this.splitOffset) + "px";
65737             }
65738         }
65739     },
65740
65741     handleHiddenChange : function(colModel, colIndex, hidden){
65742         if(hidden){
65743             this.hideColumn(colIndex);
65744         }else{
65745             this.unhideColumn(colIndex);
65746         }
65747     },
65748
65749     hideColumn : function(colIndex){
65750         var cid = this.getColumnId(colIndex);
65751         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
65752         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
65753         if(Roo.isSafari){
65754             this.updateHeaders();
65755         }
65756         this.updateSplitters();
65757         this.layout();
65758     },
65759
65760     unhideColumn : function(colIndex){
65761         var cid = this.getColumnId(colIndex);
65762         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
65763         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
65764
65765         if(Roo.isSafari){
65766             this.updateHeaders();
65767         }
65768         this.updateSplitters();
65769         this.layout();
65770     },
65771
65772     insertRows : function(dm, firstRow, lastRow, isUpdate){
65773         if(firstRow == 0 && lastRow == dm.getCount()-1){
65774             this.refresh();
65775         }else{
65776             if(!isUpdate){
65777                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
65778             }
65779             var s = this.getScrollState();
65780             var markup = this.renderRows(firstRow, lastRow);
65781             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
65782             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
65783             this.restoreScroll(s);
65784             if(!isUpdate){
65785                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
65786                 this.syncRowHeights(firstRow, lastRow);
65787                 this.stripeRows(firstRow);
65788                 this.layout();
65789             }
65790         }
65791     },
65792
65793     bufferRows : function(markup, target, index){
65794         var before = null, trows = target.rows, tbody = target.tBodies[0];
65795         if(index < trows.length){
65796             before = trows[index];
65797         }
65798         var b = document.createElement("div");
65799         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
65800         var rows = b.firstChild.rows;
65801         for(var i = 0, len = rows.length; i < len; i++){
65802             if(before){
65803                 tbody.insertBefore(rows[0], before);
65804             }else{
65805                 tbody.appendChild(rows[0]);
65806             }
65807         }
65808         b.innerHTML = "";
65809         b = null;
65810     },
65811
65812     deleteRows : function(dm, firstRow, lastRow){
65813         if(dm.getRowCount()<1){
65814             this.fireEvent("beforerefresh", this);
65815             this.mainBody.update("");
65816             this.lockedBody.update("");
65817             this.fireEvent("refresh", this);
65818         }else{
65819             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
65820             var bt = this.getBodyTable();
65821             var tbody = bt.firstChild;
65822             var rows = bt.rows;
65823             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
65824                 tbody.removeChild(rows[firstRow]);
65825             }
65826             this.stripeRows(firstRow);
65827             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
65828         }
65829     },
65830
65831     updateRows : function(dataSource, firstRow, lastRow){
65832         var s = this.getScrollState();
65833         this.refresh();
65834         this.restoreScroll(s);
65835     },
65836
65837     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
65838         if(!noRefresh){
65839            this.refresh();
65840         }
65841         this.updateHeaderSortState();
65842     },
65843
65844     getScrollState : function(){
65845         
65846         var sb = this.scroller.dom;
65847         return {left: sb.scrollLeft, top: sb.scrollTop};
65848     },
65849
65850     stripeRows : function(startRow){
65851         if(!this.grid.stripeRows || this.ds.getCount() < 1){
65852             return;
65853         }
65854         startRow = startRow || 0;
65855         var rows = this.getBodyTable().rows;
65856         var lrows = this.getLockedTable().rows;
65857         var cls = ' x-grid-row-alt ';
65858         for(var i = startRow, len = rows.length; i < len; i++){
65859             var row = rows[i], lrow = lrows[i];
65860             var isAlt = ((i+1) % 2 == 0);
65861             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
65862             if(isAlt == hasAlt){
65863                 continue;
65864             }
65865             if(isAlt){
65866                 row.className += " x-grid-row-alt";
65867             }else{
65868                 row.className = row.className.replace("x-grid-row-alt", "");
65869             }
65870             if(lrow){
65871                 lrow.className = row.className;
65872             }
65873         }
65874     },
65875
65876     restoreScroll : function(state){
65877         //Roo.log('GridView.restoreScroll');
65878         var sb = this.scroller.dom;
65879         sb.scrollLeft = state.left;
65880         sb.scrollTop = state.top;
65881         this.syncScroll();
65882     },
65883
65884     syncScroll : function(){
65885         //Roo.log('GridView.syncScroll');
65886         var sb = this.scroller.dom;
65887         var sh = this.mainHd.dom;
65888         var bs = this.mainBody.dom;
65889         var lv = this.lockedBody.dom;
65890         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
65891         lv.scrollTop = bs.scrollTop = sb.scrollTop;
65892     },
65893
65894     handleScroll : function(e){
65895         this.syncScroll();
65896         var sb = this.scroller.dom;
65897         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
65898         e.stopEvent();
65899     },
65900
65901     handleWheel : function(e){
65902         var d = e.getWheelDelta();
65903         this.scroller.dom.scrollTop -= d*22;
65904         // set this here to prevent jumpy scrolling on large tables
65905         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
65906         e.stopEvent();
65907     },
65908
65909     renderRows : function(startRow, endRow){
65910         // pull in all the crap needed to render rows
65911         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
65912         var colCount = cm.getColumnCount();
65913
65914         if(ds.getCount() < 1){
65915             return ["", ""];
65916         }
65917
65918         // build a map for all the columns
65919         var cs = [];
65920         for(var i = 0; i < colCount; i++){
65921             var name = cm.getDataIndex(i);
65922             cs[i] = {
65923                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
65924                 renderer : cm.getRenderer(i),
65925                 id : cm.getColumnId(i),
65926                 locked : cm.isLocked(i),
65927                 has_editor : cm.isCellEditable(i)
65928             };
65929         }
65930
65931         startRow = startRow || 0;
65932         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
65933
65934         // records to render
65935         var rs = ds.getRange(startRow, endRow);
65936
65937         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
65938     },
65939
65940     // As much as I hate to duplicate code, this was branched because FireFox really hates
65941     // [].join("") on strings. The performance difference was substantial enough to
65942     // branch this function
65943     doRender : Roo.isGecko ?
65944             function(cs, rs, ds, startRow, colCount, stripe){
65945                 var ts = this.templates, ct = ts.cell, rt = ts.row;
65946                 // buffers
65947                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
65948                 
65949                 var hasListener = this.grid.hasListener('rowclass');
65950                 var rowcfg = {};
65951                 for(var j = 0, len = rs.length; j < len; j++){
65952                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
65953                     for(var i = 0; i < colCount; i++){
65954                         c = cs[i];
65955                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
65956                         p.id = c.id;
65957                         p.css = p.attr = "";
65958                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
65959                         if(p.value == undefined || p.value === "") {
65960                             p.value = "&#160;";
65961                         }
65962                         if(c.has_editor){
65963                             p.css += ' x-grid-editable-cell';
65964                         }
65965                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
65966                             p.css +=  ' x-grid-dirty-cell';
65967                         }
65968                         var markup = ct.apply(p);
65969                         if(!c.locked){
65970                             cb+= markup;
65971                         }else{
65972                             lcb+= markup;
65973                         }
65974                     }
65975                     var alt = [];
65976                     if(stripe && ((rowIndex+1) % 2 == 0)){
65977                         alt.push("x-grid-row-alt")
65978                     }
65979                     if(r.dirty){
65980                         alt.push(  " x-grid-dirty-row");
65981                     }
65982                     rp.cells = lcb;
65983                     if(this.getRowClass){
65984                         alt.push(this.getRowClass(r, rowIndex));
65985                     }
65986                     if (hasListener) {
65987                         rowcfg = {
65988                              
65989                             record: r,
65990                             rowIndex : rowIndex,
65991                             rowClass : ''
65992                         };
65993                         this.grid.fireEvent('rowclass', this, rowcfg);
65994                         alt.push(rowcfg.rowClass);
65995                     }
65996                     rp.alt = alt.join(" ");
65997                     lbuf+= rt.apply(rp);
65998                     rp.cells = cb;
65999                     buf+=  rt.apply(rp);
66000                 }
66001                 return [lbuf, buf];
66002             } :
66003             function(cs, rs, ds, startRow, colCount, stripe){
66004                 var ts = this.templates, ct = ts.cell, rt = ts.row;
66005                 // buffers
66006                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
66007                 var hasListener = this.grid.hasListener('rowclass');
66008  
66009                 var rowcfg = {};
66010                 for(var j = 0, len = rs.length; j < len; j++){
66011                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
66012                     for(var i = 0; i < colCount; i++){
66013                         c = cs[i];
66014                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
66015                         p.id = c.id;
66016                         p.css = p.attr = "";
66017                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
66018                         if(p.value == undefined || p.value === "") {
66019                             p.value = "&#160;";
66020                         }
66021                         //Roo.log(c);
66022                          if(c.has_editor){
66023                             p.css += ' x-grid-editable-cell';
66024                         }
66025                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
66026                             p.css += ' x-grid-dirty-cell' 
66027                         }
66028                         
66029                         var markup = ct.apply(p);
66030                         if(!c.locked){
66031                             cb[cb.length] = markup;
66032                         }else{
66033                             lcb[lcb.length] = markup;
66034                         }
66035                     }
66036                     var alt = [];
66037                     if(stripe && ((rowIndex+1) % 2 == 0)){
66038                         alt.push( "x-grid-row-alt");
66039                     }
66040                     if(r.dirty){
66041                         alt.push(" x-grid-dirty-row");
66042                     }
66043                     rp.cells = lcb;
66044                     if(this.getRowClass){
66045                         alt.push( this.getRowClass(r, rowIndex));
66046                     }
66047                     if (hasListener) {
66048                         rowcfg = {
66049                              
66050                             record: r,
66051                             rowIndex : rowIndex,
66052                             rowClass : ''
66053                         };
66054                         this.grid.fireEvent('rowclass', this, rowcfg);
66055                         alt.push(rowcfg.rowClass);
66056                     }
66057                     
66058                     rp.alt = alt.join(" ");
66059                     rp.cells = lcb.join("");
66060                     lbuf[lbuf.length] = rt.apply(rp);
66061                     rp.cells = cb.join("");
66062                     buf[buf.length] =  rt.apply(rp);
66063                 }
66064                 return [lbuf.join(""), buf.join("")];
66065             },
66066
66067     renderBody : function(){
66068         var markup = this.renderRows();
66069         var bt = this.templates.body;
66070         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
66071     },
66072
66073     /**
66074      * Refreshes the grid
66075      * @param {Boolean} headersToo
66076      */
66077     refresh : function(headersToo){
66078         this.fireEvent("beforerefresh", this);
66079         this.grid.stopEditing();
66080         var result = this.renderBody();
66081         this.lockedBody.update(result[0]);
66082         this.mainBody.update(result[1]);
66083         if(headersToo === true){
66084             this.updateHeaders();
66085             this.updateColumns();
66086             this.updateSplitters();
66087             this.updateHeaderSortState();
66088         }
66089         this.syncRowHeights();
66090         this.layout();
66091         this.fireEvent("refresh", this);
66092     },
66093
66094     handleColumnMove : function(cm, oldIndex, newIndex){
66095         this.indexMap = null;
66096         var s = this.getScrollState();
66097         this.refresh(true);
66098         this.restoreScroll(s);
66099         this.afterMove(newIndex);
66100     },
66101
66102     afterMove : function(colIndex){
66103         if(this.enableMoveAnim && Roo.enableFx){
66104             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
66105         }
66106         // if multisort - fix sortOrder, and reload..
66107         if (this.grid.dataSource.multiSort) {
66108             // the we can call sort again..
66109             var dm = this.grid.dataSource;
66110             var cm = this.grid.colModel;
66111             var so = [];
66112             for(var i = 0; i < cm.config.length; i++ ) {
66113                 
66114                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
66115                     continue; // dont' bother, it's not in sort list or being set.
66116                 }
66117                 
66118                 so.push(cm.config[i].dataIndex);
66119             };
66120             dm.sortOrder = so;
66121             dm.load(dm.lastOptions);
66122             
66123             
66124         }
66125         
66126     },
66127
66128     updateCell : function(dm, rowIndex, dataIndex){
66129         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
66130         if(typeof colIndex == "undefined"){ // not present in grid
66131             return;
66132         }
66133         var cm = this.grid.colModel;
66134         var cell = this.getCell(rowIndex, colIndex);
66135         var cellText = this.getCellText(rowIndex, colIndex);
66136
66137         var p = {
66138             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
66139             id : cm.getColumnId(colIndex),
66140             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
66141         };
66142         var renderer = cm.getRenderer(colIndex);
66143         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
66144         if(typeof val == "undefined" || val === "") {
66145             val = "&#160;";
66146         }
66147         cellText.innerHTML = val;
66148         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
66149         this.syncRowHeights(rowIndex, rowIndex);
66150     },
66151
66152     calcColumnWidth : function(colIndex, maxRowsToMeasure){
66153         var maxWidth = 0;
66154         if(this.grid.autoSizeHeaders){
66155             var h = this.getHeaderCellMeasure(colIndex);
66156             maxWidth = Math.max(maxWidth, h.scrollWidth);
66157         }
66158         var tb, index;
66159         if(this.cm.isLocked(colIndex)){
66160             tb = this.getLockedTable();
66161             index = colIndex;
66162         }else{
66163             tb = this.getBodyTable();
66164             index = colIndex - this.cm.getLockedCount();
66165         }
66166         if(tb && tb.rows){
66167             var rows = tb.rows;
66168             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
66169             for(var i = 0; i < stopIndex; i++){
66170                 var cell = rows[i].childNodes[index].firstChild;
66171                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
66172             }
66173         }
66174         return maxWidth + /*margin for error in IE*/ 5;
66175     },
66176     /**
66177      * Autofit a column to its content.
66178      * @param {Number} colIndex
66179      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
66180      */
66181      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
66182          if(this.cm.isHidden(colIndex)){
66183              return; // can't calc a hidden column
66184          }
66185         if(forceMinSize){
66186             var cid = this.cm.getColumnId(colIndex);
66187             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
66188            if(this.grid.autoSizeHeaders){
66189                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
66190            }
66191         }
66192         var newWidth = this.calcColumnWidth(colIndex);
66193         this.cm.setColumnWidth(colIndex,
66194             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
66195         if(!suppressEvent){
66196             this.grid.fireEvent("columnresize", colIndex, newWidth);
66197         }
66198     },
66199
66200     /**
66201      * Autofits all columns to their content and then expands to fit any extra space in the grid
66202      */
66203      autoSizeColumns : function(){
66204         var cm = this.grid.colModel;
66205         var colCount = cm.getColumnCount();
66206         for(var i = 0; i < colCount; i++){
66207             this.autoSizeColumn(i, true, true);
66208         }
66209         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
66210             this.fitColumns();
66211         }else{
66212             this.updateColumns();
66213             this.layout();
66214         }
66215     },
66216
66217     /**
66218      * Autofits all columns to the grid's width proportionate with their current size
66219      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
66220      */
66221     fitColumns : function(reserveScrollSpace){
66222         var cm = this.grid.colModel;
66223         var colCount = cm.getColumnCount();
66224         var cols = [];
66225         var width = 0;
66226         var i, w;
66227         for (i = 0; i < colCount; i++){
66228             if(!cm.isHidden(i) && !cm.isFixed(i)){
66229                 w = cm.getColumnWidth(i);
66230                 cols.push(i);
66231                 cols.push(w);
66232                 width += w;
66233             }
66234         }
66235         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
66236         if(reserveScrollSpace){
66237             avail -= 17;
66238         }
66239         var frac = (avail - cm.getTotalWidth())/width;
66240         while (cols.length){
66241             w = cols.pop();
66242             i = cols.pop();
66243             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
66244         }
66245         this.updateColumns();
66246         this.layout();
66247     },
66248
66249     onRowSelect : function(rowIndex){
66250         var row = this.getRowComposite(rowIndex);
66251         row.addClass("x-grid-row-selected");
66252     },
66253
66254     onRowDeselect : function(rowIndex){
66255         var row = this.getRowComposite(rowIndex);
66256         row.removeClass("x-grid-row-selected");
66257     },
66258
66259     onCellSelect : function(row, col){
66260         var cell = this.getCell(row, col);
66261         if(cell){
66262             Roo.fly(cell).addClass("x-grid-cell-selected");
66263         }
66264     },
66265
66266     onCellDeselect : function(row, col){
66267         var cell = this.getCell(row, col);
66268         if(cell){
66269             Roo.fly(cell).removeClass("x-grid-cell-selected");
66270         }
66271     },
66272
66273     updateHeaderSortState : function(){
66274         
66275         // sort state can be single { field: xxx, direction : yyy}
66276         // or   { xxx=>ASC , yyy : DESC ..... }
66277         
66278         var mstate = {};
66279         if (!this.ds.multiSort) { 
66280             var state = this.ds.getSortState();
66281             if(!state){
66282                 return;
66283             }
66284             mstate[state.field] = state.direction;
66285             // FIXME... - this is not used here.. but might be elsewhere..
66286             this.sortState = state;
66287             
66288         } else {
66289             mstate = this.ds.sortToggle;
66290         }
66291         //remove existing sort classes..
66292         
66293         var sc = this.sortClasses;
66294         var hds = this.el.select(this.headerSelector).removeClass(sc);
66295         
66296         for(var f in mstate) {
66297         
66298             var sortColumn = this.cm.findColumnIndex(f);
66299             
66300             if(sortColumn != -1){
66301                 var sortDir = mstate[f];        
66302                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
66303             }
66304         }
66305         
66306          
66307         
66308     },
66309
66310
66311     handleHeaderClick : function(g, index,e){
66312         
66313         Roo.log("header click");
66314         
66315         if (Roo.isTouch) {
66316             // touch events on header are handled by context
66317             this.handleHdCtx(g,index,e);
66318             return;
66319         }
66320         
66321         
66322         if(this.headersDisabled){
66323             return;
66324         }
66325         var dm = g.dataSource, cm = g.colModel;
66326         if(!cm.isSortable(index)){
66327             return;
66328         }
66329         g.stopEditing();
66330         
66331         if (dm.multiSort) {
66332             // update the sortOrder
66333             var so = [];
66334             for(var i = 0; i < cm.config.length; i++ ) {
66335                 
66336                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
66337                     continue; // dont' bother, it's not in sort list or being set.
66338                 }
66339                 
66340                 so.push(cm.config[i].dataIndex);
66341             };
66342             dm.sortOrder = so;
66343         }
66344         
66345         
66346         dm.sort(cm.getDataIndex(index));
66347     },
66348
66349
66350     destroy : function(){
66351         if(this.colMenu){
66352             this.colMenu.removeAll();
66353             Roo.menu.MenuMgr.unregister(this.colMenu);
66354             this.colMenu.getEl().remove();
66355             delete this.colMenu;
66356         }
66357         if(this.hmenu){
66358             this.hmenu.removeAll();
66359             Roo.menu.MenuMgr.unregister(this.hmenu);
66360             this.hmenu.getEl().remove();
66361             delete this.hmenu;
66362         }
66363         if(this.grid.enableColumnMove){
66364             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
66365             if(dds){
66366                 for(var dd in dds){
66367                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
66368                         var elid = dds[dd].dragElId;
66369                         dds[dd].unreg();
66370                         Roo.get(elid).remove();
66371                     } else if(dds[dd].config.isTarget){
66372                         dds[dd].proxyTop.remove();
66373                         dds[dd].proxyBottom.remove();
66374                         dds[dd].unreg();
66375                     }
66376                     if(Roo.dd.DDM.locationCache[dd]){
66377                         delete Roo.dd.DDM.locationCache[dd];
66378                     }
66379                 }
66380                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
66381             }
66382         }
66383         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
66384         this.bind(null, null);
66385         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
66386     },
66387
66388     handleLockChange : function(){
66389         this.refresh(true);
66390     },
66391
66392     onDenyColumnLock : function(){
66393
66394     },
66395
66396     onDenyColumnHide : function(){
66397
66398     },
66399
66400     handleHdMenuClick : function(item){
66401         var index = this.hdCtxIndex;
66402         var cm = this.cm, ds = this.ds;
66403         switch(item.id){
66404             case "asc":
66405                 ds.sort(cm.getDataIndex(index), "ASC");
66406                 break;
66407             case "desc":
66408                 ds.sort(cm.getDataIndex(index), "DESC");
66409                 break;
66410             case "lock":
66411                 var lc = cm.getLockedCount();
66412                 if(cm.getColumnCount(true) <= lc+1){
66413                     this.onDenyColumnLock();
66414                     return;
66415                 }
66416                 if(lc != index){
66417                     cm.setLocked(index, true, true);
66418                     cm.moveColumn(index, lc);
66419                     this.grid.fireEvent("columnmove", index, lc);
66420                 }else{
66421                     cm.setLocked(index, true);
66422                 }
66423             break;
66424             case "unlock":
66425                 var lc = cm.getLockedCount();
66426                 if((lc-1) != index){
66427                     cm.setLocked(index, false, true);
66428                     cm.moveColumn(index, lc-1);
66429                     this.grid.fireEvent("columnmove", index, lc-1);
66430                 }else{
66431                     cm.setLocked(index, false);
66432                 }
66433             break;
66434             case 'wider': // used to expand cols on touch..
66435             case 'narrow':
66436                 var cw = cm.getColumnWidth(index);
66437                 cw += (item.id == 'wider' ? 1 : -1) * 50;
66438                 cw = Math.max(0, cw);
66439                 cw = Math.min(cw,4000);
66440                 cm.setColumnWidth(index, cw);
66441                 break;
66442                 
66443             default:
66444                 index = cm.getIndexById(item.id.substr(4));
66445                 if(index != -1){
66446                     if(item.checked && cm.getColumnCount(true) <= 1){
66447                         this.onDenyColumnHide();
66448                         return false;
66449                     }
66450                     cm.setHidden(index, item.checked);
66451                 }
66452         }
66453         return true;
66454     },
66455
66456     beforeColMenuShow : function(){
66457         var cm = this.cm,  colCount = cm.getColumnCount();
66458         this.colMenu.removeAll();
66459         
66460         var items = [];
66461         for(var i = 0; i < colCount; i++){
66462             items.push({
66463                 id: "col-"+cm.getColumnId(i),
66464                 text: cm.getColumnHeader(i),
66465                 checked: !cm.isHidden(i),
66466                 hideOnClick:false
66467             });
66468         }
66469         
66470         if (this.grid.sortColMenu) {
66471             items.sort(function(a,b) {
66472                 if (a.text == b.text) {
66473                     return 0;
66474                 }
66475                 return a.text.toUpperCase() > b.text.toUpperCase() ? 1 : -1;
66476             });
66477         }
66478         
66479         for(var i = 0; i < colCount; i++){
66480             this.colMenu.add(new Roo.menu.CheckItem(items[i]));
66481         }
66482     },
66483
66484     handleHdCtx : function(g, index, e){
66485         e.stopEvent();
66486         var hd = this.getHeaderCell(index);
66487         this.hdCtxIndex = index;
66488         var ms = this.hmenu.items, cm = this.cm;
66489         ms.get("asc").setDisabled(!cm.isSortable(index));
66490         ms.get("desc").setDisabled(!cm.isSortable(index));
66491         if(this.grid.enableColLock !== false){
66492             ms.get("lock").setDisabled(cm.isLocked(index));
66493             ms.get("unlock").setDisabled(!cm.isLocked(index));
66494         }
66495         this.hmenu.show(hd, "tl-bl");
66496     },
66497
66498     handleHdOver : function(e){
66499         var hd = this.findHeaderCell(e.getTarget());
66500         if(hd && !this.headersDisabled){
66501             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
66502                this.fly(hd).addClass("x-grid-hd-over");
66503             }
66504         }
66505     },
66506
66507     handleHdOut : function(e){
66508         var hd = this.findHeaderCell(e.getTarget());
66509         if(hd){
66510             this.fly(hd).removeClass("x-grid-hd-over");
66511         }
66512     },
66513
66514     handleSplitDblClick : function(e, t){
66515         var i = this.getCellIndex(t);
66516         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
66517             this.autoSizeColumn(i, true);
66518             this.layout();
66519         }
66520     },
66521
66522     render : function(){
66523
66524         var cm = this.cm;
66525         var colCount = cm.getColumnCount();
66526
66527         if(this.grid.monitorWindowResize === true){
66528             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
66529         }
66530         var header = this.renderHeaders();
66531         var body = this.templates.body.apply({rows:""});
66532         var html = this.templates.master.apply({
66533             lockedBody: body,
66534             body: body,
66535             lockedHeader: header[0],
66536             header: header[1]
66537         });
66538
66539         //this.updateColumns();
66540
66541         this.grid.getGridEl().dom.innerHTML = html;
66542
66543         this.initElements();
66544         
66545         // a kludge to fix the random scolling effect in webkit
66546         this.el.on("scroll", function() {
66547             this.el.dom.scrollTop=0; // hopefully not recursive..
66548         },this);
66549
66550         this.scroller.on("scroll", this.handleScroll, this);
66551         this.lockedBody.on("mousewheel", this.handleWheel, this);
66552         this.mainBody.on("mousewheel", this.handleWheel, this);
66553
66554         this.mainHd.on("mouseover", this.handleHdOver, this);
66555         this.mainHd.on("mouseout", this.handleHdOut, this);
66556         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
66557                 {delegate: "."+this.splitClass});
66558
66559         this.lockedHd.on("mouseover", this.handleHdOver, this);
66560         this.lockedHd.on("mouseout", this.handleHdOut, this);
66561         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
66562                 {delegate: "."+this.splitClass});
66563
66564         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
66565             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
66566         }
66567
66568         this.updateSplitters();
66569
66570         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
66571             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
66572             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
66573         }
66574
66575         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
66576             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
66577             this.hmenu.add(
66578                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
66579                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
66580             );
66581             if(this.grid.enableColLock !== false){
66582                 this.hmenu.add('-',
66583                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
66584                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
66585                 );
66586             }
66587             if (Roo.isTouch) {
66588                  this.hmenu.add('-',
66589                     {id:"wider", text: this.columnsWiderText},
66590                     {id:"narrow", text: this.columnsNarrowText }
66591                 );
66592                 
66593                  
66594             }
66595             
66596             if(this.grid.enableColumnHide !== false){
66597
66598                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
66599                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
66600                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
66601
66602                 this.hmenu.add('-',
66603                     {id:"columns", text: this.columnsText, menu: this.colMenu}
66604                 );
66605             }
66606             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
66607
66608             this.grid.on("headercontextmenu", this.handleHdCtx, this);
66609         }
66610
66611         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
66612             this.dd = new Roo.grid.GridDragZone(this.grid, {
66613                 ddGroup : this.grid.ddGroup || 'GridDD'
66614             });
66615             
66616         }
66617
66618         /*
66619         for(var i = 0; i < colCount; i++){
66620             if(cm.isHidden(i)){
66621                 this.hideColumn(i);
66622             }
66623             if(cm.config[i].align){
66624                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
66625                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
66626             }
66627         }*/
66628         
66629         this.updateHeaderSortState();
66630
66631         this.beforeInitialResize();
66632         this.layout(true);
66633
66634         // two part rendering gives faster view to the user
66635         this.renderPhase2.defer(1, this);
66636     },
66637
66638     renderPhase2 : function(){
66639         // render the rows now
66640         this.refresh();
66641         if(this.grid.autoSizeColumns){
66642             this.autoSizeColumns();
66643         }
66644     },
66645
66646     beforeInitialResize : function(){
66647
66648     },
66649
66650     onColumnSplitterMoved : function(i, w){
66651         this.userResized = true;
66652         var cm = this.grid.colModel;
66653         cm.setColumnWidth(i, w, true);
66654         var cid = cm.getColumnId(i);
66655         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
66656         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
66657         this.updateSplitters();
66658         this.layout();
66659         this.grid.fireEvent("columnresize", i, w);
66660     },
66661
66662     syncRowHeights : function(startIndex, endIndex){
66663         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
66664             startIndex = startIndex || 0;
66665             var mrows = this.getBodyTable().rows;
66666             var lrows = this.getLockedTable().rows;
66667             var len = mrows.length-1;
66668             endIndex = Math.min(endIndex || len, len);
66669             for(var i = startIndex; i <= endIndex; i++){
66670                 var m = mrows[i], l = lrows[i];
66671                 var h = Math.max(m.offsetHeight, l.offsetHeight);
66672                 m.style.height = l.style.height = h + "px";
66673             }
66674         }
66675     },
66676
66677     layout : function(initialRender, is2ndPass)
66678     {
66679         var g = this.grid;
66680         var auto = g.autoHeight;
66681         var scrollOffset = 16;
66682         var c = g.getGridEl(), cm = this.cm,
66683                 expandCol = g.autoExpandColumn,
66684                 gv = this;
66685         //c.beginMeasure();
66686
66687         if(!c.dom.offsetWidth){ // display:none?
66688             if(initialRender){
66689                 this.lockedWrap.show();
66690                 this.mainWrap.show();
66691             }
66692             return;
66693         }
66694
66695         var hasLock = this.cm.isLocked(0);
66696
66697         var tbh = this.headerPanel.getHeight();
66698         var bbh = this.footerPanel.getHeight();
66699
66700         if(auto){
66701             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
66702             var newHeight = ch + c.getBorderWidth("tb");
66703             if(g.maxHeight){
66704                 newHeight = Math.min(g.maxHeight, newHeight);
66705             }
66706             c.setHeight(newHeight);
66707         }
66708
66709         if(g.autoWidth){
66710             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
66711         }
66712
66713         var s = this.scroller;
66714
66715         var csize = c.getSize(true);
66716
66717         this.el.setSize(csize.width, csize.height);
66718
66719         this.headerPanel.setWidth(csize.width);
66720         this.footerPanel.setWidth(csize.width);
66721
66722         var hdHeight = this.mainHd.getHeight();
66723         var vw = csize.width;
66724         var vh = csize.height - (tbh + bbh);
66725
66726         s.setSize(vw, vh);
66727
66728         var bt = this.getBodyTable();
66729         
66730         if(cm.getLockedCount() == cm.config.length){
66731             bt = this.getLockedTable();
66732         }
66733         
66734         var ltWidth = hasLock ?
66735                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
66736
66737         var scrollHeight = bt.offsetHeight;
66738         var scrollWidth = ltWidth + bt.offsetWidth;
66739         var vscroll = false, hscroll = false;
66740
66741         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
66742
66743         var lw = this.lockedWrap, mw = this.mainWrap;
66744         var lb = this.lockedBody, mb = this.mainBody;
66745
66746         setTimeout(function(){
66747             var t = s.dom.offsetTop;
66748             var w = s.dom.clientWidth,
66749                 h = s.dom.clientHeight;
66750
66751             lw.setTop(t);
66752             lw.setSize(ltWidth, h);
66753
66754             mw.setLeftTop(ltWidth, t);
66755             mw.setSize(w-ltWidth, h);
66756
66757             lb.setHeight(h-hdHeight);
66758             mb.setHeight(h-hdHeight);
66759
66760             if(is2ndPass !== true && !gv.userResized && expandCol){
66761                 // high speed resize without full column calculation
66762                 
66763                 var ci = cm.getIndexById(expandCol);
66764                 if (ci < 0) {
66765                     ci = cm.findColumnIndex(expandCol);
66766                 }
66767                 ci = Math.max(0, ci); // make sure it's got at least the first col.
66768                 var expandId = cm.getColumnId(ci);
66769                 var  tw = cm.getTotalWidth(false);
66770                 var currentWidth = cm.getColumnWidth(ci);
66771                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
66772                 if(currentWidth != cw){
66773                     cm.setColumnWidth(ci, cw, true);
66774                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
66775                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
66776                     gv.updateSplitters();
66777                     gv.layout(false, true);
66778                 }
66779             }
66780
66781             if(initialRender){
66782                 lw.show();
66783                 mw.show();
66784             }
66785             //c.endMeasure();
66786         }, 10);
66787     },
66788
66789     onWindowResize : function(){
66790         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
66791             return;
66792         }
66793         this.layout();
66794     },
66795
66796     appendFooter : function(parentEl){
66797         return null;
66798     },
66799
66800     sortAscText : "Sort Ascending",
66801     sortDescText : "Sort Descending",
66802     lockText : "Lock Column",
66803     unlockText : "Unlock Column",
66804     columnsText : "Columns",
66805  
66806     columnsWiderText : "Wider",
66807     columnsNarrowText : "Thinner"
66808 });
66809
66810
66811 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
66812     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
66813     this.proxy.el.addClass('x-grid3-col-dd');
66814 };
66815
66816 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
66817     handleMouseDown : function(e){
66818
66819     },
66820
66821     callHandleMouseDown : function(e){
66822         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
66823     }
66824 });
66825 /*
66826  * Based on:
66827  * Ext JS Library 1.1.1
66828  * Copyright(c) 2006-2007, Ext JS, LLC.
66829  *
66830  * Originally Released Under LGPL - original licence link has changed is not relivant.
66831  *
66832  * Fork - LGPL
66833  * <script type="text/javascript">
66834  */
66835  /**
66836  * @extends Roo.dd.DDProxy
66837  * @class Roo.grid.SplitDragZone
66838  * Support for Column Header resizing
66839  * @constructor
66840  * @param {Object} config
66841  */
66842 // private
66843 // This is a support class used internally by the Grid components
66844 Roo.grid.SplitDragZone = function(grid, hd, hd2){
66845     this.grid = grid;
66846     this.view = grid.getView();
66847     this.proxy = this.view.resizeProxy;
66848     Roo.grid.SplitDragZone.superclass.constructor.call(
66849         this,
66850         hd, // ID
66851         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
66852         {  // CONFIG
66853             dragElId : Roo.id(this.proxy.dom),
66854             resizeFrame:false
66855         }
66856     );
66857     
66858     this.setHandleElId(Roo.id(hd));
66859     if (hd2 !== false) {
66860         this.setOuterHandleElId(Roo.id(hd2));
66861     }
66862     
66863     this.scroll = false;
66864 };
66865 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
66866     fly: Roo.Element.fly,
66867
66868     b4StartDrag : function(x, y){
66869         this.view.headersDisabled = true;
66870         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
66871                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
66872         );
66873         this.proxy.setHeight(h);
66874         
66875         // for old system colWidth really stored the actual width?
66876         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
66877         // which in reality did not work.. - it worked only for fixed sizes
66878         // for resizable we need to use actual sizes.
66879         var w = this.cm.getColumnWidth(this.cellIndex);
66880         if (!this.view.mainWrap) {
66881             // bootstrap.
66882             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
66883         }
66884         
66885         
66886         
66887         // this was w-this.grid.minColumnWidth;
66888         // doesnt really make sense? - w = thie curren width or the rendered one?
66889         var minw = Math.max(w-this.grid.minColumnWidth, 0);
66890         this.resetConstraints();
66891         this.setXConstraint(minw, 1000);
66892         this.setYConstraint(0, 0);
66893         this.minX = x - minw;
66894         this.maxX = x + 1000;
66895         this.startPos = x;
66896         if (!this.view.mainWrap) { // this is Bootstrap code..
66897             this.getDragEl().style.display='block';
66898         }
66899         
66900         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
66901     },
66902
66903
66904     handleMouseDown : function(e){
66905         ev = Roo.EventObject.setEvent(e);
66906         var t = this.fly(ev.getTarget());
66907         if(t.hasClass("x-grid-split")){
66908             this.cellIndex = this.view.getCellIndex(t.dom);
66909             this.split = t.dom;
66910             this.cm = this.grid.colModel;
66911             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
66912                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
66913             }
66914         }
66915     },
66916
66917     endDrag : function(e){
66918         this.view.headersDisabled = false;
66919         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
66920         var diff = endX - this.startPos;
66921         // 
66922         var w = this.cm.getColumnWidth(this.cellIndex);
66923         if (!this.view.mainWrap) {
66924             w = 0;
66925         }
66926         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
66927     },
66928
66929     autoOffset : function(){
66930         this.setDelta(0,0);
66931     }
66932 });/*
66933  * Based on:
66934  * Ext JS Library 1.1.1
66935  * Copyright(c) 2006-2007, Ext JS, LLC.
66936  *
66937  * Originally Released Under LGPL - original licence link has changed is not relivant.
66938  *
66939  * Fork - LGPL
66940  * <script type="text/javascript">
66941  */
66942  
66943 // private
66944 // This is a support class used internally by the Grid components
66945 Roo.grid.GridDragZone = function(grid, config){
66946     this.view = grid.getView();
66947     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
66948     if(this.view.lockedBody){
66949         this.setHandleElId(Roo.id(this.view.mainBody.dom));
66950         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
66951     }
66952     this.scroll = false;
66953     this.grid = grid;
66954     this.ddel = document.createElement('div');
66955     this.ddel.className = 'x-grid-dd-wrap';
66956 };
66957
66958 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
66959     ddGroup : "GridDD",
66960
66961     getDragData : function(e){
66962         var t = Roo.lib.Event.getTarget(e);
66963         var rowIndex = this.view.findRowIndex(t);
66964         var sm = this.grid.selModel;
66965             
66966         //Roo.log(rowIndex);
66967         
66968         if (sm.getSelectedCell) {
66969             // cell selection..
66970             if (!sm.getSelectedCell()) {
66971                 return false;
66972             }
66973             if (rowIndex != sm.getSelectedCell()[0]) {
66974                 return false;
66975             }
66976         
66977         }
66978         if (sm.getSelections && sm.getSelections().length < 1) {
66979             return false;
66980         }
66981         
66982         
66983         // before it used to all dragging of unseleted... - now we dont do that.
66984         if(rowIndex !== false){
66985             
66986             // if editorgrid.. 
66987             
66988             
66989             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
66990                
66991             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
66992               //  
66993             //}
66994             if (e.hasModifier()){
66995                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
66996             }
66997             
66998             Roo.log("getDragData");
66999             
67000             return {
67001                 grid: this.grid,
67002                 ddel: this.ddel,
67003                 rowIndex: rowIndex,
67004                 selections: sm.getSelections ? sm.getSelections() : (
67005                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
67006             };
67007         }
67008         return false;
67009     },
67010     
67011     
67012     onInitDrag : function(e){
67013         var data = this.dragData;
67014         this.ddel.innerHTML = this.grid.getDragDropText();
67015         this.proxy.update(this.ddel);
67016         // fire start drag?
67017     },
67018
67019     afterRepair : function(){
67020         this.dragging = false;
67021     },
67022
67023     getRepairXY : function(e, data){
67024         return false;
67025     },
67026
67027     onEndDrag : function(data, e){
67028         // fire end drag?
67029     },
67030
67031     onValidDrop : function(dd, e, id){
67032         // fire drag drop?
67033         this.hideProxy();
67034     },
67035
67036     beforeInvalidDrop : function(e, id){
67037
67038     }
67039 });/*
67040  * Based on:
67041  * Ext JS Library 1.1.1
67042  * Copyright(c) 2006-2007, Ext JS, LLC.
67043  *
67044  * Originally Released Under LGPL - original licence link has changed is not relivant.
67045  *
67046  * Fork - LGPL
67047  * <script type="text/javascript">
67048  */
67049  
67050
67051 /**
67052  * @class Roo.grid.ColumnModel
67053  * @extends Roo.util.Observable
67054  * This is the default implementation of a ColumnModel used by the Grid. It defines
67055  * the columns in the grid.
67056  * <br>Usage:<br>
67057  <pre><code>
67058  var colModel = new Roo.grid.ColumnModel([
67059         {header: "Ticker", width: 60, sortable: true, locked: true},
67060         {header: "Company Name", width: 150, sortable: true},
67061         {header: "Market Cap.", width: 100, sortable: true},
67062         {header: "$ Sales", width: 100, sortable: true, renderer: money},
67063         {header: "Employees", width: 100, sortable: true, resizable: false}
67064  ]);
67065  </code></pre>
67066  * <p>
67067  
67068  * The config options listed for this class are options which may appear in each
67069  * individual column definition.
67070  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
67071  * @constructor
67072  * @param {Object} config An Array of column config objects. See this class's
67073  * config objects for details.
67074 */
67075 Roo.grid.ColumnModel = function(config){
67076         /**
67077      * The config passed into the constructor
67078      */
67079     this.config = []; //config;
67080     this.lookup = {};
67081
67082     // if no id, create one
67083     // if the column does not have a dataIndex mapping,
67084     // map it to the order it is in the config
67085     for(var i = 0, len = config.length; i < len; i++){
67086         this.addColumn(config[i]);
67087         
67088     }
67089
67090     /**
67091      * The width of columns which have no width specified (defaults to 100)
67092      * @type Number
67093      */
67094     this.defaultWidth = 100;
67095
67096     /**
67097      * Default sortable of columns which have no sortable specified (defaults to false)
67098      * @type Boolean
67099      */
67100     this.defaultSortable = false;
67101
67102     this.addEvents({
67103         /**
67104              * @event widthchange
67105              * Fires when the width of a column changes.
67106              * @param {ColumnModel} this
67107              * @param {Number} columnIndex The column index
67108              * @param {Number} newWidth The new width
67109              */
67110             "widthchange": true,
67111         /**
67112              * @event headerchange
67113              * Fires when the text of a header changes.
67114              * @param {ColumnModel} this
67115              * @param {Number} columnIndex The column index
67116              * @param {Number} newText The new header text
67117              */
67118             "headerchange": true,
67119         /**
67120              * @event hiddenchange
67121              * Fires when a column is hidden or "unhidden".
67122              * @param {ColumnModel} this
67123              * @param {Number} columnIndex The column index
67124              * @param {Boolean} hidden true if hidden, false otherwise
67125              */
67126             "hiddenchange": true,
67127             /**
67128          * @event columnmoved
67129          * Fires when a column is moved.
67130          * @param {ColumnModel} this
67131          * @param {Number} oldIndex
67132          * @param {Number} newIndex
67133          */
67134         "columnmoved" : true,
67135         /**
67136          * @event columlockchange
67137          * Fires when a column's locked state is changed
67138          * @param {ColumnModel} this
67139          * @param {Number} colIndex
67140          * @param {Boolean} locked true if locked
67141          */
67142         "columnlockchange" : true
67143     });
67144     Roo.grid.ColumnModel.superclass.constructor.call(this);
67145 };
67146 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
67147     /**
67148      * @cfg {String} header [required] The header text to display in the Grid view.
67149      */
67150         /**
67151      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
67152      */
67153         /**
67154      * @cfg {String} smHeader Header at Bootsrap Small width
67155      */
67156         /**
67157      * @cfg {String} mdHeader Header at Bootsrap Medium width
67158      */
67159         /**
67160      * @cfg {String} lgHeader Header at Bootsrap Large width
67161      */
67162         /**
67163      * @cfg {String} xlHeader Header at Bootsrap extra Large width
67164      */
67165     /**
67166      * @cfg {String} dataIndex  The name of the field in the grid's {@link Roo.data.Store}'s
67167      * {@link Roo.data.Record} definition from which to draw the column's value. If not
67168      * specified, the column's index is used as an index into the Record's data Array.
67169      */
67170     /**
67171      * @cfg {Number} width  The initial width in pixels of the column. Using this
67172      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
67173      */
67174     /**
67175      * @cfg {Boolean} sortable True if sorting is to be allowed on this column.
67176      * Defaults to the value of the {@link #defaultSortable} property.
67177      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
67178      */
67179     /**
67180      * @cfg {Boolean} locked  True to lock the column in place while scrolling the Grid.  Defaults to false.
67181      */
67182     /**
67183      * @cfg {Boolean} fixed  True if the column width cannot be changed.  Defaults to false.
67184      */
67185     /**
67186      * @cfg {Boolean} resizable  False to disable column resizing. Defaults to true.
67187      */
67188     /**
67189      * @cfg {Boolean} hidden  True to hide the column. Defaults to false.
67190      */
67191     /**
67192      * @cfg {Function} renderer A function used to generate HTML markup for a cell
67193      * given the cell's data value. See {@link #setRenderer}. If not specified, the
67194      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
67195      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
67196      */
67197        /**
67198      * @cfg {Roo.grid.GridEditor} editor  For grid editors - returns the grid editor 
67199      */
67200     /**
67201      * @cfg {String} align (left|right) Set the CSS text-align property of the column.  Defaults to undefined (left).
67202      */
67203     /**
67204      * @cfg {String} valign (top|bottom|middle) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined (middle)
67205      */
67206     /**
67207      * @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)
67208      */
67209     /**
67210      * @cfg {String} tooltip mouse over tooltip text
67211      */
67212     /**
67213      * @cfg {Number} xs  can be '0' for hidden at this size (number less than 12)
67214      */
67215     /**
67216      * @cfg {Number} sm can be '0' for hidden at this size (number less than 12)
67217      */
67218     /**
67219      * @cfg {Number} md can be '0' for hidden at this size (number less than 12)
67220      */
67221     /**
67222      * @cfg {Number} lg   can be '0' for hidden at this size (number less than 12)
67223      */
67224         /**
67225      * @cfg {Number} xl   can be '0' for hidden at this size (number less than 12)
67226      */
67227     /**
67228      * Returns the id of the column at the specified index.
67229      * @param {Number} index The column index
67230      * @return {String} the id
67231      */
67232     getColumnId : function(index){
67233         return this.config[index].id;
67234     },
67235
67236     /**
67237      * Returns the column for a specified id.
67238      * @param {String} id The column id
67239      * @return {Object} the column
67240      */
67241     getColumnById : function(id){
67242         return this.lookup[id];
67243     },
67244
67245     
67246     /**
67247      * Returns the column Object for a specified dataIndex.
67248      * @param {String} dataIndex The column dataIndex
67249      * @return {Object|Boolean} the column or false if not found
67250      */
67251     getColumnByDataIndex: function(dataIndex){
67252         var index = this.findColumnIndex(dataIndex);
67253         return index > -1 ? this.config[index] : false;
67254     },
67255     
67256     /**
67257      * Returns the index for a specified column id.
67258      * @param {String} id The column id
67259      * @return {Number} the index, or -1 if not found
67260      */
67261     getIndexById : function(id){
67262         for(var i = 0, len = this.config.length; i < len; i++){
67263             if(this.config[i].id == id){
67264                 return i;
67265             }
67266         }
67267         return -1;
67268     },
67269     
67270     /**
67271      * Returns the index for a specified column dataIndex.
67272      * @param {String} dataIndex The column dataIndex
67273      * @return {Number} the index, or -1 if not found
67274      */
67275     
67276     findColumnIndex : function(dataIndex){
67277         for(var i = 0, len = this.config.length; i < len; i++){
67278             if(this.config[i].dataIndex == dataIndex){
67279                 return i;
67280             }
67281         }
67282         return -1;
67283     },
67284     
67285     
67286     moveColumn : function(oldIndex, newIndex){
67287         var c = this.config[oldIndex];
67288         this.config.splice(oldIndex, 1);
67289         this.config.splice(newIndex, 0, c);
67290         this.dataMap = null;
67291         this.fireEvent("columnmoved", this, oldIndex, newIndex);
67292     },
67293
67294     isLocked : function(colIndex){
67295         return this.config[colIndex].locked === true;
67296     },
67297
67298     setLocked : function(colIndex, value, suppressEvent){
67299         if(this.isLocked(colIndex) == value){
67300             return;
67301         }
67302         this.config[colIndex].locked = value;
67303         if(!suppressEvent){
67304             this.fireEvent("columnlockchange", this, colIndex, value);
67305         }
67306     },
67307
67308     getTotalLockedWidth : function(){
67309         var totalWidth = 0;
67310         for(var i = 0; i < this.config.length; i++){
67311             if(this.isLocked(i) && !this.isHidden(i)){
67312                 this.totalWidth += this.getColumnWidth(i);
67313             }
67314         }
67315         return totalWidth;
67316     },
67317
67318     getLockedCount : function(){
67319         for(var i = 0, len = this.config.length; i < len; i++){
67320             if(!this.isLocked(i)){
67321                 return i;
67322             }
67323         }
67324         
67325         return this.config.length;
67326     },
67327
67328     /**
67329      * Returns the number of columns.
67330      * @return {Number}
67331      */
67332     getColumnCount : function(visibleOnly){
67333         if(visibleOnly === true){
67334             var c = 0;
67335             for(var i = 0, len = this.config.length; i < len; i++){
67336                 if(!this.isHidden(i)){
67337                     c++;
67338                 }
67339             }
67340             return c;
67341         }
67342         return this.config.length;
67343     },
67344
67345     /**
67346      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
67347      * @param {Function} fn
67348      * @param {Object} scope (optional)
67349      * @return {Array} result
67350      */
67351     getColumnsBy : function(fn, scope){
67352         var r = [];
67353         for(var i = 0, len = this.config.length; i < len; i++){
67354             var c = this.config[i];
67355             if(fn.call(scope||this, c, i) === true){
67356                 r[r.length] = c;
67357             }
67358         }
67359         return r;
67360     },
67361
67362     /**
67363      * Returns true if the specified column is sortable.
67364      * @param {Number} col The column index
67365      * @return {Boolean}
67366      */
67367     isSortable : function(col){
67368         if(typeof this.config[col].sortable == "undefined"){
67369             return this.defaultSortable;
67370         }
67371         return this.config[col].sortable;
67372     },
67373
67374     /**
67375      * Returns the rendering (formatting) function defined for the column.
67376      * @param {Number} col The column index.
67377      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
67378      */
67379     getRenderer : function(col){
67380         if(!this.config[col].renderer){
67381             return Roo.grid.ColumnModel.defaultRenderer;
67382         }
67383         return this.config[col].renderer;
67384     },
67385
67386     /**
67387      * Sets the rendering (formatting) function for a column.
67388      * @param {Number} col The column index
67389      * @param {Function} fn The function to use to process the cell's raw data
67390      * to return HTML markup for the grid view. The render function is called with
67391      * the following parameters:<ul>
67392      * <li>Data value.</li>
67393      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
67394      * <li>css A CSS style string to apply to the table cell.</li>
67395      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
67396      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
67397      * <li>Row index</li>
67398      * <li>Column index</li>
67399      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
67400      */
67401     setRenderer : function(col, fn){
67402         this.config[col].renderer = fn;
67403     },
67404
67405     /**
67406      * Returns the width for the specified column.
67407      * @param {Number} col The column index
67408      * @param (optional) {String} gridSize bootstrap width size.
67409      * @return {Number}
67410      */
67411     getColumnWidth : function(col, gridSize)
67412         {
67413                 var cfg = this.config[col];
67414                 
67415                 if (typeof(gridSize) == 'undefined') {
67416                         return cfg.width * 1 || this.defaultWidth;
67417                 }
67418                 if (gridSize === false) { // if we set it..
67419                         return cfg.width || false;
67420                 }
67421                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
67422                 
67423                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
67424                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
67425                                 continue;
67426                         }
67427                         return cfg[ sizes[i] ];
67428                 }
67429                 return 1;
67430                 
67431     },
67432
67433     /**
67434      * Sets the width for a column.
67435      * @param {Number} col The column index
67436      * @param {Number} width The new width
67437      */
67438     setColumnWidth : function(col, width, suppressEvent){
67439         this.config[col].width = width;
67440         this.totalWidth = null;
67441         if(!suppressEvent){
67442              this.fireEvent("widthchange", this, col, width);
67443         }
67444     },
67445
67446     /**
67447      * Returns the total width of all columns.
67448      * @param {Boolean} includeHidden True to include hidden column widths
67449      * @return {Number}
67450      */
67451     getTotalWidth : function(includeHidden){
67452         if(!this.totalWidth){
67453             this.totalWidth = 0;
67454             for(var i = 0, len = this.config.length; i < len; i++){
67455                 if(includeHidden || !this.isHidden(i)){
67456                     this.totalWidth += this.getColumnWidth(i);
67457                 }
67458             }
67459         }
67460         return this.totalWidth;
67461     },
67462
67463     /**
67464      * Returns the header for the specified column.
67465      * @param {Number} col The column index
67466      * @return {String}
67467      */
67468     getColumnHeader : function(col){
67469         return this.config[col].header;
67470     },
67471
67472     /**
67473      * Sets the header for a column.
67474      * @param {Number} col The column index
67475      * @param {String} header The new header
67476      */
67477     setColumnHeader : function(col, header){
67478         this.config[col].header = header;
67479         this.fireEvent("headerchange", this, col, header);
67480     },
67481
67482     /**
67483      * Returns the tooltip for the specified column.
67484      * @param {Number} col The column index
67485      * @return {String}
67486      */
67487     getColumnTooltip : function(col){
67488             return this.config[col].tooltip;
67489     },
67490     /**
67491      * Sets the tooltip for a column.
67492      * @param {Number} col The column index
67493      * @param {String} tooltip The new tooltip
67494      */
67495     setColumnTooltip : function(col, tooltip){
67496             this.config[col].tooltip = tooltip;
67497     },
67498
67499     /**
67500      * Returns the dataIndex for the specified column.
67501      * @param {Number} col The column index
67502      * @return {Number}
67503      */
67504     getDataIndex : function(col){
67505         return this.config[col].dataIndex;
67506     },
67507
67508     /**
67509      * Sets the dataIndex for a column.
67510      * @param {Number} col The column index
67511      * @param {Number} dataIndex The new dataIndex
67512      */
67513     setDataIndex : function(col, dataIndex){
67514         this.config[col].dataIndex = dataIndex;
67515     },
67516
67517     
67518     
67519     /**
67520      * Returns true if the cell is editable.
67521      * @param {Number} colIndex The column index
67522      * @param {Number} rowIndex The row index - this is nto actually used..?
67523      * @return {Boolean}
67524      */
67525     isCellEditable : function(colIndex, rowIndex){
67526         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
67527     },
67528
67529     /**
67530      * Returns the editor defined for the cell/column.
67531      * return false or null to disable editing.
67532      * @param {Number} colIndex The column index
67533      * @param {Number} rowIndex The row index
67534      * @return {Object}
67535      */
67536     getCellEditor : function(colIndex, rowIndex){
67537         return this.config[colIndex].editor;
67538     },
67539
67540     /**
67541      * Sets if a column is editable.
67542      * @param {Number} col The column index
67543      * @param {Boolean} editable True if the column is editable
67544      */
67545     setEditable : function(col, editable){
67546         this.config[col].editable = editable;
67547     },
67548
67549
67550     /**
67551      * Returns true if the column is hidden.
67552      * @param {Number} colIndex The column index
67553      * @return {Boolean}
67554      */
67555     isHidden : function(colIndex){
67556         return this.config[colIndex].hidden;
67557     },
67558
67559
67560     /**
67561      * Returns true if the column width cannot be changed
67562      */
67563     isFixed : function(colIndex){
67564         return this.config[colIndex].fixed;
67565     },
67566
67567     /**
67568      * Returns true if the column can be resized
67569      * @return {Boolean}
67570      */
67571     isResizable : function(colIndex){
67572         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
67573     },
67574     /**
67575      * Sets if a column is hidden.
67576      * @param {Number} colIndex The column index
67577      * @param {Boolean} hidden True if the column is hidden
67578      */
67579     setHidden : function(colIndex, hidden){
67580         this.config[colIndex].hidden = hidden;
67581         this.totalWidth = null;
67582         this.fireEvent("hiddenchange", this, colIndex, hidden);
67583     },
67584
67585     /**
67586      * Sets the editor for a column.
67587      * @param {Number} col The column index
67588      * @param {Object} editor The editor object
67589      */
67590     setEditor : function(col, editor){
67591         this.config[col].editor = editor;
67592     },
67593     /**
67594      * Add a column (experimental...) - defaults to adding to the end..
67595      * @param {Object} config 
67596     */
67597     addColumn : function(c)
67598     {
67599     
67600         var i = this.config.length;
67601         this.config[i] = c;
67602         
67603         if(typeof c.dataIndex == "undefined"){
67604             c.dataIndex = i;
67605         }
67606         if(typeof c.renderer == "string"){
67607             c.renderer = Roo.util.Format[c.renderer];
67608         }
67609         if(typeof c.id == "undefined"){
67610             c.id = Roo.id();
67611         }
67612         if(c.editor && c.editor.xtype){
67613             c.editor  = Roo.factory(c.editor, Roo.grid);
67614         }
67615         if(c.editor && c.editor.isFormField){
67616             c.editor = new Roo.grid.GridEditor(c.editor);
67617         }
67618         this.lookup[c.id] = c;
67619     }
67620     
67621 });
67622
67623 Roo.grid.ColumnModel.defaultRenderer = function(value)
67624 {
67625     if(typeof value == "object") {
67626         return value;
67627     }
67628         if(typeof value == "string" && value.length < 1){
67629             return "&#160;";
67630         }
67631     
67632         return String.format("{0}", value);
67633 };
67634
67635 // Alias for backwards compatibility
67636 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
67637 /*
67638  * Based on:
67639  * Ext JS Library 1.1.1
67640  * Copyright(c) 2006-2007, Ext JS, LLC.
67641  *
67642  * Originally Released Under LGPL - original licence link has changed is not relivant.
67643  *
67644  * Fork - LGPL
67645  * <script type="text/javascript">
67646  */
67647
67648 /**
67649  * @class Roo.grid.AbstractSelectionModel
67650  * @extends Roo.util.Observable
67651  * @abstract
67652  * Abstract base class for grid SelectionModels.  It provides the interface that should be
67653  * implemented by descendant classes.  This class should not be directly instantiated.
67654  * @constructor
67655  */
67656 Roo.grid.AbstractSelectionModel = function(){
67657     this.locked = false;
67658     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
67659 };
67660
67661 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
67662     /** @ignore Called by the grid automatically. Do not call directly. */
67663     init : function(grid){
67664         this.grid = grid;
67665         this.initEvents();
67666     },
67667
67668     /**
67669      * Locks the selections.
67670      */
67671     lock : function(){
67672         this.locked = true;
67673     },
67674
67675     /**
67676      * Unlocks the selections.
67677      */
67678     unlock : function(){
67679         this.locked = false;
67680     },
67681
67682     /**
67683      * Returns true if the selections are locked.
67684      * @return {Boolean}
67685      */
67686     isLocked : function(){
67687         return this.locked;
67688     }
67689 });/*
67690  * Based on:
67691  * Ext JS Library 1.1.1
67692  * Copyright(c) 2006-2007, Ext JS, LLC.
67693  *
67694  * Originally Released Under LGPL - original licence link has changed is not relivant.
67695  *
67696  * Fork - LGPL
67697  * <script type="text/javascript">
67698  */
67699 /**
67700  * @extends Roo.grid.AbstractSelectionModel
67701  * @class Roo.grid.RowSelectionModel
67702  * The default SelectionModel used by {@link Roo.grid.Grid}.
67703  * It supports multiple selections and keyboard selection/navigation. 
67704  * @constructor
67705  * @param {Object} config
67706  */
67707 Roo.grid.RowSelectionModel = function(config){
67708     Roo.apply(this, config);
67709     this.selections = new Roo.util.MixedCollection(false, function(o){
67710         return o.id;
67711     });
67712
67713     this.last = false;
67714     this.lastActive = false;
67715
67716     this.addEvents({
67717         /**
67718         * @event selectionchange
67719         * Fires when the selection changes
67720         * @param {SelectionModel} this
67721         */
67722        "selectionchange" : true,
67723        /**
67724         * @event afterselectionchange
67725         * Fires after the selection changes (eg. by key press or clicking)
67726         * @param {SelectionModel} this
67727         */
67728        "afterselectionchange" : true,
67729        /**
67730         * @event beforerowselect
67731         * Fires when a row is selected being selected, return false to cancel.
67732         * @param {SelectionModel} this
67733         * @param {Number} rowIndex The selected index
67734         * @param {Boolean} keepExisting False if other selections will be cleared
67735         */
67736        "beforerowselect" : true,
67737        /**
67738         * @event rowselect
67739         * Fires when a row is selected.
67740         * @param {SelectionModel} this
67741         * @param {Number} rowIndex The selected index
67742         * @param {Roo.data.Record} r The record
67743         */
67744        "rowselect" : true,
67745        /**
67746         * @event rowdeselect
67747         * Fires when a row is deselected.
67748         * @param {SelectionModel} this
67749         * @param {Number} rowIndex The selected index
67750         */
67751         "rowdeselect" : true
67752     });
67753     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
67754     this.locked = false;
67755 };
67756
67757 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
67758     /**
67759      * @cfg {Boolean} singleSelect
67760      * True to allow selection of only one row at a time (defaults to false)
67761      */
67762     singleSelect : false,
67763
67764     // private
67765     initEvents : function(){
67766
67767         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
67768             this.grid.on("mousedown", this.handleMouseDown, this);
67769         }else{ // allow click to work like normal
67770             this.grid.on("rowclick", this.handleDragableRowClick, this);
67771         }
67772         // bootstrap does not have a view..
67773         var view = this.grid.view ? this.grid.view : this.grid;
67774         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
67775             "up" : function(e){
67776                 if(!e.shiftKey){
67777                     this.selectPrevious(e.shiftKey);
67778                 }else if(this.last !== false && this.lastActive !== false){
67779                     var last = this.last;
67780                     this.selectRange(this.last,  this.lastActive-1);
67781                     view.focusRow(this.lastActive);
67782                     if(last !== false){
67783                         this.last = last;
67784                     }
67785                 }else{
67786                     this.selectFirstRow();
67787                 }
67788                 this.fireEvent("afterselectionchange", this);
67789             },
67790             "down" : function(e){
67791                 if(!e.shiftKey){
67792                     this.selectNext(e.shiftKey);
67793                 }else if(this.last !== false && this.lastActive !== false){
67794                     var last = this.last;
67795                     this.selectRange(this.last,  this.lastActive+1);
67796                     view.focusRow(this.lastActive);
67797                     if(last !== false){
67798                         this.last = last;
67799                     }
67800                 }else{
67801                     this.selectFirstRow();
67802                 }
67803                 this.fireEvent("afterselectionchange", this);
67804             },
67805             scope: this
67806         });
67807
67808          
67809         view.on("refresh", this.onRefresh, this);
67810         view.on("rowupdated", this.onRowUpdated, this);
67811         view.on("rowremoved", this.onRemove, this);
67812     },
67813
67814     // private
67815     onRefresh : function(){
67816         var ds = this.grid.ds, i, v = this.grid.view;
67817         var s = this.selections;
67818         s.each(function(r){
67819             if((i = ds.indexOfId(r.id)) != -1){
67820                 v.onRowSelect(i);
67821                 s.add(ds.getAt(i)); // updating the selection relate data
67822             }else{
67823                 s.remove(r);
67824             }
67825         });
67826     },
67827
67828     // private
67829     onRemove : function(v, index, r){
67830         this.selections.remove(r);
67831     },
67832
67833     // private
67834     onRowUpdated : function(v, index, r){
67835         if(this.isSelected(r)){
67836             v.onRowSelect(index);
67837         }
67838     },
67839
67840     /**
67841      * Select records.
67842      * @param {Array} records The records to select
67843      * @param {Boolean} keepExisting (optional) True to keep existing selections
67844      */
67845     selectRecords : function(records, keepExisting){
67846         if(!keepExisting){
67847             this.clearSelections();
67848         }
67849         var ds = this.grid.ds;
67850         for(var i = 0, len = records.length; i < len; i++){
67851             this.selectRow(ds.indexOf(records[i]), true);
67852         }
67853     },
67854
67855     /**
67856      * Gets the number of selected rows.
67857      * @return {Number}
67858      */
67859     getCount : function(){
67860         return this.selections.length;
67861     },
67862
67863     /**
67864      * Selects the first row in the grid.
67865      */
67866     selectFirstRow : function(){
67867         this.selectRow(0);
67868     },
67869
67870     /**
67871      * Select the last row.
67872      * @param {Boolean} keepExisting (optional) True to keep existing selections
67873      */
67874     selectLastRow : function(keepExisting){
67875         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
67876     },
67877
67878     /**
67879      * Selects the row immediately following the last selected row.
67880      * @param {Boolean} keepExisting (optional) True to keep existing selections
67881      */
67882     selectNext : function(keepExisting){
67883         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
67884             this.selectRow(this.last+1, keepExisting);
67885             var view = this.grid.view ? this.grid.view : this.grid;
67886             view.focusRow(this.last);
67887         }
67888     },
67889
67890     /**
67891      * Selects the row that precedes the last selected row.
67892      * @param {Boolean} keepExisting (optional) True to keep existing selections
67893      */
67894     selectPrevious : function(keepExisting){
67895         if(this.last){
67896             this.selectRow(this.last-1, keepExisting);
67897             var view = this.grid.view ? this.grid.view : this.grid;
67898             view.focusRow(this.last);
67899         }
67900     },
67901
67902     /**
67903      * Returns the selected records
67904      * @return {Array} Array of selected records
67905      */
67906     getSelections : function(){
67907         return [].concat(this.selections.items);
67908     },
67909
67910     /**
67911      * Returns the first selected record.
67912      * @return {Record}
67913      */
67914     getSelected : function(){
67915         return this.selections.itemAt(0);
67916     },
67917
67918
67919     /**
67920      * Clears all selections.
67921      */
67922     clearSelections : function(fast){
67923         if(this.locked) {
67924             return;
67925         }
67926         if(fast !== true){
67927             var ds = this.grid.ds;
67928             var s = this.selections;
67929             s.each(function(r){
67930                 this.deselectRow(ds.indexOfId(r.id));
67931             }, this);
67932             s.clear();
67933         }else{
67934             this.selections.clear();
67935         }
67936         this.last = false;
67937     },
67938
67939
67940     /**
67941      * Selects all rows.
67942      */
67943     selectAll : function(){
67944         if(this.locked) {
67945             return;
67946         }
67947         this.selections.clear();
67948         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
67949             this.selectRow(i, true);
67950         }
67951     },
67952
67953     /**
67954      * Returns True if there is a selection.
67955      * @return {Boolean}
67956      */
67957     hasSelection : function(){
67958         return this.selections.length > 0;
67959     },
67960
67961     /**
67962      * Returns True if the specified row is selected.
67963      * @param {Number/Record} record The record or index of the record to check
67964      * @return {Boolean}
67965      */
67966     isSelected : function(index){
67967         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
67968         return (r && this.selections.key(r.id) ? true : false);
67969     },
67970
67971     /**
67972      * Returns True if the specified record id is selected.
67973      * @param {String} id The id of record to check
67974      * @return {Boolean}
67975      */
67976     isIdSelected : function(id){
67977         return (this.selections.key(id) ? true : false);
67978     },
67979
67980     // private
67981     handleMouseDown : function(e, t)
67982     {
67983         var view = this.grid.view ? this.grid.view : this.grid;
67984         var rowIndex;
67985         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
67986             return;
67987         };
67988         if(e.shiftKey && this.last !== false){
67989             var last = this.last;
67990             this.selectRange(last, rowIndex, e.ctrlKey);
67991             this.last = last; // reset the last
67992             view.focusRow(rowIndex);
67993         }else{
67994             var isSelected = this.isSelected(rowIndex);
67995             if(e.button !== 0 && isSelected){
67996                 view.focusRow(rowIndex);
67997             }else if(e.ctrlKey && isSelected){
67998                 this.deselectRow(rowIndex);
67999             }else if(!isSelected){
68000                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
68001                 view.focusRow(rowIndex);
68002             }
68003         }
68004         this.fireEvent("afterselectionchange", this);
68005     },
68006     // private
68007     handleDragableRowClick :  function(grid, rowIndex, e) 
68008     {
68009         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
68010             this.selectRow(rowIndex, false);
68011             var view = this.grid.view ? this.grid.view : this.grid;
68012             view.focusRow(rowIndex);
68013              this.fireEvent("afterselectionchange", this);
68014         }
68015     },
68016     
68017     /**
68018      * Selects multiple rows.
68019      * @param {Array} rows Array of the indexes of the row to select
68020      * @param {Boolean} keepExisting (optional) True to keep existing selections
68021      */
68022     selectRows : function(rows, keepExisting){
68023         if(!keepExisting){
68024             this.clearSelections();
68025         }
68026         for(var i = 0, len = rows.length; i < len; i++){
68027             this.selectRow(rows[i], true);
68028         }
68029     },
68030
68031     /**
68032      * Selects a range of rows. All rows in between startRow and endRow are also selected.
68033      * @param {Number} startRow The index of the first row in the range
68034      * @param {Number} endRow The index of the last row in the range
68035      * @param {Boolean} keepExisting (optional) True to retain existing selections
68036      */
68037     selectRange : function(startRow, endRow, keepExisting){
68038         if(this.locked) {
68039             return;
68040         }
68041         if(!keepExisting){
68042             this.clearSelections();
68043         }
68044         if(startRow <= endRow){
68045             for(var i = startRow; i <= endRow; i++){
68046                 this.selectRow(i, true);
68047             }
68048         }else{
68049             for(var i = startRow; i >= endRow; i--){
68050                 this.selectRow(i, true);
68051             }
68052         }
68053     },
68054
68055     /**
68056      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
68057      * @param {Number} startRow The index of the first row in the range
68058      * @param {Number} endRow The index of the last row in the range
68059      */
68060     deselectRange : function(startRow, endRow, preventViewNotify){
68061         if(this.locked) {
68062             return;
68063         }
68064         for(var i = startRow; i <= endRow; i++){
68065             this.deselectRow(i, preventViewNotify);
68066         }
68067     },
68068
68069     /**
68070      * Selects a row.
68071      * @param {Number} row The index of the row to select
68072      * @param {Boolean} keepExisting (optional) True to keep existing selections
68073      */
68074     selectRow : function(index, keepExisting, preventViewNotify){
68075         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
68076             return;
68077         }
68078         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
68079             if(!keepExisting || this.singleSelect){
68080                 this.clearSelections();
68081             }
68082             var r = this.grid.ds.getAt(index);
68083             this.selections.add(r);
68084             this.last = this.lastActive = index;
68085             if(!preventViewNotify){
68086                 var view = this.grid.view ? this.grid.view : this.grid;
68087                 view.onRowSelect(index);
68088             }
68089             this.fireEvent("rowselect", this, index, r);
68090             this.fireEvent("selectionchange", this);
68091         }
68092     },
68093
68094     /**
68095      * Deselects a row.
68096      * @param {Number} row The index of the row to deselect
68097      */
68098     deselectRow : function(index, preventViewNotify){
68099         if(this.locked) {
68100             return;
68101         }
68102         if(this.last == index){
68103             this.last = false;
68104         }
68105         if(this.lastActive == index){
68106             this.lastActive = false;
68107         }
68108         var r = this.grid.ds.getAt(index);
68109         this.selections.remove(r);
68110         if(!preventViewNotify){
68111             var view = this.grid.view ? this.grid.view : this.grid;
68112             view.onRowDeselect(index);
68113         }
68114         this.fireEvent("rowdeselect", this, index);
68115         this.fireEvent("selectionchange", this);
68116     },
68117
68118     // private
68119     restoreLast : function(){
68120         if(this._last){
68121             this.last = this._last;
68122         }
68123     },
68124
68125     // private
68126     acceptsNav : function(row, col, cm){
68127         return !cm.isHidden(col) && cm.isCellEditable(col, row);
68128     },
68129
68130     // private
68131     onEditorKey : function(field, e){
68132         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
68133         if(k == e.TAB){
68134             e.stopEvent();
68135             ed.completeEdit();
68136             if(e.shiftKey){
68137                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
68138             }else{
68139                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
68140             }
68141         }else if(k == e.ENTER && !e.ctrlKey){
68142             e.stopEvent();
68143             ed.completeEdit();
68144             if(e.shiftKey){
68145                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
68146             }else{
68147                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
68148             }
68149         }else if(k == e.ESC){
68150             ed.cancelEdit();
68151         }
68152         if(newCell){
68153             g.startEditing(newCell[0], newCell[1]);
68154         }
68155     }
68156 });/*
68157  * Based on:
68158  * Ext JS Library 1.1.1
68159  * Copyright(c) 2006-2007, Ext JS, LLC.
68160  *
68161  * Originally Released Under LGPL - original licence link has changed is not relivant.
68162  *
68163  * Fork - LGPL
68164  * <script type="text/javascript">
68165  */
68166 /**
68167  * @class Roo.grid.CellSelectionModel
68168  * @extends Roo.grid.AbstractSelectionModel
68169  * This class provides the basic implementation for cell selection in a grid.
68170  * @constructor
68171  * @param {Object} config The object containing the configuration of this model.
68172  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
68173  */
68174 Roo.grid.CellSelectionModel = function(config){
68175     Roo.apply(this, config);
68176
68177     this.selection = null;
68178
68179     this.addEvents({
68180         /**
68181              * @event beforerowselect
68182              * Fires before a cell is selected.
68183              * @param {SelectionModel} this
68184              * @param {Number} rowIndex The selected row index
68185              * @param {Number} colIndex The selected cell index
68186              */
68187             "beforecellselect" : true,
68188         /**
68189              * @event cellselect
68190              * Fires when a cell is selected.
68191              * @param {SelectionModel} this
68192              * @param {Number} rowIndex The selected row index
68193              * @param {Number} colIndex The selected cell index
68194              */
68195             "cellselect" : true,
68196         /**
68197              * @event selectionchange
68198              * Fires when the active selection changes.
68199              * @param {SelectionModel} this
68200              * @param {Object} selection null for no selection or an object (o) with two properties
68201                 <ul>
68202                 <li>o.record: the record object for the row the selection is in</li>
68203                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
68204                 </ul>
68205              */
68206             "selectionchange" : true,
68207         /**
68208              * @event tabend
68209              * Fires when the tab (or enter) was pressed on the last editable cell
68210              * You can use this to trigger add new row.
68211              * @param {SelectionModel} this
68212              */
68213             "tabend" : true,
68214          /**
68215              * @event beforeeditnext
68216              * Fires before the next editable sell is made active
68217              * You can use this to skip to another cell or fire the tabend
68218              *    if you set cell to false
68219              * @param {Object} eventdata object : { cell : [ row, col ] } 
68220              */
68221             "beforeeditnext" : true
68222     });
68223     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
68224 };
68225
68226 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
68227     
68228     enter_is_tab: false,
68229
68230     /** @ignore */
68231     initEvents : function(){
68232         this.grid.on("mousedown", this.handleMouseDown, this);
68233         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
68234         var view = this.grid.view;
68235         view.on("refresh", this.onViewChange, this);
68236         view.on("rowupdated", this.onRowUpdated, this);
68237         view.on("beforerowremoved", this.clearSelections, this);
68238         view.on("beforerowsinserted", this.clearSelections, this);
68239         if(this.grid.isEditor){
68240             this.grid.on("beforeedit", this.beforeEdit,  this);
68241         }
68242     },
68243
68244         //private
68245     beforeEdit : function(e){
68246         this.select(e.row, e.column, false, true, e.record);
68247     },
68248
68249         //private
68250     onRowUpdated : function(v, index, r){
68251         if(this.selection && this.selection.record == r){
68252             v.onCellSelect(index, this.selection.cell[1]);
68253         }
68254     },
68255
68256         //private
68257     onViewChange : function(){
68258         this.clearSelections(true);
68259     },
68260
68261         /**
68262          * Returns the currently selected cell,.
68263          * @return {Array} The selected cell (row, column) or null if none selected.
68264          */
68265     getSelectedCell : function(){
68266         return this.selection ? this.selection.cell : null;
68267     },
68268
68269     /**
68270      * Clears all selections.
68271      * @param {Boolean} true to prevent the gridview from being notified about the change.
68272      */
68273     clearSelections : function(preventNotify){
68274         var s = this.selection;
68275         if(s){
68276             if(preventNotify !== true){
68277                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
68278             }
68279             this.selection = null;
68280             this.fireEvent("selectionchange", this, null);
68281         }
68282     },
68283
68284     /**
68285      * Returns true if there is a selection.
68286      * @return {Boolean}
68287      */
68288     hasSelection : function(){
68289         return this.selection ? true : false;
68290     },
68291
68292     /** @ignore */
68293     handleMouseDown : function(e, t){
68294         var v = this.grid.getView();
68295         if(this.isLocked()){
68296             return;
68297         };
68298         var row = v.findRowIndex(t);
68299         var cell = v.findCellIndex(t);
68300         if(row !== false && cell !== false){
68301             this.select(row, cell);
68302         }
68303     },
68304
68305     /**
68306      * Selects a cell.
68307      * @param {Number} rowIndex
68308      * @param {Number} collIndex
68309      */
68310     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
68311         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
68312             this.clearSelections();
68313             r = r || this.grid.dataSource.getAt(rowIndex);
68314             this.selection = {
68315                 record : r,
68316                 cell : [rowIndex, colIndex]
68317             };
68318             if(!preventViewNotify){
68319                 var v = this.grid.getView();
68320                 v.onCellSelect(rowIndex, colIndex);
68321                 if(preventFocus !== true){
68322                     v.focusCell(rowIndex, colIndex);
68323                 }
68324             }
68325             this.fireEvent("cellselect", this, rowIndex, colIndex);
68326             this.fireEvent("selectionchange", this, this.selection);
68327         }
68328     },
68329
68330         //private
68331     isSelectable : function(rowIndex, colIndex, cm){
68332         return !cm.isHidden(colIndex);
68333     },
68334
68335     /** @ignore */
68336     handleKeyDown : function(e){
68337         //Roo.log('Cell Sel Model handleKeyDown');
68338         if(!e.isNavKeyPress()){
68339             return;
68340         }
68341         var g = this.grid, s = this.selection;
68342         if(!s){
68343             e.stopEvent();
68344             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
68345             if(cell){
68346                 this.select(cell[0], cell[1]);
68347             }
68348             return;
68349         }
68350         var sm = this;
68351         var walk = function(row, col, step){
68352             return g.walkCells(row, col, step, sm.isSelectable,  sm);
68353         };
68354         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
68355         var newCell;
68356
68357       
68358
68359         switch(k){
68360             case e.TAB:
68361                 // handled by onEditorKey
68362                 if (g.isEditor && g.editing) {
68363                     return;
68364                 }
68365                 if(e.shiftKey) {
68366                     newCell = walk(r, c-1, -1);
68367                 } else {
68368                     newCell = walk(r, c+1, 1);
68369                 }
68370                 break;
68371             
68372             case e.DOWN:
68373                newCell = walk(r+1, c, 1);
68374                 break;
68375             
68376             case e.UP:
68377                 newCell = walk(r-1, c, -1);
68378                 break;
68379             
68380             case e.RIGHT:
68381                 newCell = walk(r, c+1, 1);
68382                 break;
68383             
68384             case e.LEFT:
68385                 newCell = walk(r, c-1, -1);
68386                 break;
68387             
68388             case e.ENTER:
68389                 
68390                 if(g.isEditor && !g.editing){
68391                    g.startEditing(r, c);
68392                    e.stopEvent();
68393                    return;
68394                 }
68395                 
68396                 
68397              break;
68398         };
68399         if(newCell){
68400             this.select(newCell[0], newCell[1]);
68401             e.stopEvent();
68402             
68403         }
68404     },
68405
68406     acceptsNav : function(row, col, cm){
68407         return !cm.isHidden(col) && cm.isCellEditable(col, row);
68408     },
68409     /**
68410      * Selects a cell.
68411      * @param {Number} field (not used) - as it's normally used as a listener
68412      * @param {Number} e - event - fake it by using
68413      *
68414      * var e = Roo.EventObjectImpl.prototype;
68415      * e.keyCode = e.TAB
68416      *
68417      * 
68418      */
68419     onEditorKey : function(field, e){
68420         
68421         var k = e.getKey(),
68422             newCell,
68423             g = this.grid,
68424             ed = g.activeEditor,
68425             forward = false;
68426         ///Roo.log('onEditorKey' + k);
68427         
68428         
68429         if (this.enter_is_tab && k == e.ENTER) {
68430             k = e.TAB;
68431         }
68432         
68433         if(k == e.TAB){
68434             if(e.shiftKey){
68435                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
68436             }else{
68437                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
68438                 forward = true;
68439             }
68440             
68441             e.stopEvent();
68442             
68443         } else if(k == e.ENTER &&  !e.ctrlKey){
68444             ed.completeEdit();
68445             e.stopEvent();
68446             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
68447         
68448                 } else if(k == e.ESC){
68449             ed.cancelEdit();
68450         }
68451                 
68452         if (newCell) {
68453             var ecall = { cell : newCell, forward : forward };
68454             this.fireEvent('beforeeditnext', ecall );
68455             newCell = ecall.cell;
68456                         forward = ecall.forward;
68457         }
68458                 
68459         if(newCell){
68460             //Roo.log('next cell after edit');
68461             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
68462         } else if (forward) {
68463             // tabbed past last
68464             this.fireEvent.defer(100, this, ['tabend',this]);
68465         }
68466     }
68467 });/*
68468  * Based on:
68469  * Ext JS Library 1.1.1
68470  * Copyright(c) 2006-2007, Ext JS, LLC.
68471  *
68472  * Originally Released Under LGPL - original licence link has changed is not relivant.
68473  *
68474  * Fork - LGPL
68475  * <script type="text/javascript">
68476  */
68477  
68478 /**
68479  * @class Roo.grid.EditorGrid
68480  * @extends Roo.grid.Grid
68481  * Class for creating and editable grid.
68482  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
68483  * The container MUST have some type of size defined for the grid to fill. The container will be 
68484  * automatically set to position relative if it isn't already.
68485  * @param {Object} dataSource The data model to bind to
68486  * @param {Object} colModel The column model with info about this grid's columns
68487  */
68488 Roo.grid.EditorGrid = function(container, config){
68489     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
68490     this.getGridEl().addClass("xedit-grid");
68491
68492     if(!this.selModel){
68493         this.selModel = new Roo.grid.CellSelectionModel();
68494     }
68495
68496     this.activeEditor = null;
68497
68498         this.addEvents({
68499             /**
68500              * @event beforeedit
68501              * Fires before cell editing is triggered. The edit event object has the following properties <br />
68502              * <ul style="padding:5px;padding-left:16px;">
68503              * <li>grid - This grid</li>
68504              * <li>record - The record being edited</li>
68505              * <li>field - The field name being edited</li>
68506              * <li>value - The value for the field being edited.</li>
68507              * <li>row - The grid row index</li>
68508              * <li>column - The grid column index</li>
68509              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
68510              * </ul>
68511              * @param {Object} e An edit event (see above for description)
68512              */
68513             "beforeedit" : true,
68514             /**
68515              * @event afteredit
68516              * Fires after a cell is edited. <br />
68517              * <ul style="padding:5px;padding-left:16px;">
68518              * <li>grid - This grid</li>
68519              * <li>record - The record being edited</li>
68520              * <li>field - The field name being edited</li>
68521              * <li>value - The value being set</li>
68522              * <li>originalValue - The original value for the field, before the edit.</li>
68523              * <li>row - The grid row index</li>
68524              * <li>column - The grid column index</li>
68525              * </ul>
68526              * @param {Object} e An edit event (see above for description)
68527              */
68528             "afteredit" : true,
68529             /**
68530              * @event validateedit
68531              * Fires after a cell is edited, but before the value is set in the record. 
68532          * You can use this to modify the value being set in the field, Return false
68533              * to cancel the change. The edit event object has the following properties <br />
68534              * <ul style="padding:5px;padding-left:16px;">
68535          * <li>editor - This editor</li>
68536              * <li>grid - This grid</li>
68537              * <li>record - The record being edited</li>
68538              * <li>field - The field name being edited</li>
68539              * <li>value - The value being set</li>
68540              * <li>originalValue - The original value for the field, before the edit.</li>
68541              * <li>row - The grid row index</li>
68542              * <li>column - The grid column index</li>
68543              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
68544              * </ul>
68545              * @param {Object} e An edit event (see above for description)
68546              */
68547             "validateedit" : true
68548         });
68549     this.on("bodyscroll", this.stopEditing,  this);
68550     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
68551 };
68552
68553 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
68554     /**
68555      * @cfg {Number} clicksToEdit
68556      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
68557      */
68558     clicksToEdit: 2,
68559
68560     // private
68561     isEditor : true,
68562     // private
68563     trackMouseOver: false, // causes very odd FF errors
68564
68565     onCellDblClick : function(g, row, col){
68566         this.startEditing(row, col);
68567     },
68568
68569     onEditComplete : function(ed, value, startValue){
68570         this.editing = false;
68571         this.activeEditor = null;
68572         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
68573         var r = ed.record;
68574         var field = this.colModel.getDataIndex(ed.col);
68575         var e = {
68576             grid: this,
68577             record: r,
68578             field: field,
68579             originalValue: startValue,
68580             value: value,
68581             row: ed.row,
68582             column: ed.col,
68583             cancel:false,
68584             editor: ed
68585         };
68586         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
68587         cell.show();
68588           
68589         if(String(value) !== String(startValue)){
68590             
68591             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
68592                 r.set(field, e.value);
68593                 // if we are dealing with a combo box..
68594                 // then we also set the 'name' colum to be the displayField
68595                 if (ed.field.displayField && ed.field.name) {
68596                     r.set(ed.field.name, ed.field.el.dom.value);
68597                 }
68598                 
68599                 delete e.cancel; //?? why!!!
68600                 this.fireEvent("afteredit", e);
68601             }
68602         } else {
68603             this.fireEvent("afteredit", e); // always fire it!
68604         }
68605         this.view.focusCell(ed.row, ed.col);
68606     },
68607
68608     /**
68609      * Starts editing the specified for the specified row/column
68610      * @param {Number} rowIndex
68611      * @param {Number} colIndex
68612      */
68613     startEditing : function(row, col){
68614         this.stopEditing();
68615         if(this.colModel.isCellEditable(col, row)){
68616             this.view.ensureVisible(row, col, true);
68617           
68618             var r = this.dataSource.getAt(row);
68619             var field = this.colModel.getDataIndex(col);
68620             var cell = Roo.get(this.view.getCell(row,col));
68621             var e = {
68622                 grid: this,
68623                 record: r,
68624                 field: field,
68625                 value: r.data[field],
68626                 row: row,
68627                 column: col,
68628                 cancel:false 
68629             };
68630             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
68631                 this.editing = true;
68632                 var ed = this.colModel.getCellEditor(col, row);
68633                 
68634                 if (!ed) {
68635                     return;
68636                 }
68637                 if(!ed.rendered){
68638                     ed.render(ed.parentEl || document.body);
68639                 }
68640                 ed.field.reset();
68641                
68642                 cell.hide();
68643                 
68644                 (function(){ // complex but required for focus issues in safari, ie and opera
68645                     ed.row = row;
68646                     ed.col = col;
68647                     ed.record = r;
68648                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
68649                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
68650                     this.activeEditor = ed;
68651                     var v = r.data[field];
68652                     ed.startEdit(this.view.getCell(row, col), v);
68653                     // combo's with 'displayField and name set
68654                     if (ed.field.displayField && ed.field.name) {
68655                         ed.field.el.dom.value = r.data[ed.field.name];
68656                     }
68657                     
68658                     
68659                 }).defer(50, this);
68660             }
68661         }
68662     },
68663         
68664     /**
68665      * Stops any active editing
68666      */
68667     stopEditing : function(){
68668         if(this.activeEditor){
68669             this.activeEditor.completeEdit();
68670         }
68671         this.activeEditor = null;
68672     },
68673         
68674          /**
68675      * Called to get grid's drag proxy text, by default returns this.ddText.
68676      * @return {String}
68677      */
68678     getDragDropText : function(){
68679         var count = this.selModel.getSelectedCell() ? 1 : 0;
68680         return String.format(this.ddText, count, count == 1 ? '' : 's');
68681     }
68682         
68683 });/*
68684  * Based on:
68685  * Ext JS Library 1.1.1
68686  * Copyright(c) 2006-2007, Ext JS, LLC.
68687  *
68688  * Originally Released Under LGPL - original licence link has changed is not relivant.
68689  *
68690  * Fork - LGPL
68691  * <script type="text/javascript">
68692  */
68693
68694 // private - not really -- you end up using it !
68695 // This is a support class used internally by the Grid components
68696
68697 /**
68698  * @class Roo.grid.GridEditor
68699  * @extends Roo.Editor
68700  * Class for creating and editable grid elements.
68701  * @param {Object} config any settings (must include field)
68702  */
68703 Roo.grid.GridEditor = function(field, config){
68704     if (!config && field.field) {
68705         config = field;
68706         field = Roo.factory(config.field, Roo.form);
68707     }
68708     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
68709     field.monitorTab = false;
68710 };
68711
68712 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
68713     
68714     /**
68715      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
68716      */
68717     
68718     alignment: "tl-tl",
68719     autoSize: "width",
68720     hideEl : false,
68721     cls: "x-small-editor x-grid-editor",
68722     shim:false,
68723     shadow:"frame"
68724 });/*
68725  * Based on:
68726  * Ext JS Library 1.1.1
68727  * Copyright(c) 2006-2007, Ext JS, LLC.
68728  *
68729  * Originally Released Under LGPL - original licence link has changed is not relivant.
68730  *
68731  * Fork - LGPL
68732  * <script type="text/javascript">
68733  */
68734   
68735
68736   
68737 Roo.grid.PropertyRecord = Roo.data.Record.create([
68738     {name:'name',type:'string'},  'value'
68739 ]);
68740
68741
68742 Roo.grid.PropertyStore = function(grid, source){
68743     this.grid = grid;
68744     this.store = new Roo.data.Store({
68745         recordType : Roo.grid.PropertyRecord
68746     });
68747     this.store.on('update', this.onUpdate,  this);
68748     if(source){
68749         this.setSource(source);
68750     }
68751     Roo.grid.PropertyStore.superclass.constructor.call(this);
68752 };
68753
68754
68755
68756 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
68757     setSource : function(o){
68758         this.source = o;
68759         this.store.removeAll();
68760         var data = [];
68761         for(var k in o){
68762             if(this.isEditableValue(o[k])){
68763                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
68764             }
68765         }
68766         this.store.loadRecords({records: data}, {}, true);
68767     },
68768
68769     onUpdate : function(ds, record, type){
68770         if(type == Roo.data.Record.EDIT){
68771             var v = record.data['value'];
68772             var oldValue = record.modified['value'];
68773             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
68774                 this.source[record.id] = v;
68775                 record.commit();
68776                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
68777             }else{
68778                 record.reject();
68779             }
68780         }
68781     },
68782
68783     getProperty : function(row){
68784        return this.store.getAt(row);
68785     },
68786
68787     isEditableValue: function(val){
68788         if(val && val instanceof Date){
68789             return true;
68790         }else if(typeof val == 'object' || typeof val == 'function'){
68791             return false;
68792         }
68793         return true;
68794     },
68795
68796     setValue : function(prop, value){
68797         this.source[prop] = value;
68798         this.store.getById(prop).set('value', value);
68799     },
68800
68801     getSource : function(){
68802         return this.source;
68803     }
68804 });
68805
68806 Roo.grid.PropertyColumnModel = function(grid, store){
68807     this.grid = grid;
68808     var g = Roo.grid;
68809     g.PropertyColumnModel.superclass.constructor.call(this, [
68810         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
68811         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
68812     ]);
68813     this.store = store;
68814     this.bselect = Roo.DomHelper.append(document.body, {
68815         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
68816             {tag: 'option', value: 'true', html: 'true'},
68817             {tag: 'option', value: 'false', html: 'false'}
68818         ]
68819     });
68820     Roo.id(this.bselect);
68821     var f = Roo.form;
68822     this.editors = {
68823         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
68824         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
68825         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
68826         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
68827         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
68828     };
68829     this.renderCellDelegate = this.renderCell.createDelegate(this);
68830     this.renderPropDelegate = this.renderProp.createDelegate(this);
68831 };
68832
68833 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
68834     
68835     
68836     nameText : 'Name',
68837     valueText : 'Value',
68838     
68839     dateFormat : 'm/j/Y',
68840     
68841     
68842     renderDate : function(dateVal){
68843         return dateVal.dateFormat(this.dateFormat);
68844     },
68845
68846     renderBool : function(bVal){
68847         return bVal ? 'true' : 'false';
68848     },
68849
68850     isCellEditable : function(colIndex, rowIndex){
68851         return colIndex == 1;
68852     },
68853
68854     getRenderer : function(col){
68855         return col == 1 ?
68856             this.renderCellDelegate : this.renderPropDelegate;
68857     },
68858
68859     renderProp : function(v){
68860         return this.getPropertyName(v);
68861     },
68862
68863     renderCell : function(val){
68864         var rv = val;
68865         if(val instanceof Date){
68866             rv = this.renderDate(val);
68867         }else if(typeof val == 'boolean'){
68868             rv = this.renderBool(val);
68869         }
68870         return Roo.util.Format.htmlEncode(rv);
68871     },
68872
68873     getPropertyName : function(name){
68874         var pn = this.grid.propertyNames;
68875         return pn && pn[name] ? pn[name] : name;
68876     },
68877
68878     getCellEditor : function(colIndex, rowIndex){
68879         var p = this.store.getProperty(rowIndex);
68880         var n = p.data['name'], val = p.data['value'];
68881         
68882         if(typeof(this.grid.customEditors[n]) == 'string'){
68883             return this.editors[this.grid.customEditors[n]];
68884         }
68885         if(typeof(this.grid.customEditors[n]) != 'undefined'){
68886             return this.grid.customEditors[n];
68887         }
68888         if(val instanceof Date){
68889             return this.editors['date'];
68890         }else if(typeof val == 'number'){
68891             return this.editors['number'];
68892         }else if(typeof val == 'boolean'){
68893             return this.editors['boolean'];
68894         }else{
68895             return this.editors['string'];
68896         }
68897     }
68898 });
68899
68900 /**
68901  * @class Roo.grid.PropertyGrid
68902  * @extends Roo.grid.EditorGrid
68903  * This class represents the  interface of a component based property grid control.
68904  * <br><br>Usage:<pre><code>
68905  var grid = new Roo.grid.PropertyGrid("my-container-id", {
68906       
68907  });
68908  // set any options
68909  grid.render();
68910  * </code></pre>
68911   
68912  * @constructor
68913  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
68914  * The container MUST have some type of size defined for the grid to fill. The container will be
68915  * automatically set to position relative if it isn't already.
68916  * @param {Object} config A config object that sets properties on this grid.
68917  */
68918 Roo.grid.PropertyGrid = function(container, config){
68919     config = config || {};
68920     var store = new Roo.grid.PropertyStore(this);
68921     this.store = store;
68922     var cm = new Roo.grid.PropertyColumnModel(this, store);
68923     store.store.sort('name', 'ASC');
68924     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
68925         ds: store.store,
68926         cm: cm,
68927         enableColLock:false,
68928         enableColumnMove:false,
68929         stripeRows:false,
68930         trackMouseOver: false,
68931         clicksToEdit:1
68932     }, config));
68933     this.getGridEl().addClass('x-props-grid');
68934     this.lastEditRow = null;
68935     this.on('columnresize', this.onColumnResize, this);
68936     this.addEvents({
68937          /**
68938              * @event beforepropertychange
68939              * Fires before a property changes (return false to stop?)
68940              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
68941              * @param {String} id Record Id
68942              * @param {String} newval New Value
68943          * @param {String} oldval Old Value
68944              */
68945         "beforepropertychange": true,
68946         /**
68947              * @event propertychange
68948              * Fires after a property changes
68949              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
68950              * @param {String} id Record Id
68951              * @param {String} newval New Value
68952          * @param {String} oldval Old Value
68953              */
68954         "propertychange": true
68955     });
68956     this.customEditors = this.customEditors || {};
68957 };
68958 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
68959     
68960      /**
68961      * @cfg {Object} customEditors map of colnames=> custom editors.
68962      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
68963      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
68964      * false disables editing of the field.
68965          */
68966     
68967       /**
68968      * @cfg {Object} propertyNames map of property Names to their displayed value
68969          */
68970     
68971     render : function(){
68972         Roo.grid.PropertyGrid.superclass.render.call(this);
68973         this.autoSize.defer(100, this);
68974     },
68975
68976     autoSize : function(){
68977         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
68978         if(this.view){
68979             this.view.fitColumns();
68980         }
68981     },
68982
68983     onColumnResize : function(){
68984         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
68985         this.autoSize();
68986     },
68987     /**
68988      * Sets the data for the Grid
68989      * accepts a Key => Value object of all the elements avaiable.
68990      * @param {Object} data  to appear in grid.
68991      */
68992     setSource : function(source){
68993         this.store.setSource(source);
68994         //this.autoSize();
68995     },
68996     /**
68997      * Gets all the data from the grid.
68998      * @return {Object} data  data stored in grid
68999      */
69000     getSource : function(){
69001         return this.store.getSource();
69002     }
69003 });/*
69004   
69005  * Licence LGPL
69006  
69007  */
69008  
69009 /**
69010  * @class Roo.grid.Calendar
69011  * @extends Roo.grid.Grid
69012  * This class extends the Grid to provide a calendar widget
69013  * <br><br>Usage:<pre><code>
69014  var grid = new Roo.grid.Calendar("my-container-id", {
69015      ds: myDataStore,
69016      cm: myColModel,
69017      selModel: mySelectionModel,
69018      autoSizeColumns: true,
69019      monitorWindowResize: false,
69020      trackMouseOver: true
69021      eventstore : real data store..
69022  });
69023  // set any options
69024  grid.render();
69025   
69026   * @constructor
69027  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
69028  * The container MUST have some type of size defined for the grid to fill. The container will be
69029  * automatically set to position relative if it isn't already.
69030  * @param {Object} config A config object that sets properties on this grid.
69031  */
69032 Roo.grid.Calendar = function(container, config){
69033         // initialize the container
69034         this.container = Roo.get(container);
69035         this.container.update("");
69036         this.container.setStyle("overflow", "hidden");
69037     this.container.addClass('x-grid-container');
69038
69039     this.id = this.container.id;
69040
69041     Roo.apply(this, config);
69042     // check and correct shorthanded configs
69043     
69044     var rows = [];
69045     var d =1;
69046     for (var r = 0;r < 6;r++) {
69047         
69048         rows[r]=[];
69049         for (var c =0;c < 7;c++) {
69050             rows[r][c]= '';
69051         }
69052     }
69053     if (this.eventStore) {
69054         this.eventStore= Roo.factory(this.eventStore, Roo.data);
69055         this.eventStore.on('load',this.onLoad, this);
69056         this.eventStore.on('beforeload',this.clearEvents, this);
69057          
69058     }
69059     
69060     this.dataSource = new Roo.data.Store({
69061             proxy: new Roo.data.MemoryProxy(rows),
69062             reader: new Roo.data.ArrayReader({}, [
69063                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
69064     });
69065
69066     this.dataSource.load();
69067     this.ds = this.dataSource;
69068     this.ds.xmodule = this.xmodule || false;
69069     
69070     
69071     var cellRender = function(v,x,r)
69072     {
69073         return String.format(
69074             '<div class="fc-day  fc-widget-content"><div>' +
69075                 '<div class="fc-event-container"></div>' +
69076                 '<div class="fc-day-number">{0}</div>'+
69077                 
69078                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
69079             '</div></div>', v);
69080     
69081     }
69082     
69083     
69084     this.colModel = new Roo.grid.ColumnModel( [
69085         {
69086             xtype: 'ColumnModel',
69087             xns: Roo.grid,
69088             dataIndex : 'weekday0',
69089             header : 'Sunday',
69090             renderer : cellRender
69091         },
69092         {
69093             xtype: 'ColumnModel',
69094             xns: Roo.grid,
69095             dataIndex : 'weekday1',
69096             header : 'Monday',
69097             renderer : cellRender
69098         },
69099         {
69100             xtype: 'ColumnModel',
69101             xns: Roo.grid,
69102             dataIndex : 'weekday2',
69103             header : 'Tuesday',
69104             renderer : cellRender
69105         },
69106         {
69107             xtype: 'ColumnModel',
69108             xns: Roo.grid,
69109             dataIndex : 'weekday3',
69110             header : 'Wednesday',
69111             renderer : cellRender
69112         },
69113         {
69114             xtype: 'ColumnModel',
69115             xns: Roo.grid,
69116             dataIndex : 'weekday4',
69117             header : 'Thursday',
69118             renderer : cellRender
69119         },
69120         {
69121             xtype: 'ColumnModel',
69122             xns: Roo.grid,
69123             dataIndex : 'weekday5',
69124             header : 'Friday',
69125             renderer : cellRender
69126         },
69127         {
69128             xtype: 'ColumnModel',
69129             xns: Roo.grid,
69130             dataIndex : 'weekday6',
69131             header : 'Saturday',
69132             renderer : cellRender
69133         }
69134     ]);
69135     this.cm = this.colModel;
69136     this.cm.xmodule = this.xmodule || false;
69137  
69138         
69139           
69140     //this.selModel = new Roo.grid.CellSelectionModel();
69141     //this.sm = this.selModel;
69142     //this.selModel.init(this);
69143     
69144     
69145     if(this.width){
69146         this.container.setWidth(this.width);
69147     }
69148
69149     if(this.height){
69150         this.container.setHeight(this.height);
69151     }
69152     /** @private */
69153         this.addEvents({
69154         // raw events
69155         /**
69156          * @event click
69157          * The raw click event for the entire grid.
69158          * @param {Roo.EventObject} e
69159          */
69160         "click" : true,
69161         /**
69162          * @event dblclick
69163          * The raw dblclick event for the entire grid.
69164          * @param {Roo.EventObject} e
69165          */
69166         "dblclick" : true,
69167         /**
69168          * @event contextmenu
69169          * The raw contextmenu event for the entire grid.
69170          * @param {Roo.EventObject} e
69171          */
69172         "contextmenu" : true,
69173         /**
69174          * @event mousedown
69175          * The raw mousedown event for the entire grid.
69176          * @param {Roo.EventObject} e
69177          */
69178         "mousedown" : true,
69179         /**
69180          * @event mouseup
69181          * The raw mouseup event for the entire grid.
69182          * @param {Roo.EventObject} e
69183          */
69184         "mouseup" : true,
69185         /**
69186          * @event mouseover
69187          * The raw mouseover event for the entire grid.
69188          * @param {Roo.EventObject} e
69189          */
69190         "mouseover" : true,
69191         /**
69192          * @event mouseout
69193          * The raw mouseout event for the entire grid.
69194          * @param {Roo.EventObject} e
69195          */
69196         "mouseout" : true,
69197         /**
69198          * @event keypress
69199          * The raw keypress event for the entire grid.
69200          * @param {Roo.EventObject} e
69201          */
69202         "keypress" : true,
69203         /**
69204          * @event keydown
69205          * The raw keydown event for the entire grid.
69206          * @param {Roo.EventObject} e
69207          */
69208         "keydown" : true,
69209
69210         // custom events
69211
69212         /**
69213          * @event cellclick
69214          * Fires when a cell is clicked
69215          * @param {Grid} this
69216          * @param {Number} rowIndex
69217          * @param {Number} columnIndex
69218          * @param {Roo.EventObject} e
69219          */
69220         "cellclick" : true,
69221         /**
69222          * @event celldblclick
69223          * Fires when a cell is double clicked
69224          * @param {Grid} this
69225          * @param {Number} rowIndex
69226          * @param {Number} columnIndex
69227          * @param {Roo.EventObject} e
69228          */
69229         "celldblclick" : true,
69230         /**
69231          * @event rowclick
69232          * Fires when a row is clicked
69233          * @param {Grid} this
69234          * @param {Number} rowIndex
69235          * @param {Roo.EventObject} e
69236          */
69237         "rowclick" : true,
69238         /**
69239          * @event rowdblclick
69240          * Fires when a row is double clicked
69241          * @param {Grid} this
69242          * @param {Number} rowIndex
69243          * @param {Roo.EventObject} e
69244          */
69245         "rowdblclick" : true,
69246         /**
69247          * @event headerclick
69248          * Fires when a header is clicked
69249          * @param {Grid} this
69250          * @param {Number} columnIndex
69251          * @param {Roo.EventObject} e
69252          */
69253         "headerclick" : true,
69254         /**
69255          * @event headerdblclick
69256          * Fires when a header cell is double clicked
69257          * @param {Grid} this
69258          * @param {Number} columnIndex
69259          * @param {Roo.EventObject} e
69260          */
69261         "headerdblclick" : true,
69262         /**
69263          * @event rowcontextmenu
69264          * Fires when a row is right clicked
69265          * @param {Grid} this
69266          * @param {Number} rowIndex
69267          * @param {Roo.EventObject} e
69268          */
69269         "rowcontextmenu" : true,
69270         /**
69271          * @event cellcontextmenu
69272          * Fires when a cell is right clicked
69273          * @param {Grid} this
69274          * @param {Number} rowIndex
69275          * @param {Number} cellIndex
69276          * @param {Roo.EventObject} e
69277          */
69278          "cellcontextmenu" : true,
69279         /**
69280          * @event headercontextmenu
69281          * Fires when a header is right clicked
69282          * @param {Grid} this
69283          * @param {Number} columnIndex
69284          * @param {Roo.EventObject} e
69285          */
69286         "headercontextmenu" : true,
69287         /**
69288          * @event bodyscroll
69289          * Fires when the body element is scrolled
69290          * @param {Number} scrollLeft
69291          * @param {Number} scrollTop
69292          */
69293         "bodyscroll" : true,
69294         /**
69295          * @event columnresize
69296          * Fires when the user resizes a column
69297          * @param {Number} columnIndex
69298          * @param {Number} newSize
69299          */
69300         "columnresize" : true,
69301         /**
69302          * @event columnmove
69303          * Fires when the user moves a column
69304          * @param {Number} oldIndex
69305          * @param {Number} newIndex
69306          */
69307         "columnmove" : true,
69308         /**
69309          * @event startdrag
69310          * Fires when row(s) start being dragged
69311          * @param {Grid} this
69312          * @param {Roo.GridDD} dd The drag drop object
69313          * @param {event} e The raw browser event
69314          */
69315         "startdrag" : true,
69316         /**
69317          * @event enddrag
69318          * Fires when a drag operation is complete
69319          * @param {Grid} this
69320          * @param {Roo.GridDD} dd The drag drop object
69321          * @param {event} e The raw browser event
69322          */
69323         "enddrag" : true,
69324         /**
69325          * @event dragdrop
69326          * Fires when dragged row(s) are dropped on a valid DD target
69327          * @param {Grid} this
69328          * @param {Roo.GridDD} dd The drag drop object
69329          * @param {String} targetId The target drag drop object
69330          * @param {event} e The raw browser event
69331          */
69332         "dragdrop" : true,
69333         /**
69334          * @event dragover
69335          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
69336          * @param {Grid} this
69337          * @param {Roo.GridDD} dd The drag drop object
69338          * @param {String} targetId The target drag drop object
69339          * @param {event} e The raw browser event
69340          */
69341         "dragover" : true,
69342         /**
69343          * @event dragenter
69344          *  Fires when the dragged row(s) first cross another DD target while being dragged
69345          * @param {Grid} this
69346          * @param {Roo.GridDD} dd The drag drop object
69347          * @param {String} targetId The target drag drop object
69348          * @param {event} e The raw browser event
69349          */
69350         "dragenter" : true,
69351         /**
69352          * @event dragout
69353          * Fires when the dragged row(s) leave another DD target while being dragged
69354          * @param {Grid} this
69355          * @param {Roo.GridDD} dd The drag drop object
69356          * @param {String} targetId The target drag drop object
69357          * @param {event} e The raw browser event
69358          */
69359         "dragout" : true,
69360         /**
69361          * @event rowclass
69362          * Fires when a row is rendered, so you can change add a style to it.
69363          * @param {GridView} gridview   The grid view
69364          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
69365          */
69366         'rowclass' : true,
69367
69368         /**
69369          * @event render
69370          * Fires when the grid is rendered
69371          * @param {Grid} grid
69372          */
69373         'render' : true,
69374             /**
69375              * @event select
69376              * Fires when a date is selected
69377              * @param {DatePicker} this
69378              * @param {Date} date The selected date
69379              */
69380         'select': true,
69381         /**
69382              * @event monthchange
69383              * Fires when the displayed month changes 
69384              * @param {DatePicker} this
69385              * @param {Date} date The selected month
69386              */
69387         'monthchange': true,
69388         /**
69389              * @event evententer
69390              * Fires when mouse over an event
69391              * @param {Calendar} this
69392              * @param {event} Event
69393              */
69394         'evententer': true,
69395         /**
69396              * @event eventleave
69397              * Fires when the mouse leaves an
69398              * @param {Calendar} this
69399              * @param {event}
69400              */
69401         'eventleave': true,
69402         /**
69403              * @event eventclick
69404              * Fires when the mouse click an
69405              * @param {Calendar} this
69406              * @param {event}
69407              */
69408         'eventclick': true,
69409         /**
69410              * @event eventrender
69411              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
69412              * @param {Calendar} this
69413              * @param {data} data to be modified
69414              */
69415         'eventrender': true
69416         
69417     });
69418
69419     Roo.grid.Grid.superclass.constructor.call(this);
69420     this.on('render', function() {
69421         this.view.el.addClass('x-grid-cal'); 
69422         
69423         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
69424
69425     },this);
69426     
69427     if (!Roo.grid.Calendar.style) {
69428         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
69429             
69430             
69431             '.x-grid-cal .x-grid-col' :  {
69432                 height: 'auto !important',
69433                 'vertical-align': 'top'
69434             },
69435             '.x-grid-cal  .fc-event-hori' : {
69436                 height: '14px'
69437             }
69438              
69439             
69440         }, Roo.id());
69441     }
69442
69443     
69444     
69445 };
69446 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
69447     /**
69448      * @cfg {Store} eventStore The store that loads events.
69449      */
69450     eventStore : 25,
69451
69452      
69453     activeDate : false,
69454     startDay : 0,
69455     autoWidth : true,
69456     monitorWindowResize : false,
69457
69458     
69459     resizeColumns : function() {
69460         var col = (this.view.el.getWidth() / 7) - 3;
69461         // loop through cols, and setWidth
69462         for(var i =0 ; i < 7 ; i++){
69463             this.cm.setColumnWidth(i, col);
69464         }
69465     },
69466      setDate :function(date) {
69467         
69468         Roo.log('setDate?');
69469         
69470         this.resizeColumns();
69471         var vd = this.activeDate;
69472         this.activeDate = date;
69473 //        if(vd && this.el){
69474 //            var t = date.getTime();
69475 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
69476 //                Roo.log('using add remove');
69477 //                
69478 //                this.fireEvent('monthchange', this, date);
69479 //                
69480 //                this.cells.removeClass("fc-state-highlight");
69481 //                this.cells.each(function(c){
69482 //                   if(c.dateValue == t){
69483 //                       c.addClass("fc-state-highlight");
69484 //                       setTimeout(function(){
69485 //                            try{c.dom.firstChild.focus();}catch(e){}
69486 //                       }, 50);
69487 //                       return false;
69488 //                   }
69489 //                   return true;
69490 //                });
69491 //                return;
69492 //            }
69493 //        }
69494         
69495         var days = date.getDaysInMonth();
69496         
69497         var firstOfMonth = date.getFirstDateOfMonth();
69498         var startingPos = firstOfMonth.getDay()-this.startDay;
69499         
69500         if(startingPos < this.startDay){
69501             startingPos += 7;
69502         }
69503         
69504         var pm = date.add(Date.MONTH, -1);
69505         var prevStart = pm.getDaysInMonth()-startingPos;
69506 //        
69507         
69508         
69509         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
69510         
69511         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
69512         //this.cells.addClassOnOver('fc-state-hover');
69513         
69514         var cells = this.cells.elements;
69515         var textEls = this.textNodes;
69516         
69517         //Roo.each(cells, function(cell){
69518         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
69519         //});
69520         
69521         days += startingPos;
69522
69523         // convert everything to numbers so it's fast
69524         var day = 86400000;
69525         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
69526         //Roo.log(d);
69527         //Roo.log(pm);
69528         //Roo.log(prevStart);
69529         
69530         var today = new Date().clearTime().getTime();
69531         var sel = date.clearTime().getTime();
69532         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
69533         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
69534         var ddMatch = this.disabledDatesRE;
69535         var ddText = this.disabledDatesText;
69536         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
69537         var ddaysText = this.disabledDaysText;
69538         var format = this.format;
69539         
69540         var setCellClass = function(cal, cell){
69541             
69542             //Roo.log('set Cell Class');
69543             cell.title = "";
69544             var t = d.getTime();
69545             
69546             //Roo.log(d);
69547             
69548             
69549             cell.dateValue = t;
69550             if(t == today){
69551                 cell.className += " fc-today";
69552                 cell.className += " fc-state-highlight";
69553                 cell.title = cal.todayText;
69554             }
69555             if(t == sel){
69556                 // disable highlight in other month..
69557                 cell.className += " fc-state-highlight";
69558                 
69559             }
69560             // disabling
69561             if(t < min) {
69562                 //cell.className = " fc-state-disabled";
69563                 cell.title = cal.minText;
69564                 return;
69565             }
69566             if(t > max) {
69567                 //cell.className = " fc-state-disabled";
69568                 cell.title = cal.maxText;
69569                 return;
69570             }
69571             if(ddays){
69572                 if(ddays.indexOf(d.getDay()) != -1){
69573                     // cell.title = ddaysText;
69574                    // cell.className = " fc-state-disabled";
69575                 }
69576             }
69577             if(ddMatch && format){
69578                 var fvalue = d.dateFormat(format);
69579                 if(ddMatch.test(fvalue)){
69580                     cell.title = ddText.replace("%0", fvalue);
69581                    cell.className = " fc-state-disabled";
69582                 }
69583             }
69584             
69585             if (!cell.initialClassName) {
69586                 cell.initialClassName = cell.dom.className;
69587             }
69588             
69589             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
69590         };
69591
69592         var i = 0;
69593         
69594         for(; i < startingPos; i++) {
69595             cells[i].dayName =  (++prevStart);
69596             Roo.log(textEls[i]);
69597             d.setDate(d.getDate()+1);
69598             
69599             //cells[i].className = "fc-past fc-other-month";
69600             setCellClass(this, cells[i]);
69601         }
69602         
69603         var intDay = 0;
69604         
69605         for(; i < days; i++){
69606             intDay = i - startingPos + 1;
69607             cells[i].dayName =  (intDay);
69608             d.setDate(d.getDate()+1);
69609             
69610             cells[i].className = ''; // "x-date-active";
69611             setCellClass(this, cells[i]);
69612         }
69613         var extraDays = 0;
69614         
69615         for(; i < 42; i++) {
69616             //textEls[i].innerHTML = (++extraDays);
69617             
69618             d.setDate(d.getDate()+1);
69619             cells[i].dayName = (++extraDays);
69620             cells[i].className = "fc-future fc-other-month";
69621             setCellClass(this, cells[i]);
69622         }
69623         
69624         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
69625         
69626         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
69627         
69628         // this will cause all the cells to mis
69629         var rows= [];
69630         var i =0;
69631         for (var r = 0;r < 6;r++) {
69632             for (var c =0;c < 7;c++) {
69633                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
69634             }    
69635         }
69636         
69637         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
69638         for(i=0;i<cells.length;i++) {
69639             
69640             this.cells.elements[i].dayName = cells[i].dayName ;
69641             this.cells.elements[i].className = cells[i].className;
69642             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
69643             this.cells.elements[i].title = cells[i].title ;
69644             this.cells.elements[i].dateValue = cells[i].dateValue ;
69645         }
69646         
69647         
69648         
69649         
69650         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
69651         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
69652         
69653         ////if(totalRows != 6){
69654             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
69655            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
69656        // }
69657         
69658         this.fireEvent('monthchange', this, date);
69659         
69660         
69661     },
69662  /**
69663      * Returns the grid's SelectionModel.
69664      * @return {SelectionModel}
69665      */
69666     getSelectionModel : function(){
69667         if(!this.selModel){
69668             this.selModel = new Roo.grid.CellSelectionModel();
69669         }
69670         return this.selModel;
69671     },
69672
69673     load: function() {
69674         this.eventStore.load()
69675         
69676         
69677         
69678     },
69679     
69680     findCell : function(dt) {
69681         dt = dt.clearTime().getTime();
69682         var ret = false;
69683         this.cells.each(function(c){
69684             //Roo.log("check " +c.dateValue + '?=' + dt);
69685             if(c.dateValue == dt){
69686                 ret = c;
69687                 return false;
69688             }
69689             return true;
69690         });
69691         
69692         return ret;
69693     },
69694     
69695     findCells : function(rec) {
69696         var s = rec.data.start_dt.clone().clearTime().getTime();
69697        // Roo.log(s);
69698         var e= rec.data.end_dt.clone().clearTime().getTime();
69699        // Roo.log(e);
69700         var ret = [];
69701         this.cells.each(function(c){
69702              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
69703             
69704             if(c.dateValue > e){
69705                 return ;
69706             }
69707             if(c.dateValue < s){
69708                 return ;
69709             }
69710             ret.push(c);
69711         });
69712         
69713         return ret;    
69714     },
69715     
69716     findBestRow: function(cells)
69717     {
69718         var ret = 0;
69719         
69720         for (var i =0 ; i < cells.length;i++) {
69721             ret  = Math.max(cells[i].rows || 0,ret);
69722         }
69723         return ret;
69724         
69725     },
69726     
69727     
69728     addItem : function(rec)
69729     {
69730         // look for vertical location slot in
69731         var cells = this.findCells(rec);
69732         
69733         rec.row = this.findBestRow(cells);
69734         
69735         // work out the location.
69736         
69737         var crow = false;
69738         var rows = [];
69739         for(var i =0; i < cells.length; i++) {
69740             if (!crow) {
69741                 crow = {
69742                     start : cells[i],
69743                     end :  cells[i]
69744                 };
69745                 continue;
69746             }
69747             if (crow.start.getY() == cells[i].getY()) {
69748                 // on same row.
69749                 crow.end = cells[i];
69750                 continue;
69751             }
69752             // different row.
69753             rows.push(crow);
69754             crow = {
69755                 start: cells[i],
69756                 end : cells[i]
69757             };
69758             
69759         }
69760         
69761         rows.push(crow);
69762         rec.els = [];
69763         rec.rows = rows;
69764         rec.cells = cells;
69765         for (var i = 0; i < cells.length;i++) {
69766             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
69767             
69768         }
69769         
69770         
69771     },
69772     
69773     clearEvents: function() {
69774         
69775         if (!this.eventStore.getCount()) {
69776             return;
69777         }
69778         // reset number of rows in cells.
69779         Roo.each(this.cells.elements, function(c){
69780             c.rows = 0;
69781         });
69782         
69783         this.eventStore.each(function(e) {
69784             this.clearEvent(e);
69785         },this);
69786         
69787     },
69788     
69789     clearEvent : function(ev)
69790     {
69791         if (ev.els) {
69792             Roo.each(ev.els, function(el) {
69793                 el.un('mouseenter' ,this.onEventEnter, this);
69794                 el.un('mouseleave' ,this.onEventLeave, this);
69795                 el.remove();
69796             },this);
69797             ev.els = [];
69798         }
69799     },
69800     
69801     
69802     renderEvent : function(ev,ctr) {
69803         if (!ctr) {
69804              ctr = this.view.el.select('.fc-event-container',true).first();
69805         }
69806         
69807          
69808         this.clearEvent(ev);
69809             //code
69810        
69811         
69812         
69813         ev.els = [];
69814         var cells = ev.cells;
69815         var rows = ev.rows;
69816         this.fireEvent('eventrender', this, ev);
69817         
69818         for(var i =0; i < rows.length; i++) {
69819             
69820             cls = '';
69821             if (i == 0) {
69822                 cls += ' fc-event-start';
69823             }
69824             if ((i+1) == rows.length) {
69825                 cls += ' fc-event-end';
69826             }
69827             
69828             //Roo.log(ev.data);
69829             // how many rows should it span..
69830             var cg = this.eventTmpl.append(ctr,Roo.apply({
69831                 fccls : cls
69832                 
69833             }, ev.data) , true);
69834             
69835             
69836             cg.on('mouseenter' ,this.onEventEnter, this, ev);
69837             cg.on('mouseleave' ,this.onEventLeave, this, ev);
69838             cg.on('click', this.onEventClick, this, ev);
69839             
69840             ev.els.push(cg);
69841             
69842             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
69843             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
69844             //Roo.log(cg);
69845              
69846             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
69847             cg.setWidth(ebox.right - sbox.x -2);
69848         }
69849     },
69850     
69851     renderEvents: function()
69852     {   
69853         // first make sure there is enough space..
69854         
69855         if (!this.eventTmpl) {
69856             this.eventTmpl = new Roo.Template(
69857                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
69858                     '<div class="fc-event-inner">' +
69859                         '<span class="fc-event-time">{time}</span>' +
69860                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
69861                     '</div>' +
69862                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
69863                 '</div>'
69864             );
69865                 
69866         }
69867                
69868         
69869         
69870         this.cells.each(function(c) {
69871             //Roo.log(c.select('.fc-day-content div',true).first());
69872             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
69873         });
69874         
69875         var ctr = this.view.el.select('.fc-event-container',true).first();
69876         
69877         var cls;
69878         this.eventStore.each(function(ev){
69879             
69880             this.renderEvent(ev);
69881              
69882              
69883         }, this);
69884         this.view.layout();
69885         
69886     },
69887     
69888     onEventEnter: function (e, el,event,d) {
69889         this.fireEvent('evententer', this, el, event);
69890     },
69891     
69892     onEventLeave: function (e, el,event,d) {
69893         this.fireEvent('eventleave', this, el, event);
69894     },
69895     
69896     onEventClick: function (e, el,event,d) {
69897         this.fireEvent('eventclick', this, el, event);
69898     },
69899     
69900     onMonthChange: function () {
69901         this.store.load();
69902     },
69903     
69904     onLoad: function () {
69905         
69906         //Roo.log('calendar onload');
69907 //         
69908         if(this.eventStore.getCount() > 0){
69909             
69910            
69911             
69912             this.eventStore.each(function(d){
69913                 
69914                 
69915                 // FIXME..
69916                 var add =   d.data;
69917                 if (typeof(add.end_dt) == 'undefined')  {
69918                     Roo.log("Missing End time in calendar data: ");
69919                     Roo.log(d);
69920                     return;
69921                 }
69922                 if (typeof(add.start_dt) == 'undefined')  {
69923                     Roo.log("Missing Start time in calendar data: ");
69924                     Roo.log(d);
69925                     return;
69926                 }
69927                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
69928                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
69929                 add.id = add.id || d.id;
69930                 add.title = add.title || '??';
69931                 
69932                 this.addItem(d);
69933                 
69934              
69935             },this);
69936         }
69937         
69938         this.renderEvents();
69939     }
69940     
69941
69942 });
69943 /*
69944  grid : {
69945                 xtype: 'Grid',
69946                 xns: Roo.grid,
69947                 listeners : {
69948                     render : function ()
69949                     {
69950                         _this.grid = this;
69951                         
69952                         if (!this.view.el.hasClass('course-timesheet')) {
69953                             this.view.el.addClass('course-timesheet');
69954                         }
69955                         if (this.tsStyle) {
69956                             this.ds.load({});
69957                             return; 
69958                         }
69959                         Roo.log('width');
69960                         Roo.log(_this.grid.view.el.getWidth());
69961                         
69962                         
69963                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
69964                             '.course-timesheet .x-grid-row' : {
69965                                 height: '80px'
69966                             },
69967                             '.x-grid-row td' : {
69968                                 'vertical-align' : 0
69969                             },
69970                             '.course-edit-link' : {
69971                                 'color' : 'blue',
69972                                 'text-overflow' : 'ellipsis',
69973                                 'overflow' : 'hidden',
69974                                 'white-space' : 'nowrap',
69975                                 'cursor' : 'pointer'
69976                             },
69977                             '.sub-link' : {
69978                                 'color' : 'green'
69979                             },
69980                             '.de-act-sup-link' : {
69981                                 'color' : 'purple',
69982                                 'text-decoration' : 'line-through'
69983                             },
69984                             '.de-act-link' : {
69985                                 'color' : 'red',
69986                                 'text-decoration' : 'line-through'
69987                             },
69988                             '.course-timesheet .course-highlight' : {
69989                                 'border-top-style': 'dashed !important',
69990                                 'border-bottom-bottom': 'dashed !important'
69991                             },
69992                             '.course-timesheet .course-item' : {
69993                                 'font-family'   : 'tahoma, arial, helvetica',
69994                                 'font-size'     : '11px',
69995                                 'overflow'      : 'hidden',
69996                                 'padding-left'  : '10px',
69997                                 'padding-right' : '10px',
69998                                 'padding-top' : '10px' 
69999                             }
70000                             
70001                         }, Roo.id());
70002                                 this.ds.load({});
70003                     }
70004                 },
70005                 autoWidth : true,
70006                 monitorWindowResize : false,
70007                 cellrenderer : function(v,x,r)
70008                 {
70009                     return v;
70010                 },
70011                 sm : {
70012                     xtype: 'CellSelectionModel',
70013                     xns: Roo.grid
70014                 },
70015                 dataSource : {
70016                     xtype: 'Store',
70017                     xns: Roo.data,
70018                     listeners : {
70019                         beforeload : function (_self, options)
70020                         {
70021                             options.params = options.params || {};
70022                             options.params._month = _this.monthField.getValue();
70023                             options.params.limit = 9999;
70024                             options.params['sort'] = 'when_dt';    
70025                             options.params['dir'] = 'ASC';    
70026                             this.proxy.loadResponse = this.loadResponse;
70027                             Roo.log("load?");
70028                             //this.addColumns();
70029                         },
70030                         load : function (_self, records, options)
70031                         {
70032                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
70033                                 // if you click on the translation.. you can edit it...
70034                                 var el = Roo.get(this);
70035                                 var id = el.dom.getAttribute('data-id');
70036                                 var d = el.dom.getAttribute('data-date');
70037                                 var t = el.dom.getAttribute('data-time');
70038                                 //var id = this.child('span').dom.textContent;
70039                                 
70040                                 //Roo.log(this);
70041                                 Pman.Dialog.CourseCalendar.show({
70042                                     id : id,
70043                                     when_d : d,
70044                                     when_t : t,
70045                                     productitem_active : id ? 1 : 0
70046                                 }, function() {
70047                                     _this.grid.ds.load({});
70048                                 });
70049                            
70050                            });
70051                            
70052                            _this.panel.fireEvent('resize', [ '', '' ]);
70053                         }
70054                     },
70055                     loadResponse : function(o, success, response){
70056                             // this is overridden on before load..
70057                             
70058                             Roo.log("our code?");       
70059                             //Roo.log(success);
70060                             //Roo.log(response)
70061                             delete this.activeRequest;
70062                             if(!success){
70063                                 this.fireEvent("loadexception", this, o, response);
70064                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
70065                                 return;
70066                             }
70067                             var result;
70068                             try {
70069                                 result = o.reader.read(response);
70070                             }catch(e){
70071                                 Roo.log("load exception?");
70072                                 this.fireEvent("loadexception", this, o, response, e);
70073                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
70074                                 return;
70075                             }
70076                             Roo.log("ready...");        
70077                             // loop through result.records;
70078                             // and set this.tdate[date] = [] << array of records..
70079                             _this.tdata  = {};
70080                             Roo.each(result.records, function(r){
70081                                 //Roo.log(r.data);
70082                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
70083                                     _this.tdata[r.data.when_dt.format('j')] = [];
70084                                 }
70085                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
70086                             });
70087                             
70088                             //Roo.log(_this.tdata);
70089                             
70090                             result.records = [];
70091                             result.totalRecords = 6;
70092                     
70093                             // let's generate some duumy records for the rows.
70094                             //var st = _this.dateField.getValue();
70095                             
70096                             // work out monday..
70097                             //st = st.add(Date.DAY, -1 * st.format('w'));
70098                             
70099                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
70100                             
70101                             var firstOfMonth = date.getFirstDayOfMonth();
70102                             var days = date.getDaysInMonth();
70103                             var d = 1;
70104                             var firstAdded = false;
70105                             for (var i = 0; i < result.totalRecords ; i++) {
70106                                 //var d= st.add(Date.DAY, i);
70107                                 var row = {};
70108                                 var added = 0;
70109                                 for(var w = 0 ; w < 7 ; w++){
70110                                     if(!firstAdded && firstOfMonth != w){
70111                                         continue;
70112                                     }
70113                                     if(d > days){
70114                                         continue;
70115                                     }
70116                                     firstAdded = true;
70117                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
70118                                     row['weekday'+w] = String.format(
70119                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
70120                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
70121                                                     d,
70122                                                     date.format('Y-m-')+dd
70123                                                 );
70124                                     added++;
70125                                     if(typeof(_this.tdata[d]) != 'undefined'){
70126                                         Roo.each(_this.tdata[d], function(r){
70127                                             var is_sub = '';
70128                                             var deactive = '';
70129                                             var id = r.id;
70130                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
70131                                             if(r.parent_id*1>0){
70132                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
70133                                                 id = r.parent_id;
70134                                             }
70135                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
70136                                                 deactive = 'de-act-link';
70137                                             }
70138                                             
70139                                             row['weekday'+w] += String.format(
70140                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
70141                                                     id, //0
70142                                                     r.product_id_name, //1
70143                                                     r.when_dt.format('h:ia'), //2
70144                                                     is_sub, //3
70145                                                     deactive, //4
70146                                                     desc // 5
70147                                             );
70148                                         });
70149                                     }
70150                                     d++;
70151                                 }
70152                                 
70153                                 // only do this if something added..
70154                                 if(added > 0){ 
70155                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
70156                                 }
70157                                 
70158                                 
70159                                 // push it twice. (second one with an hour..
70160                                 
70161                             }
70162                             //Roo.log(result);
70163                             this.fireEvent("load", this, o, o.request.arg);
70164                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
70165                         },
70166                     sortInfo : {field: 'when_dt', direction : 'ASC' },
70167                     proxy : {
70168                         xtype: 'HttpProxy',
70169                         xns: Roo.data,
70170                         method : 'GET',
70171                         url : baseURL + '/Roo/Shop_course.php'
70172                     },
70173                     reader : {
70174                         xtype: 'JsonReader',
70175                         xns: Roo.data,
70176                         id : 'id',
70177                         fields : [
70178                             {
70179                                 'name': 'id',
70180                                 'type': 'int'
70181                             },
70182                             {
70183                                 'name': 'when_dt',
70184                                 'type': 'string'
70185                             },
70186                             {
70187                                 'name': 'end_dt',
70188                                 'type': 'string'
70189                             },
70190                             {
70191                                 'name': 'parent_id',
70192                                 'type': 'int'
70193                             },
70194                             {
70195                                 'name': 'product_id',
70196                                 'type': 'int'
70197                             },
70198                             {
70199                                 'name': 'productitem_id',
70200                                 'type': 'int'
70201                             },
70202                             {
70203                                 'name': 'guid',
70204                                 'type': 'int'
70205                             }
70206                         ]
70207                     }
70208                 },
70209                 toolbar : {
70210                     xtype: 'Toolbar',
70211                     xns: Roo,
70212                     items : [
70213                         {
70214                             xtype: 'Button',
70215                             xns: Roo.Toolbar,
70216                             listeners : {
70217                                 click : function (_self, e)
70218                                 {
70219                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
70220                                     sd.setMonth(sd.getMonth()-1);
70221                                     _this.monthField.setValue(sd.format('Y-m-d'));
70222                                     _this.grid.ds.load({});
70223                                 }
70224                             },
70225                             text : "Back"
70226                         },
70227                         {
70228                             xtype: 'Separator',
70229                             xns: Roo.Toolbar
70230                         },
70231                         {
70232                             xtype: 'MonthField',
70233                             xns: Roo.form,
70234                             listeners : {
70235                                 render : function (_self)
70236                                 {
70237                                     _this.monthField = _self;
70238                                    // _this.monthField.set  today
70239                                 },
70240                                 select : function (combo, date)
70241                                 {
70242                                     _this.grid.ds.load({});
70243                                 }
70244                             },
70245                             value : (function() { return new Date(); })()
70246                         },
70247                         {
70248                             xtype: 'Separator',
70249                             xns: Roo.Toolbar
70250                         },
70251                         {
70252                             xtype: 'TextItem',
70253                             xns: Roo.Toolbar,
70254                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
70255                         },
70256                         {
70257                             xtype: 'Fill',
70258                             xns: Roo.Toolbar
70259                         },
70260                         {
70261                             xtype: 'Button',
70262                             xns: Roo.Toolbar,
70263                             listeners : {
70264                                 click : function (_self, e)
70265                                 {
70266                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
70267                                     sd.setMonth(sd.getMonth()+1);
70268                                     _this.monthField.setValue(sd.format('Y-m-d'));
70269                                     _this.grid.ds.load({});
70270                                 }
70271                             },
70272                             text : "Next"
70273                         }
70274                     ]
70275                 },
70276                  
70277             }
70278         };
70279         
70280         *//*
70281  * Based on:
70282  * Ext JS Library 1.1.1
70283  * Copyright(c) 2006-2007, Ext JS, LLC.
70284  *
70285  * Originally Released Under LGPL - original licence link has changed is not relivant.
70286  *
70287  * Fork - LGPL
70288  * <script type="text/javascript">
70289  */
70290  
70291 /**
70292  * @class Roo.LoadMask
70293  * A simple utility class for generically masking elements while loading data.  If the element being masked has
70294  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
70295  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
70296  * element's UpdateManager load indicator and will be destroyed after the initial load.
70297  * @constructor
70298  * Create a new LoadMask
70299  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
70300  * @param {Object} config The config object
70301  */
70302 Roo.LoadMask = function(el, config){
70303     this.el = Roo.get(el);
70304     Roo.apply(this, config);
70305     if(this.store){
70306         this.store.on('beforeload', this.onBeforeLoad, this);
70307         this.store.on('load', this.onLoad, this);
70308         this.store.on('loadexception', this.onLoadException, this);
70309         this.removeMask = false;
70310     }else{
70311         var um = this.el.getUpdateManager();
70312         um.showLoadIndicator = false; // disable the default indicator
70313         um.on('beforeupdate', this.onBeforeLoad, this);
70314         um.on('update', this.onLoad, this);
70315         um.on('failure', this.onLoad, this);
70316         this.removeMask = true;
70317     }
70318 };
70319
70320 Roo.LoadMask.prototype = {
70321     /**
70322      * @cfg {Boolean} removeMask
70323      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
70324      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
70325      */
70326     removeMask : false,
70327     /**
70328      * @cfg {String} msg
70329      * The text to display in a centered loading message box (defaults to 'Loading...')
70330      */
70331     msg : 'Loading...',
70332     /**
70333      * @cfg {String} msgCls
70334      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
70335      */
70336     msgCls : 'x-mask-loading',
70337
70338     /**
70339      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
70340      * @type Boolean
70341      */
70342     disabled: false,
70343
70344     /**
70345      * Disables the mask to prevent it from being displayed
70346      */
70347     disable : function(){
70348        this.disabled = true;
70349     },
70350
70351     /**
70352      * Enables the mask so that it can be displayed
70353      */
70354     enable : function(){
70355         this.disabled = false;
70356     },
70357     
70358     onLoadException : function()
70359     {
70360         Roo.log(arguments);
70361         
70362         if (typeof(arguments[3]) != 'undefined') {
70363             Roo.MessageBox.alert("Error loading",arguments[3]);
70364         } 
70365         /*
70366         try {
70367             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
70368                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
70369             }   
70370         } catch(e) {
70371             
70372         }
70373         */
70374     
70375         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
70376     },
70377     // private
70378     onLoad : function()
70379     {
70380         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
70381     },
70382
70383     // private
70384     onBeforeLoad : function(){
70385         if(!this.disabled){
70386             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
70387         }
70388     },
70389
70390     // private
70391     destroy : function(){
70392         if(this.store){
70393             this.store.un('beforeload', this.onBeforeLoad, this);
70394             this.store.un('load', this.onLoad, this);
70395             this.store.un('loadexception', this.onLoadException, this);
70396         }else{
70397             var um = this.el.getUpdateManager();
70398             um.un('beforeupdate', this.onBeforeLoad, this);
70399             um.un('update', this.onLoad, this);
70400             um.un('failure', this.onLoad, this);
70401         }
70402     }
70403 };/*
70404  * Based on:
70405  * Ext JS Library 1.1.1
70406  * Copyright(c) 2006-2007, Ext JS, LLC.
70407  *
70408  * Originally Released Under LGPL - original licence link has changed is not relivant.
70409  *
70410  * Fork - LGPL
70411  * <script type="text/javascript">
70412  */
70413
70414
70415 /**
70416  * @class Roo.XTemplate
70417  * @extends Roo.Template
70418  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
70419 <pre><code>
70420 var t = new Roo.XTemplate(
70421         '&lt;select name="{name}"&gt;',
70422                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
70423         '&lt;/select&gt;'
70424 );
70425  
70426 // then append, applying the master template values
70427  </code></pre>
70428  *
70429  * Supported features:
70430  *
70431  *  Tags:
70432
70433 <pre><code>
70434       {a_variable} - output encoded.
70435       {a_variable.format:("Y-m-d")} - call a method on the variable
70436       {a_variable:raw} - unencoded output
70437       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
70438       {a_variable:this.method_on_template(...)} - call a method on the template object.
70439  
70440 </code></pre>
70441  *  The tpl tag:
70442 <pre><code>
70443         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
70444         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
70445         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
70446         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
70447   
70448         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
70449         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
70450 </code></pre>
70451  *      
70452  */
70453 Roo.XTemplate = function()
70454 {
70455     Roo.XTemplate.superclass.constructor.apply(this, arguments);
70456     if (this.html) {
70457         this.compile();
70458     }
70459 };
70460
70461
70462 Roo.extend(Roo.XTemplate, Roo.Template, {
70463
70464     /**
70465      * The various sub templates
70466      */
70467     tpls : false,
70468     /**
70469      *
70470      * basic tag replacing syntax
70471      * WORD:WORD()
70472      *
70473      * // you can fake an object call by doing this
70474      *  x.t:(test,tesT) 
70475      * 
70476      */
70477     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
70478
70479     /**
70480      * compile the template
70481      *
70482      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
70483      *
70484      */
70485     compile: function()
70486     {
70487         var s = this.html;
70488      
70489         s = ['<tpl>', s, '</tpl>'].join('');
70490     
70491         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
70492             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
70493             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
70494             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
70495             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
70496             m,
70497             id     = 0,
70498             tpls   = [];
70499     
70500         while(true == !!(m = s.match(re))){
70501             var forMatch   = m[0].match(nameRe),
70502                 ifMatch   = m[0].match(ifRe),
70503                 execMatch   = m[0].match(execRe),
70504                 namedMatch   = m[0].match(namedRe),
70505                 
70506                 exp  = null, 
70507                 fn   = null,
70508                 exec = null,
70509                 name = forMatch && forMatch[1] ? forMatch[1] : '';
70510                 
70511             if (ifMatch) {
70512                 // if - puts fn into test..
70513                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
70514                 if(exp){
70515                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
70516                 }
70517             }
70518             
70519             if (execMatch) {
70520                 // exec - calls a function... returns empty if true is  returned.
70521                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
70522                 if(exp){
70523                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
70524                 }
70525             }
70526             
70527             
70528             if (name) {
70529                 // for = 
70530                 switch(name){
70531                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
70532                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
70533                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
70534                 }
70535             }
70536             var uid = namedMatch ? namedMatch[1] : id;
70537             
70538             
70539             tpls.push({
70540                 id:     namedMatch ? namedMatch[1] : id,
70541                 target: name,
70542                 exec:   exec,
70543                 test:   fn,
70544                 body:   m[1] || ''
70545             });
70546             if (namedMatch) {
70547                 s = s.replace(m[0], '');
70548             } else { 
70549                 s = s.replace(m[0], '{xtpl'+ id + '}');
70550             }
70551             ++id;
70552         }
70553         this.tpls = [];
70554         for(var i = tpls.length-1; i >= 0; --i){
70555             this.compileTpl(tpls[i]);
70556             this.tpls[tpls[i].id] = tpls[i];
70557         }
70558         this.master = tpls[tpls.length-1];
70559         return this;
70560     },
70561     /**
70562      * same as applyTemplate, except it's done to one of the subTemplates
70563      * when using named templates, you can do:
70564      *
70565      * var str = pl.applySubTemplate('your-name', values);
70566      *
70567      * 
70568      * @param {Number} id of the template
70569      * @param {Object} values to apply to template
70570      * @param {Object} parent (normaly the instance of this object)
70571      */
70572     applySubTemplate : function(id, values, parent)
70573     {
70574         
70575         
70576         var t = this.tpls[id];
70577         
70578         
70579         try { 
70580             if(t.test && !t.test.call(this, values, parent)){
70581                 return '';
70582             }
70583         } catch(e) {
70584             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
70585             Roo.log(e.toString());
70586             Roo.log(t.test);
70587             return ''
70588         }
70589         try { 
70590             
70591             if(t.exec && t.exec.call(this, values, parent)){
70592                 return '';
70593             }
70594         } catch(e) {
70595             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
70596             Roo.log(e.toString());
70597             Roo.log(t.exec);
70598             return ''
70599         }
70600         try {
70601             var vs = t.target ? t.target.call(this, values, parent) : values;
70602             parent = t.target ? values : parent;
70603             if(t.target && vs instanceof Array){
70604                 var buf = [];
70605                 for(var i = 0, len = vs.length; i < len; i++){
70606                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
70607                 }
70608                 return buf.join('');
70609             }
70610             return t.compiled.call(this, vs, parent);
70611         } catch (e) {
70612             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
70613             Roo.log(e.toString());
70614             Roo.log(t.compiled);
70615             return '';
70616         }
70617     },
70618
70619     compileTpl : function(tpl)
70620     {
70621         var fm = Roo.util.Format;
70622         var useF = this.disableFormats !== true;
70623         var sep = Roo.isGecko ? "+" : ",";
70624         var undef = function(str) {
70625             Roo.log("Property not found :"  + str);
70626             return '';
70627         };
70628         
70629         var fn = function(m, name, format, args)
70630         {
70631             //Roo.log(arguments);
70632             args = args ? args.replace(/\\'/g,"'") : args;
70633             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
70634             if (typeof(format) == 'undefined') {
70635                 format= 'htmlEncode';
70636             }
70637             if (format == 'raw' ) {
70638                 format = false;
70639             }
70640             
70641             if(name.substr(0, 4) == 'xtpl'){
70642                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
70643             }
70644             
70645             // build an array of options to determine if value is undefined..
70646             
70647             // basically get 'xxxx.yyyy' then do
70648             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
70649             //    (function () { Roo.log("Property not found"); return ''; })() :
70650             //    ......
70651             
70652             var udef_ar = [];
70653             var lookfor = '';
70654             Roo.each(name.split('.'), function(st) {
70655                 lookfor += (lookfor.length ? '.': '') + st;
70656                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
70657             });
70658             
70659             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
70660             
70661             
70662             if(format && useF){
70663                 
70664                 args = args ? ',' + args : "";
70665                  
70666                 if(format.substr(0, 5) != "this."){
70667                     format = "fm." + format + '(';
70668                 }else{
70669                     format = 'this.call("'+ format.substr(5) + '", ';
70670                     args = ", values";
70671                 }
70672                 
70673                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
70674             }
70675              
70676             if (args.length) {
70677                 // called with xxyx.yuu:(test,test)
70678                 // change to ()
70679                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
70680             }
70681             // raw.. - :raw modifier..
70682             return "'"+ sep + udef_st  + name + ")"+sep+"'";
70683             
70684         };
70685         var body;
70686         // branched to use + in gecko and [].join() in others
70687         if(Roo.isGecko){
70688             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
70689                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
70690                     "';};};";
70691         }else{
70692             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
70693             body.push(tpl.body.replace(/(\r\n|\n)/g,
70694                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
70695             body.push("'].join('');};};");
70696             body = body.join('');
70697         }
70698         
70699         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
70700        
70701         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
70702         eval(body);
70703         
70704         return this;
70705     },
70706
70707     applyTemplate : function(values){
70708         return this.master.compiled.call(this, values, {});
70709         //var s = this.subs;
70710     },
70711
70712     apply : function(){
70713         return this.applyTemplate.apply(this, arguments);
70714     }
70715
70716  });
70717
70718 Roo.XTemplate.from = function(el){
70719     el = Roo.getDom(el);
70720     return new Roo.XTemplate(el.value || el.innerHTML);
70721 };// old names for panel elements
70722 Roo.GridPanel = Roo.panel.Grid;
70723 Roo.CalendarPanel = Roo.panel.Calendar;
70724 Roo.ContentPanel = Roo.panel.Content;
70725 Roo.NestedLayoutPanel = Roo.panel.NestedLayout;
70726 Roo.TabPanel = Roo.panel.Tab;
70727 Roo.TabPanelItem = Roo.panel.TabItem;
70728 Roo.TreePanel = Roo.panel.Tree;
70729
70730
70731 // phase 2 update
70732 Roo.ScrollPanel = Roo.panel.Scroll;
70733
70734 Roo.BorderLayout = Roo.layout.Border;
70735 Roo.BasicLayoutRegion = Roo.layout.BasicRegion;
70736 Roo.LayoutRegion = Roo.layout.Region;
70737 Roo.SplitLayoutRegion = Roo.layout.SplitRegion;
70738 Roo.LayoutManager = Roo.layout.Manager;
70739
70740
70741 Roo.NorthLayoutRegion = Roo.layout.North;
70742 Roo.EastLayoutRegion = Roo.layout.East;
70743 Roo.WestLayoutRegion = Roo.layout.West;
70744 Roo.SouthLayoutRegion = Roo.layout.South;
70745 Roo.CenterLayoutRegion = Roo.layout.Center;
70746
70747
70748 Roo.LayoutStateManager  = Roo.layout.StateManager;
70749 Roo.ReaderLayout = Roo.layout.Reader;
70750