fix pasted email address with white space and getting validated
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @static
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isFirefox = ua.indexOf("firefox") > -1,
57         isIE = ua.indexOf("msie") > -1,
58         isIE7 = ua.indexOf("msie 7") > -1,
59         isIE11 = /trident.*rv\:11\./.test(ua),
60         isEdge = ua.indexOf("edge") > -1,
61         isGecko = !isSafari && ua.indexOf("gecko") > -1,
62         isBorderBox = isIE && !isStrict,
63         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
64         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
65         isLinux = (ua.indexOf("linux") != -1),
66         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
67         isIOS = /iphone|ipad/.test(ua),
68         isAndroid = /android/.test(ua),
69         isTouch =  (function() {
70             try {
71                 if (ua.indexOf('chrome') != -1 && ua.indexOf('android') == -1) {
72                     window.addEventListener('touchstart', function __set_has_touch__ () {
73                         Roo.isTouch = true;
74                         window.removeEventListener('touchstart', __set_has_touch__);
75                     });
76                     return false; // no touch on chrome!?
77                 }
78                 document.createEvent("TouchEvent");  
79                 return true;  
80             } catch (e) {  
81                 return false;  
82             } 
83             
84         })();
85     // remove css image flicker
86         if(isIE && !isIE7){
87         try{
88             document.execCommand("BackgroundImageCache", false, true);
89         }catch(e){}
90     }
91     
92     Roo.apply(Roo, {
93         /**
94          * True if the browser is in strict mode
95          * @type Boolean
96          */
97         isStrict : isStrict,
98         /**
99          * True if the page is running over SSL
100          * @type Boolean
101          */
102         isSecure : isSecure,
103         /**
104          * True when the document is fully initialized and ready for action
105          * @type Boolean
106          */
107         isReady : false,
108         /**
109          * Turn on debugging output (currently only the factory uses this)
110          * @type Boolean
111          */
112         
113         debug: false,
114
115         /**
116          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
117          * @type Boolean
118          */
119         enableGarbageCollector : true,
120
121         /**
122          * True to automatically purge event listeners after uncaching an element (defaults to false).
123          * Note: this only happens if enableGarbageCollector is true.
124          * @type Boolean
125          */
126         enableListenerCollection:false,
127
128         /**
129          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
130          * the IE insecure content warning (defaults to javascript:false).
131          * @type String
132          */
133         SSL_SECURE_URL : "javascript:false",
134
135         /**
136          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
137          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
138          * @type String
139          */
140         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
141
142         emptyFn : function(){},
143         
144         /**
145          * Copies all the properties of config to obj if they don't already exist.
146          * @param {Object} obj The receiver of the properties
147          * @param {Object} config The source of the properties
148          * @return {Object} returns obj
149          */
150         applyIf : function(o, c){
151             if(o && c){
152                 for(var p in c){
153                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
154                 }
155             }
156             return o;
157         },
158
159         /**
160          * Applies event listeners to elements by selectors when the document is ready.
161          * The event name is specified with an @ suffix.
162 <pre><code>
163 Roo.addBehaviors({
164    // add a listener for click on all anchors in element with id foo
165    '#foo a@click' : function(e, t){
166        // do something
167    },
168
169    // add the same listener to multiple selectors (separated by comma BEFORE the @)
170    '#foo a, #bar span.some-class@mouseover' : function(){
171        // do something
172    }
173 });
174 </code></pre>
175          * @param {Object} obj The list of behaviors to apply
176          */
177         addBehaviors : function(o){
178             if(!Roo.isReady){
179                 Roo.onReady(function(){
180                     Roo.addBehaviors(o);
181                 });
182                 return;
183             }
184             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
185             for(var b in o){
186                 var parts = b.split('@');
187                 if(parts[1]){ // for Object prototype breakers
188                     var s = parts[0];
189                     if(!cache[s]){
190                         cache[s] = Roo.select(s);
191                     }
192                     cache[s].on(parts[1], o[b]);
193                 }
194             }
195             cache = null;
196         },
197
198         /**
199          * Generates unique ids. If the element already has an id, it is unchanged
200          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
201          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
202          * @return {String} The generated Id.
203          */
204         id : function(el, prefix){
205             prefix = prefix || "roo-gen";
206             el = Roo.getDom(el);
207             var id = prefix + (++idSeed);
208             return el ? (el.id ? el.id : (el.id = id)) : id;
209         },
210          
211        
212         /**
213          * Extends one class with another class and optionally overrides members with the passed literal. This class
214          * also adds the function "override()" to the class that can be used to override
215          * members on an instance.
216          * @param {Object} subclass The class inheriting the functionality
217          * @param {Object} superclass The class being extended
218          * @param {Object} overrides (optional) A literal with members
219          * @method extend
220          */
221         extend : function(){
222             // inline overrides
223             var io = function(o){
224                 for(var m in o){
225                     this[m] = o[m];
226                 }
227             };
228             return function(sb, sp, overrides){
229                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
230                     overrides = sp;
231                     sp = sb;
232                     sb = function(){sp.apply(this, arguments);};
233                 }
234                 var F = function(){}, sbp, spp = sp.prototype;
235                 F.prototype = spp;
236                 sbp = sb.prototype = new F();
237                 sbp.constructor=sb;
238                 sb.superclass=spp;
239                 
240                 if(spp.constructor == Object.prototype.constructor){
241                     spp.constructor=sp;
242                    
243                 }
244                 
245                 sb.override = function(o){
246                     Roo.override(sb, o);
247                 };
248                 sbp.override = io;
249                 Roo.override(sb, overrides);
250                 return sb;
251             };
252         }(),
253
254         /**
255          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
256          * Usage:<pre><code>
257 Roo.override(MyClass, {
258     newMethod1: function(){
259         // etc.
260     },
261     newMethod2: function(foo){
262         // etc.
263     }
264 });
265  </code></pre>
266          * @param {Object} origclass The class to override
267          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
268          * containing one or more methods.
269          * @method override
270          */
271         override : function(origclass, overrides){
272             if(overrides){
273                 var p = origclass.prototype;
274                 for(var method in overrides){
275                     p[method] = overrides[method];
276                 }
277             }
278         },
279         /**
280          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
281          * <pre><code>
282 Roo.namespace('Company', 'Company.data');
283 Company.Widget = function() { ... }
284 Company.data.CustomStore = function(config) { ... }
285 </code></pre>
286          * @param {String} namespace1
287          * @param {String} namespace2
288          * @param {String} etc
289          * @method namespace
290          */
291         namespace : function(){
292             var a=arguments, o=null, i, j, d, rt;
293             for (i=0; i<a.length; ++i) {
294                 d=a[i].split(".");
295                 rt = d[0];
296                 /** eval:var:o */
297                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
298                 for (j=1; j<d.length; ++j) {
299                     o[d[j]]=o[d[j]] || {};
300                     o=o[d[j]];
301                 }
302             }
303         },
304         /**
305          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
306          * <pre><code>
307 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
308 Roo.factory(conf, Roo.data);
309 </code></pre>
310          * @param {String} classname
311          * @param {String} namespace (optional)
312          * @method factory
313          */
314          
315         factory : function(c, ns)
316         {
317             // no xtype, no ns or c.xns - or forced off by c.xns
318             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
319                 return c;
320             }
321             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
322             if (c.constructor == ns[c.xtype]) {// already created...
323                 return c;
324             }
325             if (ns[c.xtype]) {
326                 if (Roo.debug) { Roo.log("Roo.Factory(" + c.xtype + ")"); }
327                 var ret = new ns[c.xtype](c);
328                 ret.xns = false;
329                 return ret;
330             }
331             c.xns = false; // prevent recursion..
332             return c;
333         },
334          /**
335          * Logs to console if it can.
336          *
337          * @param {String|Object} string
338          * @method log
339          */
340         log : function(s)
341         {
342             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
343                 return; // alerT?
344             }
345             
346             console.log(s);
347         },
348         /**
349          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
350          * @param {Object} o
351          * @return {String}
352          */
353         urlEncode : function(o){
354             if(!o){
355                 return "";
356             }
357             var buf = [];
358             for(var key in o){
359                 var ov = o[key], k = Roo.encodeURIComponent(key);
360                 var type = typeof ov;
361                 if(type == 'undefined'){
362                     buf.push(k, "=&");
363                 }else if(type != "function" && type != "object"){
364                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
365                 }else if(ov instanceof Array){
366                     if (ov.length) {
367                             for(var i = 0, len = ov.length; i < len; i++) {
368                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
369                             }
370                         } else {
371                             buf.push(k, "=&");
372                         }
373                 }
374             }
375             buf.pop();
376             return buf.join("");
377         },
378          /**
379          * Safe version of encodeURIComponent
380          * @param {String} data 
381          * @return {String} 
382          */
383         
384         encodeURIComponent : function (data)
385         {
386             try {
387                 return encodeURIComponent(data);
388             } catch(e) {} // should be an uri encode error.
389             
390             if (data == '' || data == null){
391                return '';
392             }
393             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
394             function nibble_to_hex(nibble){
395                 var chars = '0123456789ABCDEF';
396                 return chars.charAt(nibble);
397             }
398             data = data.toString();
399             var buffer = '';
400             for(var i=0; i<data.length; i++){
401                 var c = data.charCodeAt(i);
402                 var bs = new Array();
403                 if (c > 0x10000){
404                         // 4 bytes
405                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
406                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
407                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
408                     bs[3] = 0x80 | (c & 0x3F);
409                 }else if (c > 0x800){
410                          // 3 bytes
411                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
412                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
413                     bs[2] = 0x80 | (c & 0x3F);
414                 }else if (c > 0x80){
415                        // 2 bytes
416                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
417                     bs[1] = 0x80 | (c & 0x3F);
418                 }else{
419                         // 1 byte
420                     bs[0] = c;
421                 }
422                 for(var j=0; j<bs.length; j++){
423                     var b = bs[j];
424                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
425                             + nibble_to_hex(b &0x0F);
426                     buffer += '%'+hex;
427                }
428             }
429             return buffer;    
430              
431         },
432
433         /**
434          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
435          * @param {String} string
436          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
437          * @return {Object} A literal with members
438          */
439         urlDecode : function(string, overwrite){
440             if(!string || !string.length){
441                 return {};
442             }
443             var obj = {};
444             var pairs = string.split('&');
445             var pair, name, value;
446             for(var i = 0, len = pairs.length; i < len; i++){
447                 pair = pairs[i].split('=');
448                 name = decodeURIComponent(pair[0]);
449                 value = decodeURIComponent(pair[1]);
450                 if(overwrite !== true){
451                     if(typeof obj[name] == "undefined"){
452                         obj[name] = value;
453                     }else if(typeof obj[name] == "string"){
454                         obj[name] = [obj[name]];
455                         obj[name].push(value);
456                     }else{
457                         obj[name].push(value);
458                     }
459                 }else{
460                     obj[name] = value;
461                 }
462             }
463             return obj;
464         },
465
466         /**
467          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
468          * passed array is not really an array, your function is called once with it.
469          * The supplied function is called with (Object item, Number index, Array allItems).
470          * @param {Array/NodeList/Mixed} array
471          * @param {Function} fn
472          * @param {Object} scope
473          */
474         each : function(array, fn, scope){
475             if(typeof array.length == "undefined" || typeof array == "string"){
476                 array = [array];
477             }
478             for(var i = 0, len = array.length; i < len; i++){
479                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
480             }
481         },
482
483         // deprecated
484         combine : function(){
485             var as = arguments, l = as.length, r = [];
486             for(var i = 0; i < l; i++){
487                 var a = as[i];
488                 if(a instanceof Array){
489                     r = r.concat(a);
490                 }else if(a.length !== undefined && !a.substr){
491                     r = r.concat(Array.prototype.slice.call(a, 0));
492                 }else{
493                     r.push(a);
494                 }
495             }
496             return r;
497         },
498
499         /**
500          * Escapes the passed string for use in a regular expression
501          * @param {String} str
502          * @return {String}
503          */
504         escapeRe : function(s) {
505             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
506         },
507
508         // internal (non-delayed, will get a return value..)
509         callback : function(cb, scope, args, delay)
510                 {
511             if(typeof cb != "function"){
512                                 return false;
513                         }
514                         if(delay){
515                                 cb.defer(delay, scope, args || []);
516                                 return false
517             }
518                         return cb.apply(scope, args || []);
519
520         },
521
522         /**
523          * Return the dom node for the passed string (id), dom node, or Roo.Element
524          * @param {String/HTMLElement/Roo.Element} el
525          * @return HTMLElement
526          */
527         getDom : function(el){
528             if(!el){
529                 return null;
530             }
531             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
532         },
533
534         /**
535         * Shorthand for {@link Roo.ComponentMgr#get}
536         * @param {String} id
537         * @return Roo.Component
538         */
539         getCmp : function(id){
540             return Roo.ComponentMgr.get(id);
541         },
542          
543         num : function(v, defaultValue){
544             if(typeof v != 'number'){
545                 return defaultValue;
546             }
547             return v;
548         },
549
550         destroy : function(){
551             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
552                 var as = a[i];
553                 if(as){
554                     if(as.dom){
555                         as.removeAllListeners();
556                         as.remove();
557                         continue;
558                     }
559                     if(typeof as.purgeListeners == 'function'){
560                         as.purgeListeners();
561                     }
562                     if(typeof as.destroy == 'function'){
563                         as.destroy();
564                     }
565                 }
566             }
567         },
568
569         // inpired by a similar function in mootools library
570         /**
571          * Returns the type of object that is passed in. If the object passed in is null or undefined it
572          * return false otherwise it returns one of the following values:<ul>
573          * <li><b>string</b>: If the object passed is a string</li>
574          * <li><b>number</b>: If the object passed is a number</li>
575          * <li><b>boolean</b>: If the object passed is a boolean value</li>
576          * <li><b>function</b>: If the object passed is a function reference</li>
577          * <li><b>object</b>: If the object passed is an object</li>
578          * <li><b>array</b>: If the object passed is an array</li>
579          * <li><b>regexp</b>: If the object passed is a regular expression</li>
580          * <li><b>element</b>: If the object passed is a DOM Element</li>
581          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
582          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
583          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
584          * @param {Mixed} object
585          * @return {String}
586          */
587         type : function(o){
588             if(o === undefined || o === null){
589                 return false;
590             }
591             if(o.htmlElement){
592                 return 'element';
593             }
594             var t = typeof o;
595             if(t == 'object' && o.nodeName) {
596                 switch(o.nodeType) {
597                     case 1: return 'element';
598                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
599                 }
600             }
601             if(t == 'object' || t == 'function') {
602                 switch(o.constructor) {
603                     case Array: return 'array';
604                     case RegExp: return 'regexp';
605                 }
606                 if(typeof o.length == 'number' && typeof o.item == 'function') {
607                     return 'nodelist';
608                 }
609             }
610             return t;
611         },
612
613         /**
614          * Returns true if the passed value is null, undefined or an empty string (optional).
615          * @param {Mixed} value The value to test
616          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
617          * @return {Boolean}
618          */
619         isEmpty : function(v, allowBlank){
620             return v === null || v === undefined || (!allowBlank ? v === '' : false);
621         },
622         
623         /** @type Boolean */
624         isOpera : isOpera,
625         /** @type Boolean */
626         isSafari : isSafari,
627         /** @type Boolean */
628         isFirefox : isFirefox,
629         /** @type Boolean */
630         isIE : isIE,
631         /** @type Boolean */
632         isIE7 : isIE7,
633         /** @type Boolean */
634         isIE11 : isIE11,
635         /** @type Boolean */
636         isEdge : isEdge,
637         /** @type Boolean */
638         isGecko : isGecko,
639         /** @type Boolean */
640         isBorderBox : isBorderBox,
641         /** @type Boolean */
642         isWindows : isWindows,
643         /** @type Boolean */
644         isLinux : isLinux,
645         /** @type Boolean */
646         isMac : isMac,
647         /** @type Boolean */
648         isIOS : isIOS,
649         /** @type Boolean */
650         isAndroid : isAndroid,
651         /** @type Boolean */
652         isTouch : isTouch,
653
654         /**
655          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
656          * you may want to set this to true.
657          * @type Boolean
658          */
659         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
660         
661         
662                 
663         /**
664          * Selects a single element as a Roo Element
665          * This is about as close as you can get to jQuery's $('do crazy stuff')
666          * @param {String} selector The selector/xpath query
667          * @param {Node} root (optional) The start of the query (defaults to document).
668          * @return {Roo.Element}
669          */
670         selectNode : function(selector, root) 
671         {
672             var node = Roo.DomQuery.selectNode(selector,root);
673             return node ? Roo.get(node) : new Roo.Element(false);
674         },
675                 /**
676                  * Find the current bootstrap width Grid size
677                  * Note xs is the default for smaller.. - this is currently used by grids to render correct columns
678                  * @returns {String} (xs|sm|md|lg|xl)
679                  */
680                 
681                 getGridSize : function()
682                 {
683                         var w = Roo.lib.Dom.getViewWidth();
684                         switch(true) {
685                                 case w > 1200:
686                                         return 'xl';
687                                 case w > 992:
688                                         return 'lg';
689                                 case w > 768:
690                                         return 'md';
691                                 case w > 576:
692                                         return 'sm';
693                                 default:
694                                         return 'xs'
695                         }
696                         
697                 } 
698         
699     });
700
701
702 })();
703
704 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
705                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
706                 "Roo.app", "Roo.ux" 
707                );
708 /*
709  * Based on:
710  * Ext JS Library 1.1.1
711  * Copyright(c) 2006-2007, Ext JS, LLC.
712  *
713  * Originally Released Under LGPL - original licence link has changed is not relivant.
714  *
715  * Fork - LGPL
716  * <script type="text/javascript">
717  */
718
719 (function() {    
720     // wrappedn so fnCleanup is not in global scope...
721     if(Roo.isIE) {
722         function fnCleanUp() {
723             var p = Function.prototype;
724             delete p.createSequence;
725             delete p.defer;
726             delete p.createDelegate;
727             delete p.createCallback;
728             delete p.createInterceptor;
729
730             window.detachEvent("onunload", fnCleanUp);
731         }
732         window.attachEvent("onunload", fnCleanUp);
733     }
734 })();
735
736
737 /**
738  * @class Function
739  * These functions are available on every Function object (any JavaScript function).
740  */
741 Roo.apply(Function.prototype, {
742      /**
743      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
744      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
745      * Will create a function that is bound to those 2 args.
746      * @return {Function} The new function
747     */
748     createCallback : function(/*args...*/){
749         // make args available, in function below
750         var args = arguments;
751         var method = this;
752         return function() {
753             return method.apply(window, args);
754         };
755     },
756
757     /**
758      * Creates a delegate (callback) that sets the scope to obj.
759      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
760      * Will create a function that is automatically scoped to this.
761      * @param {Object} obj (optional) The object for which the scope is set
762      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
763      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
764      *                                             if a number the args are inserted at the specified position
765      * @return {Function} The new function
766      */
767     createDelegate : function(obj, args, appendArgs){
768         var method = this;
769         return function() {
770             var callArgs = args || arguments;
771             if(appendArgs === true){
772                 callArgs = Array.prototype.slice.call(arguments, 0);
773                 callArgs = callArgs.concat(args);
774             }else if(typeof appendArgs == "number"){
775                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
776                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
777                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
778             }
779             return method.apply(obj || window, callArgs);
780         };
781     },
782
783     /**
784      * Calls this function after the number of millseconds specified.
785      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
786      * @param {Object} obj (optional) The object for which the scope is set
787      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
788      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
789      *                                             if a number the args are inserted at the specified position
790      * @return {Number} The timeout id that can be used with clearTimeout
791      */
792     defer : function(millis, obj, args, appendArgs){
793         var fn = this.createDelegate(obj, args, appendArgs);
794         if(millis){
795             return setTimeout(fn, millis);
796         }
797         fn();
798         return 0;
799     },
800     /**
801      * Create a combined function call sequence of the original function + the passed function.
802      * The resulting function returns the results of the original function.
803      * The passed fcn is called with the parameters of the original function
804      * @param {Function} fcn The function to sequence
805      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
806      * @return {Function} The new function
807      */
808     createSequence : function(fcn, scope){
809         if(typeof fcn != "function"){
810             return this;
811         }
812         var method = this;
813         return function() {
814             var retval = method.apply(this || window, arguments);
815             fcn.apply(scope || this || window, arguments);
816             return retval;
817         };
818     },
819
820     /**
821      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
822      * The resulting function returns the results of the original function.
823      * The passed fcn is called with the parameters of the original function.
824      * @addon
825      * @param {Function} fcn The function to call before the original
826      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
827      * @return {Function} The new function
828      */
829     createInterceptor : function(fcn, scope){
830         if(typeof fcn != "function"){
831             return this;
832         }
833         var method = this;
834         return function() {
835             fcn.target = this;
836             fcn.method = method;
837             if(fcn.apply(scope || this || window, arguments) === false){
838                 return;
839             }
840             return method.apply(this || window, arguments);
841         };
842     }
843 });
844 /*
845  * Based on:
846  * Ext JS Library 1.1.1
847  * Copyright(c) 2006-2007, Ext JS, LLC.
848  *
849  * Originally Released Under LGPL - original licence link has changed is not relivant.
850  *
851  * Fork - LGPL
852  * <script type="text/javascript">
853  */
854
855 Roo.applyIf(String, {
856     
857     /** @scope String */
858     
859     /**
860      * Escapes the passed string for ' and \
861      * @param {String} string The string to escape
862      * @return {String} The escaped string
863      * @static
864      */
865     escape : function(string) {
866         return string.replace(/('|\\)/g, "\\$1");
867     },
868
869     /**
870      * Pads the left side of a string with a specified character.  This is especially useful
871      * for normalizing number and date strings.  Example usage:
872      * <pre><code>
873 var s = String.leftPad('123', 5, '0');
874 // s now contains the string: '00123'
875 </code></pre>
876      * @param {String} string The original string
877      * @param {Number} size The total length of the output string
878      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
879      * @return {String} The padded string
880      * @static
881      */
882     leftPad : function (val, size, ch) {
883         var result = new String(val);
884         if(ch === null || ch === undefined || ch === '') {
885             ch = " ";
886         }
887         while (result.length < size) {
888             result = ch + result;
889         }
890         return result;
891     },
892
893     /**
894      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
895      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
896      * <pre><code>
897 var cls = 'my-class', text = 'Some text';
898 var s = String.format('<div class="{0}">{1}</div>', cls, text);
899 // s now contains the string: '<div class="my-class">Some text</div>'
900 </code></pre>
901      * @param {String} string The tokenized string to be formatted
902      * @param {String} value1 The value to replace token {0}
903      * @param {String} value2 Etc...
904      * @return {String} The formatted string
905      * @static
906      */
907     format : function(format){
908         var args = Array.prototype.slice.call(arguments, 1);
909         return format.replace(/\{(\d+)\}/g, function(m, i){
910             return Roo.util.Format.htmlEncode(args[i]);
911         });
912     }
913   
914     
915 });
916
917 /**
918  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
919  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
920  * they are already different, the first value passed in is returned.  Note that this method returns the new value
921  * but does not change the current string.
922  * <pre><code>
923 // alternate sort directions
924 sort = sort.toggle('ASC', 'DESC');
925
926 // instead of conditional logic:
927 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
928 </code></pre>
929  * @param {String} value The value to compare to the current string
930  * @param {String} other The new value to use if the string already equals the first value passed in
931  * @return {String} The new value
932  */
933  
934 String.prototype.toggle = function(value, other){
935     return this == value ? other : value;
936 };
937
938
939 /**
940   * Remove invalid unicode characters from a string 
941   *
942   * @return {String} The clean string
943   */
944 String.prototype.unicodeClean = function () {
945     return this.replace(/[\s\S]/g,
946         function(character) {
947             if (character.charCodeAt()< 256) {
948               return character;
949            }
950            try {
951                 encodeURIComponent(character);
952            } catch(e) { 
953               return '';
954            }
955            return character;
956         }
957     );
958 };
959   
960
961 /**
962   * Make the first letter of a string uppercase
963   *
964   * @return {String} The new string.
965   */
966 String.prototype.toUpperCaseFirst = function () {
967     return this.charAt(0).toUpperCase() + this.slice(1);
968 };  
969   
970 /*
971  * Based on:
972  * Ext JS Library 1.1.1
973  * Copyright(c) 2006-2007, Ext JS, LLC.
974  *
975  * Originally Released Under LGPL - original licence link has changed is not relivant.
976  *
977  * Fork - LGPL
978  * <script type="text/javascript">
979  */
980
981  /**
982  * @class Number
983  */
984 Roo.applyIf(Number.prototype, {
985     /**
986      * Checks whether or not the current number is within a desired range.  If the number is already within the
987      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
988      * exceeded.  Note that this method returns the constrained value but does not change the current number.
989      * @param {Number} min The minimum number in the range
990      * @param {Number} max The maximum number in the range
991      * @return {Number} The constrained value if outside the range, otherwise the current value
992      */
993     constrain : function(min, max){
994         return Math.min(Math.max(this, min), max);
995     }
996 });/*
997  * Based on:
998  * Ext JS Library 1.1.1
999  * Copyright(c) 2006-2007, Ext JS, LLC.
1000  *
1001  * Originally Released Under LGPL - original licence link has changed is not relivant.
1002  *
1003  * Fork - LGPL
1004  * <script type="text/javascript">
1005  */
1006  /**
1007  * @class Array
1008  */
1009 Roo.applyIf(Array.prototype, {
1010     /**
1011      * 
1012      * Checks whether or not the specified object exists in the array.
1013      * @param {Object} o The object to check for
1014      * @return {Number} The index of o in the array (or -1 if it is not found)
1015      */
1016     indexOf : function(o){
1017        for (var i = 0, len = this.length; i < len; i++){
1018               if(this[i] == o) { return i; }
1019        }
1020            return -1;
1021     },
1022
1023     /**
1024      * Removes the specified object from the array.  If the object is not found nothing happens.
1025      * @param {Object} o The object to remove
1026      */
1027     remove : function(o){
1028        var index = this.indexOf(o);
1029        if(index != -1){
1030            this.splice(index, 1);
1031        }
1032     },
1033     /**
1034      * Map (JS 1.6 compatibility)
1035      * @param {Function} function  to call
1036      */
1037     map : function(fun )
1038     {
1039         var len = this.length >>> 0;
1040         if (typeof fun != "function") {
1041             throw new TypeError();
1042         }
1043         var res = new Array(len);
1044         var thisp = arguments[1];
1045         for (var i = 0; i < len; i++)
1046         {
1047             if (i in this) {
1048                 res[i] = fun.call(thisp, this[i], i, this);
1049             }
1050         }
1051
1052         return res;
1053     },
1054     /**
1055      * equals
1056      * @param {Array} o The array to compare to
1057      * @returns {Boolean} true if the same
1058      */
1059     equals : function(b)
1060     {
1061             // https://stackoverflow.com/questions/3115982/how-to-check-if-two-arrays-are-equal-with-javascript
1062         if (this === b) {
1063             return true;
1064         }
1065         if (b == null) {
1066             return false;
1067         }
1068         if (this.length !== b.length) {
1069             return false;
1070         }
1071           
1072         // sort?? a.sort().equals(b.sort());
1073           
1074         for (var i = 0; i < this.length; ++i) {
1075             if (this[i] !== b[i]) {
1076             return false;
1077             }
1078         }
1079         return true;
1080     } 
1081     
1082     
1083     
1084     
1085 });
1086
1087 Roo.applyIf(Array, {
1088  /**
1089      * from
1090      * @static
1091      * @param {Array} o Or Array like object (eg. nodelist)
1092      * @returns {Array} 
1093      */
1094     from : function(o)
1095     {
1096         var ret= [];
1097     
1098         for (var i =0; i < o.length; i++) { 
1099             ret[i] = o[i];
1100         }
1101         return ret;
1102       
1103     }
1104 });
1105 /*
1106  * Based on:
1107  * Ext JS Library 1.1.1
1108  * Copyright(c) 2006-2007, Ext JS, LLC.
1109  *
1110  * Originally Released Under LGPL - original licence link has changed is not relivant.
1111  *
1112  * Fork - LGPL
1113  * <script type="text/javascript">
1114  */
1115
1116 /**
1117  * @class Date
1118  *
1119  * The date parsing and format syntax is a subset of
1120  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1121  * supported will provide results equivalent to their PHP versions.
1122  *
1123  * Following is the list of all currently supported formats:
1124  *<pre>
1125 Sample date:
1126 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1127
1128 Format  Output      Description
1129 ------  ----------  --------------------------------------------------------------
1130   d      10         Day of the month, 2 digits with leading zeros
1131   D      Wed        A textual representation of a day, three letters
1132   j      10         Day of the month without leading zeros
1133   l      Wednesday  A full textual representation of the day of the week
1134   S      th         English ordinal day of month suffix, 2 chars (use with j)
1135   w      3          Numeric representation of the day of the week
1136   z      9          The julian date, or day of the year (0-365)
1137   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1138   F      January    A full textual representation of the month
1139   m      01         Numeric representation of a month, with leading zeros
1140   M      Jan        Month name abbreviation, three letters
1141   n      1          Numeric representation of a month, without leading zeros
1142   t      31         Number of days in the given month
1143   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1144   Y      2007       A full numeric representation of a year, 4 digits
1145   y      07         A two digit representation of a year
1146   a      pm         Lowercase Ante meridiem and Post meridiem
1147   A      PM         Uppercase Ante meridiem and Post meridiem
1148   g      3          12-hour format of an hour without leading zeros
1149   G      15         24-hour format of an hour without leading zeros
1150   h      03         12-hour format of an hour with leading zeros
1151   H      15         24-hour format of an hour with leading zeros
1152   i      05         Minutes with leading zeros
1153   s      01         Seconds, with leading zeros
1154   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1155   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1156   T      CST        Timezone setting of the machine running the code
1157   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1158 </pre>
1159  *
1160  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1161  * <pre><code>
1162 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1163 document.write(dt.format('Y-m-d'));                         //2007-01-10
1164 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1165 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1166  </code></pre>
1167  *
1168  * Here are some standard date/time patterns that you might find helpful.  They
1169  * are not part of the source of Date.js, but to use them you can simply copy this
1170  * block of code into any script that is included after Date.js and they will also become
1171  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1172  * <pre><code>
1173 Date.patterns = {
1174     ISO8601Long:"Y-m-d H:i:s",
1175     ISO8601Short:"Y-m-d",
1176     ShortDate: "n/j/Y",
1177     LongDate: "l, F d, Y",
1178     FullDateTime: "l, F d, Y g:i:s A",
1179     MonthDay: "F d",
1180     ShortTime: "g:i A",
1181     LongTime: "g:i:s A",
1182     SortableDateTime: "Y-m-d\\TH:i:s",
1183     UniversalSortableDateTime: "Y-m-d H:i:sO",
1184     YearMonth: "F, Y"
1185 };
1186 </code></pre>
1187  *
1188  * Example usage:
1189  * <pre><code>
1190 var dt = new Date();
1191 document.write(dt.format(Date.patterns.ShortDate));
1192  </code></pre>
1193  */
1194
1195 /*
1196  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1197  * They generate precompiled functions from date formats instead of parsing and
1198  * processing the pattern every time you format a date.  These functions are available
1199  * on every Date object (any javascript function).
1200  *
1201  * The original article and download are here:
1202  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1203  *
1204  */
1205  
1206  
1207  // was in core
1208 /**
1209  Returns the number of milliseconds between this date and date
1210  @param {Date} date (optional) Defaults to now
1211  @param {String} interval (optional) Default Date.MILLI, A valid date interval enum value (eg. Date.DAY) 
1212  @return {Number} The diff in milliseconds or units of interval
1213  @member Date getElapsed
1214  */
1215 Date.prototype.getElapsed = function(date, interval)
1216 {
1217     date = date ||  new Date();
1218     var ret = Math.abs(date.getTime()-this.getTime());
1219     switch (interval) {
1220        
1221         case  Date.SECOND:
1222             return Math.floor(ret / (1000));
1223         case  Date.MINUTE:
1224             return Math.floor(ret / (1000*60));
1225         case  Date.HOUR:
1226             return Math.floor(ret / (1000*60*60));
1227         case  Date.DAY:
1228             return Math.floor(ret / (1000*60*60*24));
1229         case  Date.MONTH: // this does not give exact number...??
1230             return ((date.format("Y") - this.format("Y")) * 12) + (date.format("m") - this.format("m"));
1231         case  Date.YEAR: // this does not give exact number...??
1232             return (date.format("Y") - this.format("Y"));
1233        
1234         case  Date.MILLI:
1235         default:
1236             return ret;
1237     }
1238 };
1239  
1240 // was in date file..
1241
1242
1243 // private
1244 Date.parseFunctions = {count:0};
1245 // private
1246 Date.parseRegexes = [];
1247 // private
1248 Date.formatFunctions = {count:0};
1249
1250 // private
1251 Date.prototype.dateFormat = function(format) {
1252     if (Date.formatFunctions[format] == null) {
1253         Date.createNewFormat(format);
1254     }
1255     var func = Date.formatFunctions[format];
1256     return this[func]();
1257 };
1258
1259
1260 /**
1261  * Formats a date given the supplied format string
1262  * @param {String} format The format string
1263  * @return {String} The formatted date
1264  * @method
1265  */
1266 Date.prototype.format = Date.prototype.dateFormat;
1267
1268 // private
1269 Date.createNewFormat = function(format) {
1270     var funcName = "format" + Date.formatFunctions.count++;
1271     Date.formatFunctions[format] = funcName;
1272     var code = "Date.prototype." + funcName + " = function(){return ";
1273     var special = false;
1274     var ch = '';
1275     for (var i = 0; i < format.length; ++i) {
1276         ch = format.charAt(i);
1277         if (!special && ch == "\\") {
1278             special = true;
1279         }
1280         else if (special) {
1281             special = false;
1282             code += "'" + String.escape(ch) + "' + ";
1283         }
1284         else {
1285             code += Date.getFormatCode(ch);
1286         }
1287     }
1288     /** eval:var:zzzzzzzzzzzzz */
1289     eval(code.substring(0, code.length - 3) + ";}");
1290 };
1291
1292 // private
1293 Date.getFormatCode = function(character) {
1294     switch (character) {
1295     case "d":
1296         return "String.leftPad(this.getDate(), 2, '0') + ";
1297     case "D":
1298         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1299     case "j":
1300         return "this.getDate() + ";
1301     case "l":
1302         return "Date.dayNames[this.getDay()] + ";
1303     case "S":
1304         return "this.getSuffix() + ";
1305     case "w":
1306         return "this.getDay() + ";
1307     case "z":
1308         return "this.getDayOfYear() + ";
1309     case "W":
1310         return "this.getWeekOfYear() + ";
1311     case "F":
1312         return "Date.monthNames[this.getMonth()] + ";
1313     case "m":
1314         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1315     case "M":
1316         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1317     case "n":
1318         return "(this.getMonth() + 1) + ";
1319     case "t":
1320         return "this.getDaysInMonth() + ";
1321     case "L":
1322         return "(this.isLeapYear() ? 1 : 0) + ";
1323     case "Y":
1324         return "this.getFullYear() + ";
1325     case "y":
1326         return "('' + this.getFullYear()).substring(2, 4) + ";
1327     case "a":
1328         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1329     case "A":
1330         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1331     case "g":
1332         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1333     case "G":
1334         return "this.getHours() + ";
1335     case "h":
1336         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1337     case "H":
1338         return "String.leftPad(this.getHours(), 2, '0') + ";
1339     case "i":
1340         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1341     case "s":
1342         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1343     case "O":
1344         return "this.getGMTOffset() + ";
1345     case "P":
1346         return "this.getGMTColonOffset() + ";
1347     case "T":
1348         return "this.getTimezone() + ";
1349     case "Z":
1350         return "(this.getTimezoneOffset() * -60) + ";
1351     default:
1352         return "'" + String.escape(character) + "' + ";
1353     }
1354 };
1355
1356 /**
1357  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1358  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1359  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1360  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1361  * string or the parse operation will fail.
1362  * Example Usage:
1363 <pre><code>
1364 //dt = Fri May 25 2007 (current date)
1365 var dt = new Date();
1366
1367 //dt = Thu May 25 2006 (today's month/day in 2006)
1368 dt = Date.parseDate("2006", "Y");
1369
1370 //dt = Sun Jan 15 2006 (all date parts specified)
1371 dt = Date.parseDate("2006-1-15", "Y-m-d");
1372
1373 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1374 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1375 </code></pre>
1376  * @param {String} input The unparsed date as a string
1377  * @param {String} format The format the date is in
1378  * @return {Date} The parsed date
1379  * @static
1380  */
1381 Date.parseDate = function(input, format) {
1382     if (Date.parseFunctions[format] == null) {
1383         Date.createParser(format);
1384     }
1385     var func = Date.parseFunctions[format];
1386     return Date[func](input);
1387 };
1388 /**
1389  * @private
1390  */
1391
1392 Date.createParser = function(format) {
1393     var funcName = "parse" + Date.parseFunctions.count++;
1394     var regexNum = Date.parseRegexes.length;
1395     var currentGroup = 1;
1396     Date.parseFunctions[format] = funcName;
1397
1398     var code = "Date." + funcName + " = function(input){\n"
1399         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1400         + "var d = new Date();\n"
1401         + "y = d.getFullYear();\n"
1402         + "m = d.getMonth();\n"
1403         + "d = d.getDate();\n"
1404         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1405         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1406         + "if (results && results.length > 0) {";
1407     var regex = "";
1408
1409     var special = false;
1410     var ch = '';
1411     for (var i = 0; i < format.length; ++i) {
1412         ch = format.charAt(i);
1413         if (!special && ch == "\\") {
1414             special = true;
1415         }
1416         else if (special) {
1417             special = false;
1418             regex += String.escape(ch);
1419         }
1420         else {
1421             var obj = Date.formatCodeToRegex(ch, currentGroup);
1422             currentGroup += obj.g;
1423             regex += obj.s;
1424             if (obj.g && obj.c) {
1425                 code += obj.c;
1426             }
1427         }
1428     }
1429
1430     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1431         + "{v = new Date(y, m, d, h, i, s); v.setFullYear(y);}\n"
1432         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1433         + "{v = new Date(y, m, d, h, i); v.setFullYear(y);}\n"
1434         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1435         + "{v = new Date(y, m, d, h); v.setFullYear(y);}\n"
1436         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1437         + "{v = new Date(y, m, d); v.setFullYear(y);}\n"
1438         + "else if (y >= 0 && m >= 0)\n"
1439         + "{v = new Date(y, m); v.setFullYear(y);}\n"
1440         + "else if (y >= 0)\n"
1441         + "{v = new Date(y); v.setFullYear(y);}\n"
1442         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1443         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1444         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1445         + ";}";
1446
1447     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1448     /** eval:var:zzzzzzzzzzzzz */
1449     eval(code);
1450 };
1451
1452 // private
1453 Date.formatCodeToRegex = function(character, currentGroup) {
1454     switch (character) {
1455     case "D":
1456         return {g:0,
1457         c:null,
1458         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1459     case "j":
1460         return {g:1,
1461             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1462             s:"(\\d{1,2})"}; // day of month without leading zeroes
1463     case "d":
1464         return {g:1,
1465             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1466             s:"(\\d{2})"}; // day of month with leading zeroes
1467     case "l":
1468         return {g:0,
1469             c:null,
1470             s:"(?:" + Date.dayNames.join("|") + ")"};
1471     case "S":
1472         return {g:0,
1473             c:null,
1474             s:"(?:st|nd|rd|th)"};
1475     case "w":
1476         return {g:0,
1477             c:null,
1478             s:"\\d"};
1479     case "z":
1480         return {g:0,
1481             c:null,
1482             s:"(?:\\d{1,3})"};
1483     case "W":
1484         return {g:0,
1485             c:null,
1486             s:"(?:\\d{2})"};
1487     case "F":
1488         return {g:1,
1489             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1490             s:"(" + Date.monthNames.join("|") + ")"};
1491     case "M":
1492         return {g:1,
1493             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1494             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1495     case "n":
1496         return {g:1,
1497             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1498             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1499     case "m":
1500         return {g:1,
1501             c:"m = Math.max(0,parseInt(results[" + currentGroup + "], 10) - 1);\n",
1502             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1503     case "t":
1504         return {g:0,
1505             c:null,
1506             s:"\\d{1,2}"};
1507     case "L":
1508         return {g:0,
1509             c:null,
1510             s:"(?:1|0)"};
1511     case "Y":
1512         return {g:1,
1513             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1514             s:"(\\d{4})"};
1515     case "y":
1516         return {g:1,
1517             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1518                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1519             s:"(\\d{1,2})"};
1520     case "a":
1521         return {g:1,
1522             c:"if (results[" + currentGroup + "] == 'am') {\n"
1523                 + "if (h == 12) { h = 0; }\n"
1524                 + "} else { if (h < 12) { h += 12; }}",
1525             s:"(am|pm)"};
1526     case "A":
1527         return {g:1,
1528             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1529                 + "if (h == 12) { h = 0; }\n"
1530                 + "} else { if (h < 12) { h += 12; }}",
1531             s:"(AM|PM)"};
1532     case "g":
1533     case "G":
1534         return {g:1,
1535             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1536             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1537     case "h":
1538     case "H":
1539         return {g:1,
1540             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1541             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1542     case "i":
1543         return {g:1,
1544             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1545             s:"(\\d{2})"};
1546     case "s":
1547         return {g:1,
1548             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1549             s:"(\\d{2})"};
1550     case "O":
1551         return {g:1,
1552             c:[
1553                 "o = results[", currentGroup, "];\n",
1554                 "var sn = o.substring(0,1);\n", // get + / - sign
1555                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1556                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1557                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1558                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1559             ].join(""),
1560             s:"([+\-]\\d{2,4})"};
1561     
1562     
1563     case "P":
1564         return {g:1,
1565                 c:[
1566                    "o = results[", currentGroup, "];\n",
1567                    "var sn = o.substring(0,1);\n",
1568                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1569                    "var mn = o.substring(4,6) % 60;\n",
1570                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1571                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1572             ].join(""),
1573             s:"([+\-]\\d{4})"};
1574     case "T":
1575         return {g:0,
1576             c:null,
1577             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1578     case "Z":
1579         return {g:1,
1580             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1581                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1582             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1583     default:
1584         return {g:0,
1585             c:null,
1586             s:String.escape(character)};
1587     }
1588 };
1589
1590 /**
1591  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1592  * @return {String} The abbreviated timezone name (e.g. 'CST')
1593  */
1594 Date.prototype.getTimezone = function() {
1595     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1596 };
1597
1598 /**
1599  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1600  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1601  */
1602 Date.prototype.getGMTOffset = function() {
1603     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1604         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1605         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1606 };
1607
1608 /**
1609  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1610  * @return {String} 2-characters representing hours and 2-characters representing minutes
1611  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1612  */
1613 Date.prototype.getGMTColonOffset = function() {
1614         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1615                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1616                 + ":"
1617                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1618 }
1619
1620 /**
1621  * Get the numeric day number of the year, adjusted for leap year.
1622  * @return {Number} 0 through 364 (365 in leap years)
1623  */
1624 Date.prototype.getDayOfYear = function() {
1625     var num = 0;
1626     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1627     for (var i = 0; i < this.getMonth(); ++i) {
1628         num += Date.daysInMonth[i];
1629     }
1630     return num + this.getDate() - 1;
1631 };
1632
1633 /**
1634  * Get the string representation of the numeric week number of the year
1635  * (equivalent to the format specifier 'W').
1636  * @return {String} '00' through '52'
1637  */
1638 Date.prototype.getWeekOfYear = function() {
1639     // Skip to Thursday of this week
1640     var now = this.getDayOfYear() + (4 - this.getDay());
1641     // Find the first Thursday of the year
1642     var jan1 = new Date(this.getFullYear(), 0, 1);
1643     var then = (7 - jan1.getDay() + 4);
1644     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1645 };
1646
1647 /**
1648  * Whether or not the current date is in a leap year.
1649  * @return {Boolean} True if the current date is in a leap year, else false
1650  */
1651 Date.prototype.isLeapYear = function() {
1652     var year = this.getFullYear();
1653     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1654 };
1655
1656 /**
1657  * Get the first day of the current month, adjusted for leap year.  The returned value
1658  * is the numeric day index within the week (0-6) which can be used in conjunction with
1659  * the {@link #monthNames} array to retrieve the textual day name.
1660  * Example:
1661  *<pre><code>
1662 var dt = new Date('1/10/2007');
1663 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1664 </code></pre>
1665  * @return {Number} The day number (0-6)
1666  */
1667 Date.prototype.getFirstDayOfMonth = function() {
1668     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1669     return (day < 0) ? (day + 7) : day;
1670 };
1671
1672 /**
1673  * Get the last day of the current month, adjusted for leap year.  The returned value
1674  * is the numeric day index within the week (0-6) which can be used in conjunction with
1675  * the {@link #monthNames} array to retrieve the textual day name.
1676  * Example:
1677  *<pre><code>
1678 var dt = new Date('1/10/2007');
1679 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1680 </code></pre>
1681  * @return {Number} The day number (0-6)
1682  */
1683 Date.prototype.getLastDayOfMonth = function() {
1684     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1685     return (day < 0) ? (day + 7) : day;
1686 };
1687
1688
1689 /**
1690  * Get the first date of this date's month
1691  * @return {Date}
1692  */
1693 Date.prototype.getFirstDateOfMonth = function() {
1694     return new Date(this.getFullYear(), this.getMonth(), 1);
1695 };
1696
1697 /**
1698  * Get the last date of this date's month
1699  * @return {Date}
1700  */
1701 Date.prototype.getLastDateOfMonth = function() {
1702     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1703 };
1704 /**
1705  * Get the number of days in the current month, adjusted for leap year.
1706  * @return {Number} The number of days in the month
1707  */
1708 Date.prototype.getDaysInMonth = function() {
1709     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1710     return Date.daysInMonth[this.getMonth()];
1711 };
1712
1713 /**
1714  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1715  * @return {String} 'st, 'nd', 'rd' or 'th'
1716  */
1717 Date.prototype.getSuffix = function() {
1718     switch (this.getDate()) {
1719         case 1:
1720         case 21:
1721         case 31:
1722             return "st";
1723         case 2:
1724         case 22:
1725             return "nd";
1726         case 3:
1727         case 23:
1728             return "rd";
1729         default:
1730             return "th";
1731     }
1732 };
1733
1734 // private
1735 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1736
1737 /**
1738  * An array of textual month names.
1739  * Override these values for international dates, for example...
1740  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1741  * @type Array
1742  * @static
1743  */
1744 Date.monthNames =
1745    ["January",
1746     "February",
1747     "March",
1748     "April",
1749     "May",
1750     "June",
1751     "July",
1752     "August",
1753     "September",
1754     "October",
1755     "November",
1756     "December"];
1757
1758 /**
1759  * An array of textual day names.
1760  * Override these values for international dates, for example...
1761  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1762  * @type Array
1763  * @static
1764  */
1765 Date.dayNames =
1766    ["Sunday",
1767     "Monday",
1768     "Tuesday",
1769     "Wednesday",
1770     "Thursday",
1771     "Friday",
1772     "Saturday"];
1773
1774 // private
1775 Date.y2kYear = 50;
1776 // private
1777 Date.monthNumbers = {
1778     Jan:0,
1779     Feb:1,
1780     Mar:2,
1781     Apr:3,
1782     May:4,
1783     Jun:5,
1784     Jul:6,
1785     Aug:7,
1786     Sep:8,
1787     Oct:9,
1788     Nov:10,
1789     Dec:11};
1790
1791 /**
1792  * Creates and returns a new Date instance with the exact same date value as the called instance.
1793  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1794  * variable will also be changed.  When the intention is to create a new variable that will not
1795  * modify the original instance, you should create a clone.
1796  *
1797  * Example of correctly cloning a date:
1798  * <pre><code>
1799 //wrong way:
1800 var orig = new Date('10/1/2006');
1801 var copy = orig;
1802 copy.setDate(5);
1803 document.write(orig);  //returns 'Thu Oct 05 2006'!
1804
1805 //correct way:
1806 var orig = new Date('10/1/2006');
1807 var copy = orig.clone();
1808 copy.setDate(5);
1809 document.write(orig);  //returns 'Thu Oct 01 2006'
1810 </code></pre>
1811  * @return {Date} The new Date instance
1812  */
1813 Date.prototype.clone = function() {
1814         return new Date(this.getTime());
1815 };
1816
1817 /**
1818  * Clears any time information from this date
1819  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1820  @return {Date} this or the clone
1821  */
1822 Date.prototype.clearTime = function(clone){
1823     if(clone){
1824         return this.clone().clearTime();
1825     }
1826     this.setHours(0);
1827     this.setMinutes(0);
1828     this.setSeconds(0);
1829     this.setMilliseconds(0);
1830     return this;
1831 };
1832
1833 // private
1834 // safari setMonth is broken -- check that this is only donw once...
1835 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1836     Date.brokenSetMonth = Date.prototype.setMonth;
1837         Date.prototype.setMonth = function(num){
1838                 if(num <= -1){
1839                         var n = Math.ceil(-num);
1840                         var back_year = Math.ceil(n/12);
1841                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1842                         this.setFullYear(this.getFullYear() - back_year);
1843                         return Date.brokenSetMonth.call(this, month);
1844                 } else {
1845                         return Date.brokenSetMonth.apply(this, arguments);
1846                 }
1847         };
1848 }
1849
1850 /** Date interval constant 
1851 * @static 
1852 * @type String */
1853 Date.MILLI = "ms";
1854 /** Date interval constant 
1855 * @static 
1856 * @type String */
1857 Date.SECOND = "s";
1858 /** Date interval constant 
1859 * @static 
1860 * @type String */
1861 Date.MINUTE = "mi";
1862 /** Date interval constant 
1863 * @static 
1864 * @type String */
1865 Date.HOUR = "h";
1866 /** Date interval constant 
1867 * @static 
1868 * @type String */
1869 Date.DAY = "d";
1870 /** Date interval constant 
1871 * @static 
1872 * @type String */
1873 Date.MONTH = "mo";
1874 /** Date interval constant 
1875 * @static 
1876 * @type String */
1877 Date.YEAR = "y";
1878
1879 /**
1880  * Provides a convenient method of performing basic date arithmetic.  This method
1881  * does not modify the Date instance being called - it creates and returns
1882  * a new Date instance containing the resulting date value.
1883  *
1884  * Examples:
1885  * <pre><code>
1886 //Basic usage:
1887 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1888 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1889
1890 //Negative values will subtract correctly:
1891 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1892 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1893
1894 //You can even chain several calls together in one line!
1895 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1896 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1897  </code></pre>
1898  *
1899  * @param {String} interval   A valid date interval enum value
1900  * @param {Number} value      The amount to add to the current date
1901  * @return {Date} The new Date instance
1902  */
1903 Date.prototype.add = function(interval, value){
1904   var d = this.clone();
1905   if (!interval || value === 0) { return d; }
1906   switch(interval.toLowerCase()){
1907     case Date.MILLI:
1908       d.setMilliseconds(this.getMilliseconds() + value);
1909       break;
1910     case Date.SECOND:
1911       d.setSeconds(this.getSeconds() + value);
1912       break;
1913     case Date.MINUTE:
1914       d.setMinutes(this.getMinutes() + value);
1915       break;
1916     case Date.HOUR:
1917       d.setHours(this.getHours() + value);
1918       break;
1919     case Date.DAY:
1920       d.setDate(this.getDate() + value);
1921       break;
1922     case Date.MONTH:
1923       var day = this.getDate();
1924       if(day > 28){
1925           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1926       }
1927       d.setDate(day);
1928       d.setMonth(this.getMonth() + value);
1929       break;
1930     case Date.YEAR:
1931       d.setFullYear(this.getFullYear() + value);
1932       break;
1933   }
1934   return d;
1935 };
1936 /**
1937  * @class Roo.lib.Dom
1938  * @licence LGPL
1939  * @static
1940  * 
1941  * Dom utils (from YIU afaik)
1942  *
1943  * 
1944  **/
1945 Roo.lib.Dom = {
1946     /**
1947      * Get the view width
1948      * @param {Boolean} full True will get the full document, otherwise it's the view width
1949      * @return {Number} The width
1950      */
1951      
1952     getViewWidth : function(full) {
1953         return full ? this.getDocumentWidth() : this.getViewportWidth();
1954     },
1955     /**
1956      * Get the view height
1957      * @param {Boolean} full True will get the full document, otherwise it's the view height
1958      * @return {Number} The height
1959      */
1960     getViewHeight : function(full) {
1961         return full ? this.getDocumentHeight() : this.getViewportHeight();
1962     },
1963     /**
1964      * Get the Full Document height 
1965      * @return {Number} The height
1966      */
1967     getDocumentHeight: function() {
1968         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1969         return Math.max(scrollHeight, this.getViewportHeight());
1970     },
1971     /**
1972      * Get the Full Document width
1973      * @return {Number} The width
1974      */
1975     getDocumentWidth: function() {
1976         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1977         return Math.max(scrollWidth, this.getViewportWidth());
1978     },
1979     /**
1980      * Get the Window Viewport height
1981      * @return {Number} The height
1982      */
1983     getViewportHeight: function() {
1984         var height = self.innerHeight;
1985         var mode = document.compatMode;
1986
1987         if ((mode || Roo.isIE) && !Roo.isOpera) {
1988             height = (mode == "CSS1Compat") ?
1989                      document.documentElement.clientHeight :
1990                      document.body.clientHeight;
1991         }
1992
1993         return height;
1994     },
1995     /**
1996      * Get the Window Viewport width
1997      * @return {Number} The width
1998      */
1999     getViewportWidth: function() {
2000         var width = self.innerWidth;
2001         var mode = document.compatMode;
2002
2003         if (mode || Roo.isIE) {
2004             width = (mode == "CSS1Compat") ?
2005                     document.documentElement.clientWidth :
2006                     document.body.clientWidth;
2007         }
2008         return width;
2009     },
2010
2011     isAncestor : function(p, c) {
2012         p = Roo.getDom(p);
2013         c = Roo.getDom(c);
2014         if (!p || !c) {
2015             return false;
2016         }
2017
2018         if (p.contains && !Roo.isSafari) {
2019             return p.contains(c);
2020         } else if (p.compareDocumentPosition) {
2021             return !!(p.compareDocumentPosition(c) & 16);
2022         } else {
2023             var parent = c.parentNode;
2024             while (parent) {
2025                 if (parent == p) {
2026                     return true;
2027                 }
2028                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
2029                     return false;
2030                 }
2031                 parent = parent.parentNode;
2032             }
2033             return false;
2034         }
2035     },
2036
2037     getRegion : function(el) {
2038         return Roo.lib.Region.getRegion(el);
2039     },
2040
2041     getY : function(el) {
2042         return this.getXY(el)[1];
2043     },
2044
2045     getX : function(el) {
2046         return this.getXY(el)[0];
2047     },
2048
2049     getXY : function(el) {
2050         var p, pe, b, scroll, bd = document.body;
2051         el = Roo.getDom(el);
2052         var fly = Roo.lib.AnimBase.fly;
2053         if (el.getBoundingClientRect) {
2054             b = el.getBoundingClientRect();
2055             scroll = fly(document).getScroll();
2056             return [b.left + scroll.left, b.top + scroll.top];
2057         }
2058         var x = 0, y = 0;
2059
2060         p = el;
2061
2062         var hasAbsolute = fly(el).getStyle("position") == "absolute";
2063
2064         while (p) {
2065
2066             x += p.offsetLeft;
2067             y += p.offsetTop;
2068
2069             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
2070                 hasAbsolute = true;
2071             }
2072
2073             if (Roo.isGecko) {
2074                 pe = fly(p);
2075
2076                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
2077                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
2078
2079
2080                 x += bl;
2081                 y += bt;
2082
2083
2084                 if (p != el && pe.getStyle('overflow') != 'visible') {
2085                     x += bl;
2086                     y += bt;
2087                 }
2088             }
2089             p = p.offsetParent;
2090         }
2091
2092         if (Roo.isSafari && hasAbsolute) {
2093             x -= bd.offsetLeft;
2094             y -= bd.offsetTop;
2095         }
2096
2097         if (Roo.isGecko && !hasAbsolute) {
2098             var dbd = fly(bd);
2099             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
2100             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
2101         }
2102
2103         p = el.parentNode;
2104         while (p && p != bd) {
2105             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
2106                 x -= p.scrollLeft;
2107                 y -= p.scrollTop;
2108             }
2109             p = p.parentNode;
2110         }
2111         return [x, y];
2112     },
2113  
2114   
2115
2116
2117     setXY : function(el, xy) {
2118         el = Roo.fly(el, '_setXY');
2119         el.position();
2120         var pts = el.translatePoints(xy);
2121         if (xy[0] !== false) {
2122             el.dom.style.left = pts.left + "px";
2123         }
2124         if (xy[1] !== false) {
2125             el.dom.style.top = pts.top + "px";
2126         }
2127     },
2128
2129     setX : function(el, x) {
2130         this.setXY(el, [x, false]);
2131     },
2132
2133     setY : function(el, y) {
2134         this.setXY(el, [false, y]);
2135     }
2136 };
2137 /*
2138  * Portions of this file are based on pieces of Yahoo User Interface Library
2139  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2140  * YUI licensed under the BSD License:
2141  * http://developer.yahoo.net/yui/license.txt
2142  * <script type="text/javascript">
2143  *
2144  */
2145
2146 Roo.lib.Event = function() {
2147     var loadComplete = false;
2148     var listeners = [];
2149     var unloadListeners = [];
2150     var retryCount = 0;
2151     var onAvailStack = [];
2152     var counter = 0;
2153     var lastError = null;
2154
2155     return {
2156         POLL_RETRYS: 200,
2157         POLL_INTERVAL: 20,
2158         EL: 0,
2159         TYPE: 1,
2160         FN: 2,
2161         WFN: 3,
2162         OBJ: 3,
2163         ADJ_SCOPE: 4,
2164         _interval: null,
2165
2166         startInterval: function() {
2167             if (!this._interval) {
2168                 var self = this;
2169                 var callback = function() {
2170                     self._tryPreloadAttach();
2171                 };
2172                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2173
2174             }
2175         },
2176
2177         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2178             onAvailStack.push({ id:         p_id,
2179                 fn:         p_fn,
2180                 obj:        p_obj,
2181                 override:   p_override,
2182                 checkReady: false    });
2183
2184             retryCount = this.POLL_RETRYS;
2185             this.startInterval();
2186         },
2187
2188
2189         addListener: function(el, eventName, fn) {
2190             el = Roo.getDom(el);
2191             if (!el || !fn) {
2192                 return false;
2193             }
2194
2195             if ("unload" == eventName) {
2196                 unloadListeners[unloadListeners.length] =
2197                 [el, eventName, fn];
2198                 return true;
2199             }
2200
2201             var wrappedFn = function(e) {
2202                 return fn(Roo.lib.Event.getEvent(e));
2203             };
2204
2205             var li = [el, eventName, fn, wrappedFn];
2206
2207             var index = listeners.length;
2208             listeners[index] = li;
2209
2210             this.doAdd(el, eventName, wrappedFn, false);
2211             return true;
2212
2213         },
2214
2215
2216         removeListener: function(el, eventName, fn) {
2217             var i, len;
2218
2219             el = Roo.getDom(el);
2220
2221             if(!fn) {
2222                 return this.purgeElement(el, false, eventName);
2223             }
2224
2225
2226             if ("unload" == eventName) {
2227
2228                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2229                     var li = unloadListeners[i];
2230                     if (li &&
2231                         li[0] == el &&
2232                         li[1] == eventName &&
2233                         li[2] == fn) {
2234                         unloadListeners.splice(i, 1);
2235                         return true;
2236                     }
2237                 }
2238
2239                 return false;
2240             }
2241
2242             var cacheItem = null;
2243
2244
2245             var index = arguments[3];
2246
2247             if ("undefined" == typeof index) {
2248                 index = this._getCacheIndex(el, eventName, fn);
2249             }
2250
2251             if (index >= 0) {
2252                 cacheItem = listeners[index];
2253             }
2254
2255             if (!el || !cacheItem) {
2256                 return false;
2257             }
2258
2259             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2260
2261             delete listeners[index][this.WFN];
2262             delete listeners[index][this.FN];
2263             listeners.splice(index, 1);
2264
2265             return true;
2266
2267         },
2268
2269
2270         getTarget: function(ev, resolveTextNode) {
2271             ev = ev.browserEvent || ev;
2272             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2273             var t = ev.target || ev.srcElement;
2274             return this.resolveTextNode(t);
2275         },
2276
2277
2278         resolveTextNode: function(node) {
2279             if (Roo.isSafari && node && 3 == node.nodeType) {
2280                 return node.parentNode;
2281             } else {
2282                 return node;
2283             }
2284         },
2285
2286
2287         getPageX: function(ev) {
2288             ev = ev.browserEvent || ev;
2289             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2290             var x = ev.pageX;
2291             if (!x && 0 !== x) {
2292                 x = ev.clientX || 0;
2293
2294                 if (Roo.isIE) {
2295                     x += this.getScroll()[1];
2296                 }
2297             }
2298
2299             return x;
2300         },
2301
2302
2303         getPageY: function(ev) {
2304             ev = ev.browserEvent || ev;
2305             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2306             var y = ev.pageY;
2307             if (!y && 0 !== y) {
2308                 y = ev.clientY || 0;
2309
2310                 if (Roo.isIE) {
2311                     y += this.getScroll()[0];
2312                 }
2313             }
2314
2315
2316             return y;
2317         },
2318
2319
2320         getXY: function(ev) {
2321             ev = ev.browserEvent || ev;
2322             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2323             return [this.getPageX(ev), this.getPageY(ev)];
2324         },
2325
2326
2327         getRelatedTarget: function(ev) {
2328             ev = ev.browserEvent || ev;
2329             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2330             var t = ev.relatedTarget;
2331             if (!t) {
2332                 if (ev.type == "mouseout") {
2333                     t = ev.toElement;
2334                 } else if (ev.type == "mouseover") {
2335                     t = ev.fromElement;
2336                 }
2337             }
2338
2339             return this.resolveTextNode(t);
2340         },
2341
2342
2343         getTime: function(ev) {
2344             ev = ev.browserEvent || ev;
2345             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2346             if (!ev.time) {
2347                 var t = new Date().getTime();
2348                 try {
2349                     ev.time = t;
2350                 } catch(ex) {
2351                     this.lastError = ex;
2352                     return t;
2353                 }
2354             }
2355
2356             return ev.time;
2357         },
2358
2359
2360         stopEvent: function(ev) {
2361             this.stopPropagation(ev);
2362             this.preventDefault(ev);
2363         },
2364
2365
2366         stopPropagation: function(ev) {
2367             ev = ev.browserEvent || ev;
2368             if (ev.stopPropagation) {
2369                 ev.stopPropagation();
2370             } else {
2371                 ev.cancelBubble = true;
2372             }
2373         },
2374
2375
2376         preventDefault: function(ev) {
2377             ev = ev.browserEvent || ev;
2378             if(ev.preventDefault) {
2379                 ev.preventDefault();
2380             } else {
2381                 ev.returnValue = false;
2382             }
2383         },
2384
2385
2386         getEvent: function(e) {
2387             var ev = e || window.event;
2388             if (!ev) {
2389                 var c = this.getEvent.caller;
2390                 while (c) {
2391                     ev = c.arguments[0];
2392                     if (ev && Event == ev.constructor) {
2393                         break;
2394                     }
2395                     c = c.caller;
2396                 }
2397             }
2398             return ev;
2399         },
2400
2401
2402         getCharCode: function(ev) {
2403             ev = ev.browserEvent || ev;
2404             return ev.charCode || ev.keyCode || 0;
2405         },
2406
2407
2408         _getCacheIndex: function(el, eventName, fn) {
2409             for (var i = 0,len = listeners.length; i < len; ++i) {
2410                 var li = listeners[i];
2411                 if (li &&
2412                     li[this.FN] == fn &&
2413                     li[this.EL] == el &&
2414                     li[this.TYPE] == eventName) {
2415                     return i;
2416                 }
2417             }
2418
2419             return -1;
2420         },
2421
2422
2423         elCache: {},
2424
2425
2426         getEl: function(id) {
2427             return document.getElementById(id);
2428         },
2429
2430
2431         clearCache: function() {
2432         },
2433
2434
2435         _load: function(e) {
2436             loadComplete = true;
2437             var EU = Roo.lib.Event;
2438
2439
2440             if (Roo.isIE) {
2441                 EU.doRemove(window, "load", EU._load);
2442             }
2443         },
2444
2445
2446         _tryPreloadAttach: function() {
2447
2448             if (this.locked) {
2449                 return false;
2450             }
2451
2452             this.locked = true;
2453
2454
2455             var tryAgain = !loadComplete;
2456             if (!tryAgain) {
2457                 tryAgain = (retryCount > 0);
2458             }
2459
2460
2461             var notAvail = [];
2462             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2463                 var item = onAvailStack[i];
2464                 if (item) {
2465                     var el = this.getEl(item.id);
2466
2467                     if (el) {
2468                         if (!item.checkReady ||
2469                             loadComplete ||
2470                             el.nextSibling ||
2471                             (document && document.body)) {
2472
2473                             var scope = el;
2474                             if (item.override) {
2475                                 if (item.override === true) {
2476                                     scope = item.obj;
2477                                 } else {
2478                                     scope = item.override;
2479                                 }
2480                             }
2481                             item.fn.call(scope, item.obj);
2482                             onAvailStack[i] = null;
2483                         }
2484                     } else {
2485                         notAvail.push(item);
2486                     }
2487                 }
2488             }
2489
2490             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2491
2492             if (tryAgain) {
2493
2494                 this.startInterval();
2495             } else {
2496                 clearInterval(this._interval);
2497                 this._interval = null;
2498             }
2499
2500             this.locked = false;
2501
2502             return true;
2503
2504         },
2505
2506
2507         purgeElement: function(el, recurse, eventName) {
2508             var elListeners = this.getListeners(el, eventName);
2509             if (elListeners) {
2510                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2511                     var l = elListeners[i];
2512                     this.removeListener(el, l.type, l.fn);
2513                 }
2514             }
2515
2516             if (recurse && el && el.childNodes) {
2517                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2518                     this.purgeElement(el.childNodes[i], recurse, eventName);
2519                 }
2520             }
2521         },
2522
2523
2524         getListeners: function(el, eventName) {
2525             var results = [], searchLists;
2526             if (!eventName) {
2527                 searchLists = [listeners, unloadListeners];
2528             } else if (eventName == "unload") {
2529                 searchLists = [unloadListeners];
2530             } else {
2531                 searchLists = [listeners];
2532             }
2533
2534             for (var j = 0; j < searchLists.length; ++j) {
2535                 var searchList = searchLists[j];
2536                 if (searchList && searchList.length > 0) {
2537                     for (var i = 0,len = searchList.length; i < len; ++i) {
2538                         var l = searchList[i];
2539                         if (l && l[this.EL] === el &&
2540                             (!eventName || eventName === l[this.TYPE])) {
2541                             results.push({
2542                                 type:   l[this.TYPE],
2543                                 fn:     l[this.FN],
2544                                 obj:    l[this.OBJ],
2545                                 adjust: l[this.ADJ_SCOPE],
2546                                 index:  i
2547                             });
2548                         }
2549                     }
2550                 }
2551             }
2552
2553             return (results.length) ? results : null;
2554         },
2555
2556
2557         _unload: function(e) {
2558
2559             var EU = Roo.lib.Event, i, j, l, len, index;
2560
2561             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2562                 l = unloadListeners[i];
2563                 if (l) {
2564                     var scope = window;
2565                     if (l[EU.ADJ_SCOPE]) {
2566                         if (l[EU.ADJ_SCOPE] === true) {
2567                             scope = l[EU.OBJ];
2568                         } else {
2569                             scope = l[EU.ADJ_SCOPE];
2570                         }
2571                     }
2572                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2573                     unloadListeners[i] = null;
2574                     l = null;
2575                     scope = null;
2576                 }
2577             }
2578
2579             unloadListeners = null;
2580
2581             if (listeners && listeners.length > 0) {
2582                 j = listeners.length;
2583                 while (j) {
2584                     index = j - 1;
2585                     l = listeners[index];
2586                     if (l) {
2587                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2588                                 l[EU.FN], index);
2589                     }
2590                     j = j - 1;
2591                 }
2592                 l = null;
2593
2594                 EU.clearCache();
2595             }
2596
2597             EU.doRemove(window, "unload", EU._unload);
2598
2599         },
2600
2601
2602         getScroll: function() {
2603             var dd = document.documentElement, db = document.body;
2604             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2605                 return [dd.scrollTop, dd.scrollLeft];
2606             } else if (db) {
2607                 return [db.scrollTop, db.scrollLeft];
2608             } else {
2609                 return [0, 0];
2610             }
2611         },
2612
2613
2614         doAdd: function () {
2615             if (window.addEventListener) {
2616                 return function(el, eventName, fn, capture) {
2617                     el.addEventListener(eventName, fn, (capture));
2618                 };
2619             } else if (window.attachEvent) {
2620                 return function(el, eventName, fn, capture) {
2621                     el.attachEvent("on" + eventName, fn);
2622                 };
2623             } else {
2624                 return function() {
2625                 };
2626             }
2627         }(),
2628
2629
2630         doRemove: function() {
2631             if (window.removeEventListener) {
2632                 return function (el, eventName, fn, capture) {
2633                     el.removeEventListener(eventName, fn, (capture));
2634                 };
2635             } else if (window.detachEvent) {
2636                 return function (el, eventName, fn) {
2637                     el.detachEvent("on" + eventName, fn);
2638                 };
2639             } else {
2640                 return function() {
2641                 };
2642             }
2643         }()
2644     };
2645     
2646 }();
2647 (function() {     
2648    
2649     var E = Roo.lib.Event;
2650     E.on = E.addListener;
2651     E.un = E.removeListener;
2652
2653     if (document && document.body) {
2654         E._load();
2655     } else {
2656         E.doAdd(window, "load", E._load);
2657     }
2658     E.doAdd(window, "unload", E._unload);
2659     E._tryPreloadAttach();
2660 })();
2661
2662  
2663
2664 (function() {
2665     /**
2666      * @class Roo.lib.Ajax
2667      *
2668      * provide a simple Ajax request utility functions
2669      * 
2670      * Portions of this file are based on pieces of Yahoo User Interface Library
2671     * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2672     * YUI licensed under the BSD License:
2673     * http://developer.yahoo.net/yui/license.txt
2674     * <script type="text/javascript">
2675     *
2676      *
2677      */
2678     Roo.lib.Ajax = {
2679         /**
2680          * @static 
2681          */
2682         request : function(method, uri, cb, data, options) {
2683             if(options){
2684                 var hs = options.headers;
2685                 if(hs){
2686                     for(var h in hs){
2687                         if(hs.hasOwnProperty(h)){
2688                             this.initHeader(h, hs[h], false);
2689                         }
2690                     }
2691                 }
2692                 if(options.xmlData){
2693                     this.initHeader('Content-Type', 'text/xml', false);
2694                     method = 'POST';
2695                     data = options.xmlData;
2696                 }
2697             }
2698
2699             return this.asyncRequest(method, uri, cb, data);
2700         },
2701         /**
2702          * serialize a form
2703          *
2704          * @static
2705          * @param {DomForm} form element
2706          * @return {String} urlencode form output.
2707          */
2708         serializeForm : function(form) {
2709             if(typeof form == 'string') {
2710                 form = (document.getElementById(form) || document.forms[form]);
2711             }
2712
2713             var el, name, val, disabled, data = '', hasSubmit = false;
2714             for (var i = 0; i < form.elements.length; i++) {
2715                 el = form.elements[i];
2716                 disabled = form.elements[i].disabled;
2717                 name = form.elements[i].name;
2718                 val = form.elements[i].value;
2719
2720                 if (!disabled && name){
2721                     switch (el.type)
2722                             {
2723                         case 'select-one':
2724                         case 'select-multiple':
2725                             for (var j = 0; j < el.options.length; j++) {
2726                                 if (el.options[j].selected) {
2727                                     if (Roo.isIE) {
2728                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2729                                     }
2730                                     else {
2731                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2732                                     }
2733                                 }
2734                             }
2735                             break;
2736                         case 'radio':
2737                         case 'checkbox':
2738                             if (el.checked) {
2739                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2740                             }
2741                             break;
2742                         case 'file':
2743
2744                         case undefined:
2745
2746                         case 'reset':
2747
2748                         case 'button':
2749
2750                             break;
2751                         case 'submit':
2752                             if(hasSubmit == false) {
2753                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2754                                 hasSubmit = true;
2755                             }
2756                             break;
2757                         default:
2758                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2759                             break;
2760                     }
2761                 }
2762             }
2763             data = data.substr(0, data.length - 1);
2764             return data;
2765         },
2766
2767         headers:{},
2768
2769         hasHeaders:false,
2770
2771         useDefaultHeader:true,
2772
2773         defaultPostHeader:'application/x-www-form-urlencoded',
2774
2775         useDefaultXhrHeader:true,
2776
2777         defaultXhrHeader:'XMLHttpRequest',
2778
2779         hasDefaultHeaders:true,
2780
2781         defaultHeaders:{},
2782
2783         poll:{},
2784
2785         timeout:{},
2786
2787         pollInterval:50,
2788
2789         transactionId:0,
2790
2791         setProgId:function(id)
2792         {
2793             this.activeX.unshift(id);
2794         },
2795
2796         setDefaultPostHeader:function(b)
2797         {
2798             this.useDefaultHeader = b;
2799         },
2800
2801         setDefaultXhrHeader:function(b)
2802         {
2803             this.useDefaultXhrHeader = b;
2804         },
2805
2806         setPollingInterval:function(i)
2807         {
2808             if (typeof i == 'number' && isFinite(i)) {
2809                 this.pollInterval = i;
2810             }
2811         },
2812
2813         createXhrObject:function(transactionId)
2814         {
2815             var obj,http;
2816             try
2817             {
2818
2819                 http = new XMLHttpRequest();
2820
2821                 obj = { conn:http, tId:transactionId };
2822             }
2823             catch(e)
2824             {
2825                 for (var i = 0; i < this.activeX.length; ++i) {
2826                     try
2827                     {
2828
2829                         http = new ActiveXObject(this.activeX[i]);
2830
2831                         obj = { conn:http, tId:transactionId };
2832                         break;
2833                     }
2834                     catch(e) {
2835                     }
2836                 }
2837             }
2838             finally
2839             {
2840                 return obj;
2841             }
2842         },
2843
2844         getConnectionObject:function()
2845         {
2846             var o;
2847             var tId = this.transactionId;
2848
2849             try
2850             {
2851                 o = this.createXhrObject(tId);
2852                 if (o) {
2853                     this.transactionId++;
2854                 }
2855             }
2856             catch(e) {
2857             }
2858             finally
2859             {
2860                 return o;
2861             }
2862         },
2863
2864         asyncRequest:function(method, uri, callback, postData)
2865         {
2866             var o = this.getConnectionObject();
2867
2868             if (!o) {
2869                 return null;
2870             }
2871             else {
2872                 o.conn.open(method, uri, true);
2873
2874                 if (this.useDefaultXhrHeader) {
2875                     if (!this.defaultHeaders['X-Requested-With']) {
2876                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2877                     }
2878                 }
2879
2880                 if(postData && this.useDefaultHeader){
2881                     this.initHeader('Content-Type', this.defaultPostHeader);
2882                 }
2883
2884                  if (this.hasDefaultHeaders || this.hasHeaders) {
2885                     this.setHeader(o);
2886                 }
2887
2888                 this.handleReadyState(o, callback);
2889                 o.conn.send(postData || null);
2890
2891                 return o;
2892             }
2893         },
2894
2895         handleReadyState:function(o, callback)
2896         {
2897             var oConn = this;
2898
2899             if (callback && callback.timeout) {
2900                 
2901                 this.timeout[o.tId] = window.setTimeout(function() {
2902                     oConn.abort(o, callback, true);
2903                 }, callback.timeout);
2904             }
2905
2906             this.poll[o.tId] = window.setInterval(
2907                     function() {
2908                         if (o.conn && o.conn.readyState == 4) {
2909                             window.clearInterval(oConn.poll[o.tId]);
2910                             delete oConn.poll[o.tId];
2911
2912                             if(callback && callback.timeout) {
2913                                 window.clearTimeout(oConn.timeout[o.tId]);
2914                                 delete oConn.timeout[o.tId];
2915                             }
2916
2917                             oConn.handleTransactionResponse(o, callback);
2918                         }
2919                     }
2920                     , this.pollInterval);
2921         },
2922
2923         handleTransactionResponse:function(o, callback, isAbort)
2924         {
2925
2926             if (!callback) {
2927                 this.releaseObject(o);
2928                 return;
2929             }
2930
2931             var httpStatus, responseObject;
2932
2933             try
2934             {
2935                 if (o.conn.status !== undefined && o.conn.status != 0) {
2936                     httpStatus = o.conn.status;
2937                 }
2938                 else {
2939                     httpStatus = 13030;
2940                 }
2941             }
2942             catch(e) {
2943
2944
2945                 httpStatus = 13030;
2946             }
2947
2948             if (httpStatus >= 200 && httpStatus < 300) {
2949                 responseObject = this.createResponseObject(o, callback.argument);
2950                 if (callback.success) {
2951                     if (!callback.scope) {
2952                         callback.success(responseObject);
2953                     }
2954                     else {
2955
2956
2957                         callback.success.apply(callback.scope, [responseObject]);
2958                     }
2959                 }
2960             }
2961             else {
2962                 switch (httpStatus) {
2963
2964                     case 12002:
2965                     case 12029:
2966                     case 12030:
2967                     case 12031:
2968                     case 12152:
2969                     case 13030:
2970                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2971                         if (callback.failure) {
2972                             if (!callback.scope) {
2973                                 callback.failure(responseObject);
2974                             }
2975                             else {
2976                                 callback.failure.apply(callback.scope, [responseObject]);
2977                             }
2978                         }
2979                         break;
2980                     default:
2981                         responseObject = this.createResponseObject(o, callback.argument);
2982                         if (callback.failure) {
2983                             if (!callback.scope) {
2984                                 callback.failure(responseObject);
2985                             }
2986                             else {
2987                                 callback.failure.apply(callback.scope, [responseObject]);
2988                             }
2989                         }
2990                 }
2991             }
2992
2993             this.releaseObject(o);
2994             responseObject = null;
2995         },
2996
2997         createResponseObject:function(o, callbackArg)
2998         {
2999             var obj = {};
3000             var headerObj = {};
3001
3002             try
3003             {
3004                 var headerStr = o.conn.getAllResponseHeaders();
3005                 var header = headerStr.split('\n');
3006                 for (var i = 0; i < header.length; i++) {
3007                     var delimitPos = header[i].indexOf(':');
3008                     if (delimitPos != -1) {
3009                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
3010                     }
3011                 }
3012             }
3013             catch(e) {
3014             }
3015
3016             obj.tId = o.tId;
3017             obj.status = o.conn.status;
3018             obj.statusText = o.conn.statusText;
3019             obj.getResponseHeader = headerObj;
3020             obj.getAllResponseHeaders = headerStr;
3021             obj.responseText = o.conn.responseText;
3022             obj.responseXML = o.conn.responseXML;
3023
3024             if (typeof callbackArg !== undefined) {
3025                 obj.argument = callbackArg;
3026             }
3027
3028             return obj;
3029         },
3030
3031         createExceptionObject:function(tId, callbackArg, isAbort)
3032         {
3033             var COMM_CODE = 0;
3034             var COMM_ERROR = 'communication failure';
3035             var ABORT_CODE = -1;
3036             var ABORT_ERROR = 'transaction aborted';
3037
3038             var obj = {};
3039
3040             obj.tId = tId;
3041             if (isAbort) {
3042                 obj.status = ABORT_CODE;
3043                 obj.statusText = ABORT_ERROR;
3044             }
3045             else {
3046                 obj.status = COMM_CODE;
3047                 obj.statusText = COMM_ERROR;
3048             }
3049
3050             if (callbackArg) {
3051                 obj.argument = callbackArg;
3052             }
3053
3054             return obj;
3055         },
3056
3057         initHeader:function(label, value, isDefault)
3058         {
3059             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
3060
3061             if (headerObj[label] === undefined) {
3062                 headerObj[label] = value;
3063             }
3064             else {
3065
3066
3067                 headerObj[label] = value + "," + headerObj[label];
3068             }
3069
3070             if (isDefault) {
3071                 this.hasDefaultHeaders = true;
3072             }
3073             else {
3074                 this.hasHeaders = true;
3075             }
3076         },
3077
3078
3079         setHeader:function(o)
3080         {
3081             if (this.hasDefaultHeaders) {
3082                 for (var prop in this.defaultHeaders) {
3083                     if (this.defaultHeaders.hasOwnProperty(prop)) {
3084                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
3085                     }
3086                 }
3087             }
3088
3089             if (this.hasHeaders) {
3090                 for (var prop in this.headers) {
3091                     if (this.headers.hasOwnProperty(prop)) {
3092                         o.conn.setRequestHeader(prop, this.headers[prop]);
3093                     }
3094                 }
3095                 this.headers = {};
3096                 this.hasHeaders = false;
3097             }
3098         },
3099
3100         resetDefaultHeaders:function() {
3101             delete this.defaultHeaders;
3102             this.defaultHeaders = {};
3103             this.hasDefaultHeaders = false;
3104         },
3105
3106         abort:function(o, callback, isTimeout)
3107         {
3108             if(this.isCallInProgress(o)) {
3109                 o.conn.abort();
3110                 window.clearInterval(this.poll[o.tId]);
3111                 delete this.poll[o.tId];
3112                 if (isTimeout) {
3113                     delete this.timeout[o.tId];
3114                 }
3115
3116                 this.handleTransactionResponse(o, callback, true);
3117
3118                 return true;
3119             }
3120             else {
3121                 return false;
3122             }
3123         },
3124
3125
3126         isCallInProgress:function(o)
3127         {
3128             if (o && o.conn) {
3129                 return o.conn.readyState != 4 && o.conn.readyState != 0;
3130             }
3131             else {
3132
3133                 return false;
3134             }
3135         },
3136
3137
3138         releaseObject:function(o)
3139         {
3140
3141             o.conn = null;
3142
3143             o = null;
3144         },
3145
3146         activeX:[
3147         'MSXML2.XMLHTTP.3.0',
3148         'MSXML2.XMLHTTP',
3149         'Microsoft.XMLHTTP'
3150         ]
3151
3152
3153     };
3154 })();/*
3155  * Portions of this file are based on pieces of Yahoo User Interface Library
3156  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3157  * YUI licensed under the BSD License:
3158  * http://developer.yahoo.net/yui/license.txt
3159  * <script type="text/javascript">
3160  *
3161  */
3162
3163 Roo.lib.Region = function(t, r, b, l) {
3164     this.top = t;
3165     this[1] = t;
3166     this.right = r;
3167     this.bottom = b;
3168     this.left = l;
3169     this[0] = l;
3170 };
3171
3172
3173 Roo.lib.Region.prototype = {
3174     contains : function(region) {
3175         return ( region.left >= this.left &&
3176                  region.right <= this.right &&
3177                  region.top >= this.top &&
3178                  region.bottom <= this.bottom    );
3179
3180     },
3181
3182     getArea : function() {
3183         return ( (this.bottom - this.top) * (this.right - this.left) );
3184     },
3185
3186     intersect : function(region) {
3187         var t = Math.max(this.top, region.top);
3188         var r = Math.min(this.right, region.right);
3189         var b = Math.min(this.bottom, region.bottom);
3190         var l = Math.max(this.left, region.left);
3191
3192         if (b >= t && r >= l) {
3193             return new Roo.lib.Region(t, r, b, l);
3194         } else {
3195             return null;
3196         }
3197     },
3198     union : function(region) {
3199         var t = Math.min(this.top, region.top);
3200         var r = Math.max(this.right, region.right);
3201         var b = Math.max(this.bottom, region.bottom);
3202         var l = Math.min(this.left, region.left);
3203
3204         return new Roo.lib.Region(t, r, b, l);
3205     },
3206
3207     adjust : function(t, l, b, r) {
3208         this.top += t;
3209         this.left += l;
3210         this.right += r;
3211         this.bottom += b;
3212         return this;
3213     }
3214 };
3215
3216 Roo.lib.Region.getRegion = function(el) {
3217     var p = Roo.lib.Dom.getXY(el);
3218
3219     var t = p[1];
3220     var r = p[0] + el.offsetWidth;
3221     var b = p[1] + el.offsetHeight;
3222     var l = p[0];
3223
3224     return new Roo.lib.Region(t, r, b, l);
3225 };
3226 /*
3227  * Portions of this file are based on pieces of Yahoo User Interface Library
3228  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3229  * YUI licensed under the BSD License:
3230  * http://developer.yahoo.net/yui/license.txt
3231  * <script type="text/javascript">
3232  *
3233  */
3234 //@@dep Roo.lib.Region
3235
3236
3237 Roo.lib.Point = function(x, y) {
3238     if (x instanceof Array) {
3239         y = x[1];
3240         x = x[0];
3241     }
3242     this.x = this.right = this.left = this[0] = x;
3243     this.y = this.top = this.bottom = this[1] = y;
3244 };
3245
3246 Roo.lib.Point.prototype = new Roo.lib.Region();
3247 /*
3248  * Portions of this file are based on pieces of Yahoo User Interface Library
3249  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3250  * YUI licensed under the BSD License:
3251  * http://developer.yahoo.net/yui/license.txt
3252  * <script type="text/javascript">
3253  *
3254  */
3255  
3256 (function() {   
3257
3258     Roo.lib.Anim = {
3259         scroll : function(el, args, duration, easing, cb, scope) {
3260             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3261         },
3262
3263         motion : function(el, args, duration, easing, cb, scope) {
3264             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3265         },
3266
3267         color : function(el, args, duration, easing, cb, scope) {
3268             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3269         },
3270
3271         run : function(el, args, duration, easing, cb, scope, type) {
3272             type = type || Roo.lib.AnimBase;
3273             if (typeof easing == "string") {
3274                 easing = Roo.lib.Easing[easing];
3275             }
3276             var anim = new type(el, args, duration, easing);
3277             anim.animateX(function() {
3278                 Roo.callback(cb, scope);
3279             });
3280             return anim;
3281         }
3282     };
3283 })();/*
3284  * Portions of this file are based on pieces of Yahoo User Interface Library
3285  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3286  * YUI licensed under the BSD License:
3287  * http://developer.yahoo.net/yui/license.txt
3288  * <script type="text/javascript">
3289  *
3290  */
3291
3292 (function() {    
3293     var libFlyweight;
3294     
3295     function fly(el) {
3296         if (!libFlyweight) {
3297             libFlyweight = new Roo.Element.Flyweight();
3298         }
3299         libFlyweight.dom = el;
3300         return libFlyweight;
3301     }
3302
3303     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3304     
3305    
3306     
3307     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3308         if (el) {
3309             this.init(el, attributes, duration, method);
3310         }
3311     };
3312
3313     Roo.lib.AnimBase.fly = fly;
3314     
3315     
3316     
3317     Roo.lib.AnimBase.prototype = {
3318
3319         toString: function() {
3320             var el = this.getEl();
3321             var id = el.id || el.tagName;
3322             return ("Anim " + id);
3323         },
3324
3325         patterns: {
3326             noNegatives:        /width|height|opacity|padding/i,
3327             offsetAttribute:  /^((width|height)|(top|left))$/,
3328             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3329             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3330         },
3331
3332
3333         doMethod: function(attr, start, end) {
3334             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3335         },
3336
3337
3338         setAttribute: function(attr, val, unit) {
3339             if (this.patterns.noNegatives.test(attr)) {
3340                 val = (val > 0) ? val : 0;
3341             }
3342
3343             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3344         },
3345
3346
3347         getAttribute: function(attr) {
3348             var el = this.getEl();
3349             var val = fly(el).getStyle(attr);
3350
3351             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3352                 return parseFloat(val);
3353             }
3354
3355             var a = this.patterns.offsetAttribute.exec(attr) || [];
3356             var pos = !!( a[3] );
3357             var box = !!( a[2] );
3358
3359
3360             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3361                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3362             } else {
3363                 val = 0;
3364             }
3365
3366             return val;
3367         },
3368
3369
3370         getDefaultUnit: function(attr) {
3371             if (this.patterns.defaultUnit.test(attr)) {
3372                 return 'px';
3373             }
3374
3375             return '';
3376         },
3377
3378         animateX : function(callback, scope) {
3379             var f = function() {
3380                 this.onComplete.removeListener(f);
3381                 if (typeof callback == "function") {
3382                     callback.call(scope || this, this);
3383                 }
3384             };
3385             this.onComplete.addListener(f, this);
3386             this.animate();
3387         },
3388
3389
3390         setRuntimeAttribute: function(attr) {
3391             var start;
3392             var end;
3393             var attributes = this.attributes;
3394
3395             this.runtimeAttributes[attr] = {};
3396
3397             var isset = function(prop) {
3398                 return (typeof prop !== 'undefined');
3399             };
3400
3401             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3402                 return false;
3403             }
3404
3405             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3406
3407
3408             if (isset(attributes[attr]['to'])) {
3409                 end = attributes[attr]['to'];
3410             } else if (isset(attributes[attr]['by'])) {
3411                 if (start.constructor == Array) {
3412                     end = [];
3413                     for (var i = 0, len = start.length; i < len; ++i) {
3414                         end[i] = start[i] + attributes[attr]['by'][i];
3415                     }
3416                 } else {
3417                     end = start + attributes[attr]['by'];
3418                 }
3419             }
3420
3421             this.runtimeAttributes[attr].start = start;
3422             this.runtimeAttributes[attr].end = end;
3423
3424
3425             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3426         },
3427
3428
3429         init: function(el, attributes, duration, method) {
3430
3431             var isAnimated = false;
3432
3433
3434             var startTime = null;
3435
3436
3437             var actualFrames = 0;
3438
3439
3440             el = Roo.getDom(el);
3441
3442
3443             this.attributes = attributes || {};
3444
3445
3446             this.duration = duration || 1;
3447
3448
3449             this.method = method || Roo.lib.Easing.easeNone;
3450
3451
3452             this.useSeconds = true;
3453
3454
3455             this.currentFrame = 0;
3456
3457
3458             this.totalFrames = Roo.lib.AnimMgr.fps;
3459
3460
3461             this.getEl = function() {
3462                 return el;
3463             };
3464
3465
3466             this.isAnimated = function() {
3467                 return isAnimated;
3468             };
3469
3470
3471             this.getStartTime = function() {
3472                 return startTime;
3473             };
3474
3475             this.runtimeAttributes = {};
3476
3477
3478             this.animate = function() {
3479                 if (this.isAnimated()) {
3480                     return false;
3481                 }
3482
3483                 this.currentFrame = 0;
3484
3485                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3486
3487                 Roo.lib.AnimMgr.registerElement(this);
3488             };
3489
3490
3491             this.stop = function(finish) {
3492                 if (finish) {
3493                     this.currentFrame = this.totalFrames;
3494                     this._onTween.fire();
3495                 }
3496                 Roo.lib.AnimMgr.stop(this);
3497             };
3498
3499             var onStart = function() {
3500                 this.onStart.fire();
3501
3502                 this.runtimeAttributes = {};
3503                 for (var attr in this.attributes) {
3504                     this.setRuntimeAttribute(attr);
3505                 }
3506
3507                 isAnimated = true;
3508                 actualFrames = 0;
3509                 startTime = new Date();
3510             };
3511
3512
3513             var onTween = function() {
3514                 var data = {
3515                     duration: new Date() - this.getStartTime(),
3516                     currentFrame: this.currentFrame
3517                 };
3518
3519                 data.toString = function() {
3520                     return (
3521                             'duration: ' + data.duration +
3522                             ', currentFrame: ' + data.currentFrame
3523                             );
3524                 };
3525
3526                 this.onTween.fire(data);
3527
3528                 var runtimeAttributes = this.runtimeAttributes;
3529
3530                 for (var attr in runtimeAttributes) {
3531                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3532                 }
3533
3534                 actualFrames += 1;
3535             };
3536
3537             var onComplete = function() {
3538                 var actual_duration = (new Date() - startTime) / 1000 ;
3539
3540                 var data = {
3541                     duration: actual_duration,
3542                     frames: actualFrames,
3543                     fps: actualFrames / actual_duration
3544                 };
3545
3546                 data.toString = function() {
3547                     return (
3548                             'duration: ' + data.duration +
3549                             ', frames: ' + data.frames +
3550                             ', fps: ' + data.fps
3551                             );
3552                 };
3553
3554                 isAnimated = false;
3555                 actualFrames = 0;
3556                 this.onComplete.fire(data);
3557             };
3558
3559
3560             this._onStart = new Roo.util.Event(this);
3561             this.onStart = new Roo.util.Event(this);
3562             this.onTween = new Roo.util.Event(this);
3563             this._onTween = new Roo.util.Event(this);
3564             this.onComplete = new Roo.util.Event(this);
3565             this._onComplete = new Roo.util.Event(this);
3566             this._onStart.addListener(onStart);
3567             this._onTween.addListener(onTween);
3568             this._onComplete.addListener(onComplete);
3569         }
3570     };
3571 })();
3572 /*
3573  * Portions of this file are based on pieces of Yahoo User Interface Library
3574  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3575  * YUI licensed under the BSD License:
3576  * http://developer.yahoo.net/yui/license.txt
3577  * <script type="text/javascript">
3578  *
3579  */
3580
3581 Roo.lib.AnimMgr = new function() {
3582
3583     var thread = null;
3584
3585
3586     var queue = [];
3587
3588
3589     var tweenCount = 0;
3590
3591
3592     this.fps = 1000;
3593
3594
3595     this.delay = 1;
3596
3597
3598     this.registerElement = function(tween) {
3599         queue[queue.length] = tween;
3600         tweenCount += 1;
3601         tween._onStart.fire();
3602         this.start();
3603     };
3604
3605
3606     this.unRegister = function(tween, index) {
3607         tween._onComplete.fire();
3608         index = index || getIndex(tween);
3609         if (index != -1) {
3610             queue.splice(index, 1);
3611         }
3612
3613         tweenCount -= 1;
3614         if (tweenCount <= 0) {
3615             this.stop();
3616         }
3617     };
3618
3619
3620     this.start = function() {
3621         if (thread === null) {
3622             thread = setInterval(this.run, this.delay);
3623         }
3624     };
3625
3626
3627     this.stop = function(tween) {
3628         if (!tween) {
3629             clearInterval(thread);
3630
3631             for (var i = 0, len = queue.length; i < len; ++i) {
3632                 if (queue[0].isAnimated()) {
3633                     this.unRegister(queue[0], 0);
3634                 }
3635             }
3636
3637             queue = [];
3638             thread = null;
3639             tweenCount = 0;
3640         }
3641         else {
3642             this.unRegister(tween);
3643         }
3644     };
3645
3646
3647     this.run = function() {
3648         for (var i = 0, len = queue.length; i < len; ++i) {
3649             var tween = queue[i];
3650             if (!tween || !tween.isAnimated()) {
3651                 continue;
3652             }
3653
3654             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3655             {
3656                 tween.currentFrame += 1;
3657
3658                 if (tween.useSeconds) {
3659                     correctFrame(tween);
3660                 }
3661                 tween._onTween.fire();
3662             }
3663             else {
3664                 Roo.lib.AnimMgr.stop(tween, i);
3665             }
3666         }
3667     };
3668
3669     var getIndex = function(anim) {
3670         for (var i = 0, len = queue.length; i < len; ++i) {
3671             if (queue[i] == anim) {
3672                 return i;
3673             }
3674         }
3675         return -1;
3676     };
3677
3678
3679     var correctFrame = function(tween) {
3680         var frames = tween.totalFrames;
3681         var frame = tween.currentFrame;
3682         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3683         var elapsed = (new Date() - tween.getStartTime());
3684         var tweak = 0;
3685
3686         if (elapsed < tween.duration * 1000) {
3687             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3688         } else {
3689             tweak = frames - (frame + 1);
3690         }
3691         if (tweak > 0 && isFinite(tweak)) {
3692             if (tween.currentFrame + tweak >= frames) {
3693                 tweak = frames - (frame + 1);
3694             }
3695
3696             tween.currentFrame += tweak;
3697         }
3698     };
3699 };
3700
3701     /*
3702  * Portions of this file are based on pieces of Yahoo User Interface Library
3703  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3704  * YUI licensed under the BSD License:
3705  * http://developer.yahoo.net/yui/license.txt
3706  * <script type="text/javascript">
3707  *
3708  */
3709 Roo.lib.Bezier = new function() {
3710
3711         this.getPosition = function(points, t) {
3712             var n = points.length;
3713             var tmp = [];
3714
3715             for (var i = 0; i < n; ++i) {
3716                 tmp[i] = [points[i][0], points[i][1]];
3717             }
3718
3719             for (var j = 1; j < n; ++j) {
3720                 for (i = 0; i < n - j; ++i) {
3721                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3722                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3723                 }
3724             }
3725
3726             return [ tmp[0][0], tmp[0][1] ];
3727
3728         };
3729     }; 
3730
3731 /**
3732  * @class Roo.lib.Color
3733  * @constructor
3734  * An abstract Color implementation. Concrete Color implementations should use
3735  * an instance of this function as their prototype, and implement the getRGB and
3736  * getHSL functions. getRGB should return an object representing the RGB
3737  * components of this Color, with the red, green, and blue components in the
3738  * range [0,255] and the alpha component in the range [0,100]. getHSL should
3739  * return an object representing the HSL components of this Color, with the hue
3740  * component in the range [0,360), the saturation and lightness components in
3741  * the range [0,100], and the alpha component in the range [0,1].
3742  *
3743  *
3744  * Color.js
3745  *
3746  * Functions for Color handling and processing.
3747  *
3748  * http://www.safalra.com/web-design/javascript/Color-handling-and-processing/
3749  *
3750  * The author of this program, Safalra (Stephen Morley), irrevocably releases all
3751  * rights to this program, with the intention of it becoming part of the public
3752  * domain. Because this program is released into the public domain, it comes with
3753  * no warranty either expressed or implied, to the extent permitted by law.
3754  * 
3755  * For more free and public domain JavaScript code by the same author, visit:
3756  * http://www.safalra.com/web-design/javascript/
3757  * 
3758  */
3759 Roo.lib.Color = function() { }
3760
3761
3762 Roo.apply(Roo.lib.Color.prototype, {
3763   
3764   rgb : null,
3765   hsv : null,
3766   hsl : null,
3767   
3768   /**
3769    * getIntegerRGB
3770    * @return {Object} an object representing the RGBA components of this Color. The red,
3771    * green, and blue components are converted to integers in the range [0,255].
3772    * The alpha is a value in the range [0,1].
3773    */
3774   getIntegerRGB : function(){
3775
3776     // get the RGB components of this Color
3777     var rgb = this.getRGB();
3778
3779     // return the integer components
3780     return {
3781       'r' : Math.round(rgb.r),
3782       'g' : Math.round(rgb.g),
3783       'b' : Math.round(rgb.b),
3784       'a' : rgb.a
3785     };
3786
3787   },
3788
3789   /**
3790    * getPercentageRGB
3791    * @return {Object} an object representing the RGBA components of this Color. The red,
3792    * green, and blue components are converted to numbers in the range [0,100].
3793    * The alpha is a value in the range [0,1].
3794    */
3795   getPercentageRGB : function(){
3796
3797     // get the RGB components of this Color
3798     var rgb = this.getRGB();
3799
3800     // return the percentage components
3801     return {
3802       'r' : 100 * rgb.r / 255,
3803       'g' : 100 * rgb.g / 255,
3804       'b' : 100 * rgb.b / 255,
3805       'a' : rgb.a
3806     };
3807
3808   },
3809
3810   /**
3811    * getCSSHexadecimalRGB
3812    * @return {String} a string representing this Color as a CSS hexadecimal RGB Color
3813    * value - that is, a string of the form #RRGGBB where each of RR, GG, and BB
3814    * are two-digit hexadecimal numbers.
3815    */
3816   getCSSHexadecimalRGB : function()
3817   {
3818
3819     // get the integer RGB components
3820     var rgb = this.getIntegerRGB();
3821
3822     // determine the hexadecimal equivalents
3823     var r16 = rgb.r.toString(16);
3824     var g16 = rgb.g.toString(16);
3825     var b16 = rgb.b.toString(16);
3826
3827     // return the CSS RGB Color value
3828     return '#'
3829         + (r16.length == 2 ? r16 : '0' + r16)
3830         + (g16.length == 2 ? g16 : '0' + g16)
3831         + (b16.length == 2 ? b16 : '0' + b16);
3832
3833   },
3834
3835   /**
3836    * getCSSIntegerRGB
3837    * @return {String} a string representing this Color as a CSS integer RGB Color
3838    * value - that is, a string of the form rgb(r,g,b) where each of r, g, and b
3839    * are integers in the range [0,255].
3840    */
3841   getCSSIntegerRGB : function(){
3842
3843     // get the integer RGB components
3844     var rgb = this.getIntegerRGB();
3845
3846     // return the CSS RGB Color value
3847     return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ')';
3848
3849   },
3850
3851   /**
3852    * getCSSIntegerRGBA
3853    * @return {String} Returns a string representing this Color as a CSS integer RGBA Color
3854    * value - that is, a string of the form rgba(r,g,b,a) where each of r, g, and
3855    * b are integers in the range [0,255] and a is in the range [0,1].
3856    */
3857   getCSSIntegerRGBA : function(){
3858
3859     // get the integer RGB components
3860     var rgb = this.getIntegerRGB();
3861
3862     // return the CSS integer RGBA Color value
3863     return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + rgb.a + ')';
3864
3865   },
3866
3867   /**
3868    * getCSSPercentageRGB
3869    * @return {String} a string representing this Color as a CSS percentage RGB Color
3870    * value - that is, a string of the form rgb(r%,g%,b%) where each of r, g, and
3871    * b are in the range [0,100].
3872    */
3873   getCSSPercentageRGB : function(){
3874
3875     // get the percentage RGB components
3876     var rgb = this.getPercentageRGB();
3877
3878     // return the CSS RGB Color value
3879     return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%)';
3880
3881   },
3882
3883   /**
3884    * getCSSPercentageRGBA
3885    * @return {String} a string representing this Color as a CSS percentage RGBA Color
3886    * value - that is, a string of the form rgba(r%,g%,b%,a) where each of r, g,
3887    * and b are in the range [0,100] and a is in the range [0,1].
3888    */
3889   getCSSPercentageRGBA : function(){
3890
3891     // get the percentage RGB components
3892     var rgb = this.getPercentageRGB();
3893
3894     // return the CSS percentage RGBA Color value
3895     return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%,' + rgb.a + ')';
3896
3897   },
3898
3899   /**
3900    * getCSSHSL
3901    * @return {String} a string representing this Color as a CSS HSL Color value - that
3902    * is, a string of the form hsl(h,s%,l%) where h is in the range [0,100] and
3903    * s and l are in the range [0,100].
3904    */
3905   getCSSHSL : function(){
3906
3907     // get the HSL components
3908     var hsl = this.getHSL();
3909
3910     // return the CSS HSL Color value
3911     return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%)';
3912
3913   },
3914
3915   /**
3916    * getCSSHSLA
3917    * @return {String} a string representing this Color as a CSS HSLA Color value - that
3918    * is, a string of the form hsla(h,s%,l%,a) where h is in the range [0,100],
3919    * s and l are in the range [0,100], and a is in the range [0,1].
3920    */
3921   getCSSHSLA : function(){
3922
3923     // get the HSL components
3924     var hsl = this.getHSL();
3925
3926     // return the CSS HSL Color value
3927     return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%,' + hsl.a + ')';
3928
3929   },
3930
3931   /**
3932    * Sets the Color of the specified node to this Color. This functions sets
3933    * the CSS 'color' property for the node. The parameter is:
3934    * 
3935    * @param {DomElement} node - the node whose Color should be set
3936    */
3937   setNodeColor : function(node){
3938
3939     // set the Color of the node
3940     node.style.color = this.getCSSHexadecimalRGB();
3941
3942   },
3943
3944   /**
3945    * Sets the background Color of the specified node to this Color. This
3946    * functions sets the CSS 'background-color' property for the node. The
3947    * parameter is:
3948    *
3949    * @param {DomElement} node - the node whose background Color should be set
3950    */
3951   setNodeBackgroundColor : function(node){
3952
3953     // set the background Color of the node
3954     node.style.backgroundColor = this.getCSSHexadecimalRGB();
3955
3956   },
3957   // convert between formats..
3958   toRGB: function()
3959   {
3960     var r = this.getIntegerRGB();
3961     return new Roo.lib.RGBColor(r.r,r.g,r.b,r.a);
3962     
3963   },
3964   toHSL : function()
3965   {
3966      var hsl = this.getHSL();
3967   // return the CSS HSL Color value
3968     return new Roo.lib.HSLColor(hsl.h,  hsl.s, hsl.l ,  hsl.a );
3969     
3970   },
3971   
3972   toHSV : function()
3973   {
3974     var rgb = this.toRGB();
3975     var hsv = rgb.getHSV();
3976    // return the CSS HSL Color value
3977     return new Roo.lib.HSVColor(hsv.h,  hsv.s, hsv.v ,  hsv.a );
3978     
3979   },
3980   
3981   // modify  v = 0 ... 1 (eg. 0.5)
3982   saturate : function(v)
3983   {
3984       var rgb = this.toRGB();
3985       var hsv = rgb.getHSV();
3986       return new Roo.lib.HSVColor(hsv.h,  hsv.s * v, hsv.v ,  hsv.a );
3987       
3988   
3989   },
3990   
3991    
3992   /**
3993    * getRGB
3994    * @return {Object} the RGB and alpha components of this Color as an object with r,
3995    * g, b, and a properties. r, g, and b are in the range [0,255] and a is in
3996    * the range [0,1].
3997    */
3998   getRGB: function(){
3999    
4000     // return the RGB components
4001     return {
4002       'r' : this.rgb.r,
4003       'g' : this.rgb.g,
4004       'b' : this.rgb.b,
4005       'a' : this.alpha
4006     };
4007
4008   },
4009
4010   /**
4011    * getHSV
4012    * @return {Object} the HSV and alpha components of this Color as an object with h,
4013    * s, v, and a properties. h is in the range [0,360), s and v are in the range
4014    * [0,100], and a is in the range [0,1].
4015    */
4016   getHSV : function()
4017   {
4018     
4019     // calculate the HSV components if necessary
4020     if (this.hsv == null) {
4021       this.calculateHSV();
4022     }
4023
4024     // return the HSV components
4025     return {
4026       'h' : this.hsv.h,
4027       's' : this.hsv.s,
4028       'v' : this.hsv.v,
4029       'a' : this.alpha
4030     };
4031
4032   },
4033
4034   /**
4035    * getHSL
4036    * @return {Object} the HSL and alpha components of this Color as an object with h,
4037    * s, l, and a properties. h is in the range [0,360), s and l are in the range
4038    * [0,100], and a is in the range [0,1].
4039    */
4040   getHSL : function(){
4041     
4042      
4043     // calculate the HSV components if necessary
4044     if (this.hsl == null) { this.calculateHSL(); }
4045
4046     // return the HSL components
4047     return {
4048       'h' : this.hsl.h,
4049       's' : this.hsl.s,
4050       'l' : this.hsl.l,
4051       'a' : this.alpha
4052     };
4053
4054   }
4055   
4056
4057 });
4058
4059
4060 /**
4061  * @class Roo.lib.RGBColor
4062  * @extends Roo.lib.Color
4063  * Creates a Color specified in the RGB Color space, with an optional alpha
4064  * component. The parameters are:
4065  * @constructor
4066  * 
4067
4068  * @param {Number} r - the red component, clipped to the range [0,255]
4069  * @param {Number} g - the green component, clipped to the range [0,255]
4070  * @param {Number} b - the blue component, clipped to the range [0,255]
4071  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4072  *     optional and defaults to 1
4073  */
4074 Roo.lib.RGBColor = function (r, g, b, a){
4075
4076   // store the alpha component after clipping it if necessary
4077   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4078
4079   // store the RGB components after clipping them if necessary
4080   this.rgb =
4081       {
4082         'r' : Math.max(0, Math.min(255, r)),
4083         'g' : Math.max(0, Math.min(255, g)),
4084         'b' : Math.max(0, Math.min(255, b))
4085       };
4086
4087   // initialise the HSV and HSL components to null
4088   
4089
4090   /* 
4091    * //private returns the HSV or HSL hue component of this RGBColor. The hue is in the
4092    * range [0,360). The parameters are:
4093    *
4094    * maximum - the maximum of the RGB component values
4095    * range   - the range of the RGB component values
4096    */
4097    
4098
4099 }
4100 // this does an 'exteds'
4101 Roo.extend(Roo.lib.RGBColor, Roo.lib.Color, {
4102
4103   
4104     getHue  : function(maximum, range)
4105     {
4106       var rgb = this.rgb;
4107        
4108       // check whether the range is zero
4109       if (range == 0){
4110   
4111         // set the hue to zero (any hue is acceptable as the Color is grey)
4112         var hue = 0;
4113   
4114       }else{
4115   
4116         // determine which of the components has the highest value and set the hue
4117         switch (maximum){
4118   
4119           // red has the highest value
4120           case rgb.r:
4121             var hue = (rgb.g - rgb.b) / range * 60;
4122             if (hue < 0) { hue += 360; }
4123             break;
4124   
4125           // green has the highest value
4126           case rgb.g:
4127             var hue = (rgb.b - rgb.r) / range * 60 + 120;
4128             break;
4129   
4130           // blue has the highest value
4131           case rgb.b:
4132             var hue = (rgb.r - rgb.g) / range * 60 + 240;
4133             break;
4134   
4135         }
4136   
4137       }
4138   
4139       // return the hue
4140       return hue;
4141   
4142     },
4143
4144   /* //private Calculates and stores the HSV components of this RGBColor so that they can
4145    * be returned be the getHSV function.
4146    */
4147    calculateHSV : function(){
4148     var rgb = this.rgb;
4149     // get the maximum and range of the RGB component values
4150     var maximum = Math.max(rgb.r, rgb.g, rgb.b);
4151     var range   = maximum - Math.min(rgb.r, rgb.g, rgb.b);
4152
4153     // store the HSV components
4154     this.hsv =
4155         {
4156           'h' : this.getHue(maximum, range),
4157           's' : (maximum == 0 ? 0 : 100 * range / maximum),
4158           'v' : maximum / 2.55
4159         };
4160
4161   },
4162
4163   /* //private Calculates and stores the HSL components of this RGBColor so that they can
4164    * be returned be the getHSL function.
4165    */
4166    calculateHSL : function(){
4167     var rgb = this.rgb;
4168     // get the maximum and range of the RGB component values
4169     var maximum = Math.max(rgb.r, rgb.g, rgb.b);
4170     var range   = maximum - Math.min(rgb.r, rgb.g, rgb.b);
4171
4172     // determine the lightness in the range [0,1]
4173     var l = maximum / 255 - range / 510;
4174
4175     // store the HSL components
4176     this.hsl =
4177         {
4178           'h' : this.getHue(maximum, range),
4179           's' : (range == 0 ? 0 : range / 2.55 / (l < 0.5 ? l * 2 : 2 - l * 2)),
4180           'l' : 100 * l
4181         };
4182
4183   }
4184
4185 });
4186
4187 /**
4188  * @class Roo.lib.HSVColor
4189  * @extends Roo.lib.Color
4190  * Creates a Color specified in the HSV Color space, with an optional alpha
4191  * component. The parameters are:
4192  * @constructor
4193  *
4194  * @param {Number} h - the hue component, wrapped to the range [0,360)
4195  * @param {Number} s - the saturation component, clipped to the range [0,100]
4196  * @param {Number} v - the value component, clipped to the range [0,100]
4197  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4198  *     optional and defaults to 1
4199  */
4200 Roo.lib.HSVColor = function (h, s, v, a){
4201
4202   // store the alpha component after clipping it if necessary
4203   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4204
4205   // store the HSV components after clipping or wrapping them if necessary
4206   this.hsv =
4207       {
4208         'h' : (h % 360 + 360) % 360,
4209         's' : Math.max(0, Math.min(100, s)),
4210         'v' : Math.max(0, Math.min(100, v))
4211       };
4212
4213   // initialise the RGB and HSL components to null
4214   this.rgb = null;
4215   this.hsl = null;
4216 }
4217
4218 Roo.extend(Roo.lib.HSVColor, Roo.lib.Color, {
4219   /* Calculates and stores the RGB components of this HSVColor so that they can
4220    * be returned be the getRGB function.
4221    */
4222   calculateRGB: function ()
4223   {
4224     var hsv = this.hsv;
4225     // check whether the saturation is zero
4226     if (hsv.s == 0){
4227
4228       // set the Color to the appropriate shade of grey
4229       var r = hsv.v;
4230       var g = hsv.v;
4231       var b = hsv.v;
4232
4233     }else{
4234
4235       // set some temporary values
4236       var f  = hsv.h / 60 - Math.floor(hsv.h / 60);
4237       var p  = hsv.v * (1 - hsv.s / 100);
4238       var q  = hsv.v * (1 - hsv.s / 100 * f);
4239       var t  = hsv.v * (1 - hsv.s / 100 * (1 - f));
4240
4241       // set the RGB Color components to their temporary values
4242       switch (Math.floor(hsv.h / 60)){
4243         case 0: var r = hsv.v; var g = t; var b = p; break;
4244         case 1: var r = q; var g = hsv.v; var b = p; break;
4245         case 2: var r = p; var g = hsv.v; var b = t; break;
4246         case 3: var r = p; var g = q; var b = hsv.v; break;
4247         case 4: var r = t; var g = p; var b = hsv.v; break;
4248         case 5: var r = hsv.v; var g = p; var b = q; break;
4249       }
4250
4251     }
4252
4253     // store the RGB components
4254     this.rgb =
4255         {
4256           'r' : r * 2.55,
4257           'g' : g * 2.55,
4258           'b' : b * 2.55
4259         };
4260
4261   },
4262
4263   /* Calculates and stores the HSL components of this HSVColor so that they can
4264    * be returned be the getHSL function.
4265    */
4266   calculateHSL : function (){
4267
4268     var hsv = this.hsv;
4269     // determine the lightness in the range [0,100]
4270     var l = (2 - hsv.s / 100) * hsv.v / 2;
4271
4272     // store the HSL components
4273     this.hsl =
4274         {
4275           'h' : hsv.h,
4276           's' : hsv.s * hsv.v / (l < 50 ? l * 2 : 200 - l * 2),
4277           'l' : l
4278         };
4279
4280     // correct a division-by-zero error
4281     if (isNaN(hsl.s)) { hsl.s = 0; }
4282
4283   } 
4284  
4285
4286 });
4287  
4288
4289 /**
4290  * @class Roo.lib.HSLColor
4291  * @extends Roo.lib.Color
4292  *
4293  * @constructor
4294  * Creates a Color specified in the HSL Color space, with an optional alpha
4295  * component. The parameters are:
4296  *
4297  * @param {Number} h - the hue component, wrapped to the range [0,360)
4298  * @param {Number} s - the saturation component, clipped to the range [0,100]
4299  * @param {Number} l - the lightness component, clipped to the range [0,100]
4300  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4301  *     optional and defaults to 1
4302  */
4303
4304 Roo.lib.HSLColor = function(h, s, l, a){
4305
4306   // store the alpha component after clipping it if necessary
4307   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4308
4309   // store the HSL components after clipping or wrapping them if necessary
4310   this.hsl =
4311       {
4312         'h' : (h % 360 + 360) % 360,
4313         's' : Math.max(0, Math.min(100, s)),
4314         'l' : Math.max(0, Math.min(100, l))
4315       };
4316
4317   // initialise the RGB and HSV components to null
4318 }
4319
4320 Roo.extend(Roo.lib.HSLColor, Roo.lib.Color, {
4321
4322   /* Calculates and stores the RGB components of this HSLColor so that they can
4323    * be returned be the getRGB function.
4324    */
4325   calculateRGB: function (){
4326
4327     // check whether the saturation is zero
4328     if (this.hsl.s == 0){
4329
4330       // store the RGB components representing the appropriate shade of grey
4331       this.rgb =
4332           {
4333             'r' : this.hsl.l * 2.55,
4334             'g' : this.hsl.l * 2.55,
4335             'b' : this.hsl.l * 2.55
4336           };
4337
4338     }else{
4339
4340       // set some temporary values
4341       var p = this.hsl.l < 50
4342             ? this.hsl.l * (1 + hsl.s / 100)
4343             : this.hsl.l + hsl.s - hsl.l * hsl.s / 100;
4344       var q = 2 * hsl.l - p;
4345
4346       // initialise the RGB components
4347       this.rgb =
4348           {
4349             'r' : (h + 120) / 60 % 6,
4350             'g' : h / 60,
4351             'b' : (h + 240) / 60 % 6
4352           };
4353
4354       // loop over the RGB components
4355       for (var key in this.rgb){
4356
4357         // ensure that the property is not inherited from the root object
4358         if (this.rgb.hasOwnProperty(key)){
4359
4360           // set the component to its value in the range [0,100]
4361           if (this.rgb[key] < 1){
4362             this.rgb[key] = q + (p - q) * this.rgb[key];
4363           }else if (this.rgb[key] < 3){
4364             this.rgb[key] = p;
4365           }else if (this.rgb[key] < 4){
4366             this.rgb[key] = q + (p - q) * (4 - this.rgb[key]);
4367           }else{
4368             this.rgb[key] = q;
4369           }
4370
4371           // set the component to its value in the range [0,255]
4372           this.rgb[key] *= 2.55;
4373
4374         }
4375
4376       }
4377
4378     }
4379
4380   },
4381
4382   /* Calculates and stores the HSV components of this HSLColor so that they can
4383    * be returned be the getHSL function.
4384    */
4385    calculateHSV : function(){
4386
4387     // set a temporary value
4388     var t = this.hsl.s * (this.hsl.l < 50 ? this.hsl.l : 100 - this.hsl.l) / 100;
4389
4390     // store the HSV components
4391     this.hsv =
4392         {
4393           'h' : this.hsl.h,
4394           's' : 200 * t / (this.hsl.l + t),
4395           'v' : t + this.hsl.l
4396         };
4397
4398     // correct a division-by-zero error
4399     if (isNaN(this.hsv.s)) { this.hsv.s = 0; }
4400
4401   }
4402  
4403
4404 });
4405 /*
4406  * Portions of this file are based on pieces of Yahoo User Interface Library
4407  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4408  * YUI licensed under the BSD License:
4409  * http://developer.yahoo.net/yui/license.txt
4410  * <script type="text/javascript">
4411  *
4412  */
4413 (function() {
4414
4415     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
4416         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
4417     };
4418
4419     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
4420
4421     var fly = Roo.lib.AnimBase.fly;
4422     var Y = Roo.lib;
4423     var superclass = Y.ColorAnim.superclass;
4424     var proto = Y.ColorAnim.prototype;
4425
4426     proto.toString = function() {
4427         var el = this.getEl();
4428         var id = el.id || el.tagName;
4429         return ("ColorAnim " + id);
4430     };
4431
4432     proto.patterns.color = /color$/i;
4433     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
4434     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
4435     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
4436     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
4437
4438
4439     proto.parseColor = function(s) {
4440         if (s.length == 3) {
4441             return s;
4442         }
4443
4444         var c = this.patterns.hex.exec(s);
4445         if (c && c.length == 4) {
4446             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
4447         }
4448
4449         c = this.patterns.rgb.exec(s);
4450         if (c && c.length == 4) {
4451             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
4452         }
4453
4454         c = this.patterns.hex3.exec(s);
4455         if (c && c.length == 4) {
4456             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
4457         }
4458
4459         return null;
4460     };
4461     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
4462     proto.getAttribute = function(attr) {
4463         var el = this.getEl();
4464         if (this.patterns.color.test(attr)) {
4465             var val = fly(el).getStyle(attr);
4466
4467             if (this.patterns.transparent.test(val)) {
4468                 var parent = el.parentNode;
4469                 val = fly(parent).getStyle(attr);
4470
4471                 while (parent && this.patterns.transparent.test(val)) {
4472                     parent = parent.parentNode;
4473                     val = fly(parent).getStyle(attr);
4474                     if (parent.tagName.toUpperCase() == 'HTML') {
4475                         val = '#fff';
4476                     }
4477                 }
4478             }
4479         } else {
4480             val = superclass.getAttribute.call(this, attr);
4481         }
4482
4483         return val;
4484     };
4485     proto.getAttribute = function(attr) {
4486         var el = this.getEl();
4487         if (this.patterns.color.test(attr)) {
4488             var val = fly(el).getStyle(attr);
4489
4490             if (this.patterns.transparent.test(val)) {
4491                 var parent = el.parentNode;
4492                 val = fly(parent).getStyle(attr);
4493
4494                 while (parent && this.patterns.transparent.test(val)) {
4495                     parent = parent.parentNode;
4496                     val = fly(parent).getStyle(attr);
4497                     if (parent.tagName.toUpperCase() == 'HTML') {
4498                         val = '#fff';
4499                     }
4500                 }
4501             }
4502         } else {
4503             val = superclass.getAttribute.call(this, attr);
4504         }
4505
4506         return val;
4507     };
4508
4509     proto.doMethod = function(attr, start, end) {
4510         var val;
4511
4512         if (this.patterns.color.test(attr)) {
4513             val = [];
4514             for (var i = 0, len = start.length; i < len; ++i) {
4515                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
4516             }
4517
4518             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
4519         }
4520         else {
4521             val = superclass.doMethod.call(this, attr, start, end);
4522         }
4523
4524         return val;
4525     };
4526
4527     proto.setRuntimeAttribute = function(attr) {
4528         superclass.setRuntimeAttribute.call(this, attr);
4529
4530         if (this.patterns.color.test(attr)) {
4531             var attributes = this.attributes;
4532             var start = this.parseColor(this.runtimeAttributes[attr].start);
4533             var end = this.parseColor(this.runtimeAttributes[attr].end);
4534
4535             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
4536                 end = this.parseColor(attributes[attr].by);
4537
4538                 for (var i = 0, len = start.length; i < len; ++i) {
4539                     end[i] = start[i] + end[i];
4540                 }
4541             }
4542
4543             this.runtimeAttributes[attr].start = start;
4544             this.runtimeAttributes[attr].end = end;
4545         }
4546     };
4547 })();
4548
4549 /*
4550  * Portions of this file are based on pieces of Yahoo User Interface Library
4551  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4552  * YUI licensed under the BSD License:
4553  * http://developer.yahoo.net/yui/license.txt
4554  * <script type="text/javascript">
4555  *
4556  */
4557 Roo.lib.Easing = {
4558
4559
4560     easeNone: function (t, b, c, d) {
4561         return c * t / d + b;
4562     },
4563
4564
4565     easeIn: function (t, b, c, d) {
4566         return c * (t /= d) * t + b;
4567     },
4568
4569
4570     easeOut: function (t, b, c, d) {
4571         return -c * (t /= d) * (t - 2) + b;
4572     },
4573
4574
4575     easeBoth: function (t, b, c, d) {
4576         if ((t /= d / 2) < 1) {
4577             return c / 2 * t * t + b;
4578         }
4579
4580         return -c / 2 * ((--t) * (t - 2) - 1) + b;
4581     },
4582
4583
4584     easeInStrong: function (t, b, c, d) {
4585         return c * (t /= d) * t * t * t + b;
4586     },
4587
4588
4589     easeOutStrong: function (t, b, c, d) {
4590         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
4591     },
4592
4593
4594     easeBothStrong: function (t, b, c, d) {
4595         if ((t /= d / 2) < 1) {
4596             return c / 2 * t * t * t * t + b;
4597         }
4598
4599         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
4600     },
4601
4602
4603
4604     elasticIn: function (t, b, c, d, a, p) {
4605         if (t == 0) {
4606             return b;
4607         }
4608         if ((t /= d) == 1) {
4609             return b + c;
4610         }
4611         if (!p) {
4612             p = d * .3;
4613         }
4614
4615         if (!a || a < Math.abs(c)) {
4616             a = c;
4617             var s = p / 4;
4618         }
4619         else {
4620             var s = p / (2 * Math.PI) * Math.asin(c / a);
4621         }
4622
4623         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4624     },
4625
4626
4627     elasticOut: function (t, b, c, d, a, p) {
4628         if (t == 0) {
4629             return b;
4630         }
4631         if ((t /= d) == 1) {
4632             return b + c;
4633         }
4634         if (!p) {
4635             p = d * .3;
4636         }
4637
4638         if (!a || a < Math.abs(c)) {
4639             a = c;
4640             var s = p / 4;
4641         }
4642         else {
4643             var s = p / (2 * Math.PI) * Math.asin(c / a);
4644         }
4645
4646         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
4647     },
4648
4649
4650     elasticBoth: function (t, b, c, d, a, p) {
4651         if (t == 0) {
4652             return b;
4653         }
4654
4655         if ((t /= d / 2) == 2) {
4656             return b + c;
4657         }
4658
4659         if (!p) {
4660             p = d * (.3 * 1.5);
4661         }
4662
4663         if (!a || a < Math.abs(c)) {
4664             a = c;
4665             var s = p / 4;
4666         }
4667         else {
4668             var s = p / (2 * Math.PI) * Math.asin(c / a);
4669         }
4670
4671         if (t < 1) {
4672             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
4673                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4674         }
4675         return a * Math.pow(2, -10 * (t -= 1)) *
4676                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
4677     },
4678
4679
4680
4681     backIn: function (t, b, c, d, s) {
4682         if (typeof s == 'undefined') {
4683             s = 1.70158;
4684         }
4685         return c * (t /= d) * t * ((s + 1) * t - s) + b;
4686     },
4687
4688
4689     backOut: function (t, b, c, d, s) {
4690         if (typeof s == 'undefined') {
4691             s = 1.70158;
4692         }
4693         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
4694     },
4695
4696
4697     backBoth: function (t, b, c, d, s) {
4698         if (typeof s == 'undefined') {
4699             s = 1.70158;
4700         }
4701
4702         if ((t /= d / 2 ) < 1) {
4703             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
4704         }
4705         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
4706     },
4707
4708
4709     bounceIn: function (t, b, c, d) {
4710         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
4711     },
4712
4713
4714     bounceOut: function (t, b, c, d) {
4715         if ((t /= d) < (1 / 2.75)) {
4716             return c * (7.5625 * t * t) + b;
4717         } else if (t < (2 / 2.75)) {
4718             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
4719         } else if (t < (2.5 / 2.75)) {
4720             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
4721         }
4722         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
4723     },
4724
4725
4726     bounceBoth: function (t, b, c, d) {
4727         if (t < d / 2) {
4728             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
4729         }
4730         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
4731     }
4732 };/*
4733  * Portions of this file are based on pieces of Yahoo User Interface Library
4734  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4735  * YUI licensed under the BSD License:
4736  * http://developer.yahoo.net/yui/license.txt
4737  * <script type="text/javascript">
4738  *
4739  */
4740     (function() {
4741         Roo.lib.Motion = function(el, attributes, duration, method) {
4742             if (el) {
4743                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
4744             }
4745         };
4746
4747         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
4748
4749
4750         var Y = Roo.lib;
4751         var superclass = Y.Motion.superclass;
4752         var proto = Y.Motion.prototype;
4753
4754         proto.toString = function() {
4755             var el = this.getEl();
4756             var id = el.id || el.tagName;
4757             return ("Motion " + id);
4758         };
4759
4760         proto.patterns.points = /^points$/i;
4761
4762         proto.setAttribute = function(attr, val, unit) {
4763             if (this.patterns.points.test(attr)) {
4764                 unit = unit || 'px';
4765                 superclass.setAttribute.call(this, 'left', val[0], unit);
4766                 superclass.setAttribute.call(this, 'top', val[1], unit);
4767             } else {
4768                 superclass.setAttribute.call(this, attr, val, unit);
4769             }
4770         };
4771
4772         proto.getAttribute = function(attr) {
4773             if (this.patterns.points.test(attr)) {
4774                 var val = [
4775                         superclass.getAttribute.call(this, 'left'),
4776                         superclass.getAttribute.call(this, 'top')
4777                         ];
4778             } else {
4779                 val = superclass.getAttribute.call(this, attr);
4780             }
4781
4782             return val;
4783         };
4784
4785         proto.doMethod = function(attr, start, end) {
4786             var val = null;
4787
4788             if (this.patterns.points.test(attr)) {
4789                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
4790                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
4791             } else {
4792                 val = superclass.doMethod.call(this, attr, start, end);
4793             }
4794             return val;
4795         };
4796
4797         proto.setRuntimeAttribute = function(attr) {
4798             if (this.patterns.points.test(attr)) {
4799                 var el = this.getEl();
4800                 var attributes = this.attributes;
4801                 var start;
4802                 var control = attributes['points']['control'] || [];
4803                 var end;
4804                 var i, len;
4805
4806                 if (control.length > 0 && !(control[0] instanceof Array)) {
4807                     control = [control];
4808                 } else {
4809                     var tmp = [];
4810                     for (i = 0,len = control.length; i < len; ++i) {
4811                         tmp[i] = control[i];
4812                     }
4813                     control = tmp;
4814                 }
4815
4816                 Roo.fly(el).position();
4817
4818                 if (isset(attributes['points']['from'])) {
4819                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
4820                 }
4821                 else {
4822                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4823                 }
4824
4825                 start = this.getAttribute('points');
4826
4827
4828                 if (isset(attributes['points']['to'])) {
4829                     end = translateValues.call(this, attributes['points']['to'], start);
4830
4831                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4832                     for (i = 0,len = control.length; i < len; ++i) {
4833                         control[i] = translateValues.call(this, control[i], start);
4834                     }
4835
4836
4837                 } else if (isset(attributes['points']['by'])) {
4838                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4839
4840                     for (i = 0,len = control.length; i < len; ++i) {
4841                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4842                     }
4843                 }
4844
4845                 this.runtimeAttributes[attr] = [start];
4846
4847                 if (control.length > 0) {
4848                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4849                 }
4850
4851                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4852             }
4853             else {
4854                 superclass.setRuntimeAttribute.call(this, attr);
4855             }
4856         };
4857
4858         var translateValues = function(val, start) {
4859             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4860             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4861
4862             return val;
4863         };
4864
4865         var isset = function(prop) {
4866             return (typeof prop !== 'undefined');
4867         };
4868     })();
4869 /*
4870  * Portions of this file are based on pieces of Yahoo User Interface Library
4871  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4872  * YUI licensed under the BSD License:
4873  * http://developer.yahoo.net/yui/license.txt
4874  * <script type="text/javascript">
4875  *
4876  */
4877     (function() {
4878         Roo.lib.Scroll = function(el, attributes, duration, method) {
4879             if (el) {
4880                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4881             }
4882         };
4883
4884         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4885
4886
4887         var Y = Roo.lib;
4888         var superclass = Y.Scroll.superclass;
4889         var proto = Y.Scroll.prototype;
4890
4891         proto.toString = function() {
4892             var el = this.getEl();
4893             var id = el.id || el.tagName;
4894             return ("Scroll " + id);
4895         };
4896
4897         proto.doMethod = function(attr, start, end) {
4898             var val = null;
4899
4900             if (attr == 'scroll') {
4901                 val = [
4902                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4903                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4904                         ];
4905
4906             } else {
4907                 val = superclass.doMethod.call(this, attr, start, end);
4908             }
4909             return val;
4910         };
4911
4912         proto.getAttribute = function(attr) {
4913             var val = null;
4914             var el = this.getEl();
4915
4916             if (attr == 'scroll') {
4917                 val = [ el.scrollLeft, el.scrollTop ];
4918             } else {
4919                 val = superclass.getAttribute.call(this, attr);
4920             }
4921
4922             return val;
4923         };
4924
4925         proto.setAttribute = function(attr, val, unit) {
4926             var el = this.getEl();
4927
4928             if (attr == 'scroll') {
4929                 el.scrollLeft = val[0];
4930                 el.scrollTop = val[1];
4931             } else {
4932                 superclass.setAttribute.call(this, attr, val, unit);
4933             }
4934         };
4935     })();
4936 /**
4937  * Originally based of this code... - refactored for Roo...
4938  * https://github.com/aaalsaleh/undo-manager
4939  
4940  * undo-manager.js
4941  * @author  Abdulrahman Alsaleh 
4942  * @copyright 2015 Abdulrahman Alsaleh 
4943  * @license  MIT License (c) 
4944  *
4945  * Hackily modifyed by alan@roojs.com
4946  *
4947  *
4948  *  
4949  *
4950  *  TOTALLY UNTESTED...
4951  *
4952  *  Documentation to be done....
4953  */
4954  
4955
4956 /**
4957 * @class Roo.lib.UndoManager
4958 * An undo manager implementation in JavaScript. It follows the W3C UndoManager and DOM Transaction
4959 * Draft and the undocumented and disabled Mozilla Firefox's UndoManager implementation.
4960
4961  * Usage:
4962  * <pre><code>
4963
4964
4965 editor.undoManager = new Roo.lib.UndoManager(1000, editor);
4966  
4967 </code></pre>
4968
4969 * For more information see this blog post with examples:
4970 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4971      - Create Elements using DOM, HTML fragments and Templates</a>. 
4972 * @constructor
4973 * @param {Number} limit how far back to go ... use 1000?
4974 * @param {Object} scope usually use document..
4975 */
4976
4977 Roo.lib.UndoManager = function (limit, undoScopeHost)
4978 {
4979     this.stack = [];
4980     this.limit = limit;
4981     this.scope = undoScopeHost;
4982     this.fireEvent = typeof CustomEvent != 'undefined' && undoScopeHost && undoScopeHost.dispatchEvent;
4983     if (this.fireEvent) {
4984         this.bindEvents();
4985     }
4986     this.reset();
4987     
4988 };
4989         
4990 Roo.lib.UndoManager.prototype = {
4991     
4992     limit : false,
4993     stack : false,
4994     scope :  false,
4995     fireEvent : false,
4996     position : 0,
4997     length : 0,
4998     
4999     
5000      /**
5001      * To push and execute a transaction, the method undoManager.transact
5002      * must be called by passing a transaction object as the first argument, and a merge
5003      * flag as the second argument. A transaction object has the following properties:
5004      *
5005      * Usage:
5006 <pre><code>
5007 undoManager.transact({
5008     label: 'Typing',
5009     execute: function() { ... },
5010     undo: function() { ... },
5011     // redo same as execute
5012     redo: function() { this.execute(); }
5013 }, false);
5014
5015 // merge transaction
5016 undoManager.transact({
5017     label: 'Typing',
5018     execute: function() { ... },  // this will be run...
5019     undo: function() { ... }, // what to do when undo is run.
5020     // redo same as execute
5021     redo: function() { this.execute(); }
5022 }, true); 
5023 </code></pre> 
5024      *
5025      * 
5026      * @param {Object} transaction The transaction to add to the stack.
5027      * @return {String} The HTML fragment
5028      */
5029     
5030     
5031     transact : function (transaction, merge)
5032     {
5033         if (arguments.length < 2) {
5034             throw new TypeError('Not enough arguments to UndoManager.transact.');
5035         }
5036
5037         transaction.execute();
5038
5039         this.stack.splice(0, this.position);
5040         if (merge && this.length) {
5041             this.stack[0].push(transaction);
5042         } else {
5043             this.stack.unshift([transaction]);
5044         }
5045     
5046         this.position = 0;
5047
5048         if (this.limit && this.stack.length > this.limit) {
5049             this.length = this.stack.length = this.limit;
5050         } else {
5051             this.length = this.stack.length;
5052         }
5053
5054         if (this.fireEvent) {
5055             this.scope.dispatchEvent(
5056                 new CustomEvent('DOMTransaction', {
5057                     detail: {
5058                         transactions: this.stack[0].slice()
5059                     },
5060                     bubbles: true,
5061                     cancelable: false
5062                 })
5063             );
5064         }
5065         
5066         //Roo.log("transaction: pos:" + this.position + " len: " + this.length + " slen:" + this.stack.length);
5067       
5068         
5069     },
5070
5071     undo : function ()
5072     {
5073         //Roo.log("undo: pos:" + this.position + " len: " + this.length + " slen:" + this.stack.length);
5074         
5075         if (this.position < this.length) {
5076             for (var i = this.stack[this.position].length - 1; i >= 0; i--) {
5077                 this.stack[this.position][i].undo();
5078             }
5079             this.position++;
5080
5081             if (this.fireEvent) {
5082                 this.scope.dispatchEvent(
5083                     new CustomEvent('undo', {
5084                         detail: {
5085                             transactions: this.stack[this.position - 1].slice()
5086                         },
5087                         bubbles: true,
5088                         cancelable: false
5089                     })
5090                 );
5091             }
5092         }
5093     },
5094
5095     redo : function ()
5096     {
5097         if (this.position > 0) {
5098             for (var i = 0, n = this.stack[this.position - 1].length; i < n; i++) {
5099                 this.stack[this.position - 1][i].redo();
5100             }
5101             this.position--;
5102
5103             if (this.fireEvent) {
5104                 this.scope.dispatchEvent(
5105                     new CustomEvent('redo', {
5106                         detail: {
5107                             transactions: this.stack[this.position].slice()
5108                         },
5109                         bubbles: true,
5110                         cancelable: false
5111                     })
5112                 );
5113             }
5114         }
5115     },
5116
5117     item : function (index)
5118     {
5119         if (index >= 0 && index < this.length) {
5120             return this.stack[index].slice();
5121         }
5122         return null;
5123     },
5124
5125     clearUndo : function () {
5126         this.stack.length = this.length = this.position;
5127     },
5128
5129     clearRedo : function () {
5130         this.stack.splice(0, this.position);
5131         this.position = 0;
5132         this.length = this.stack.length;
5133     },
5134     /**
5135      * Reset the undo - probaly done on load to clear all history.
5136      */
5137     reset : function()
5138     {
5139         this.stack = [];
5140         this.position = 0;
5141         this.length = 0;
5142         this.current_html = this.scope.innerHTML;
5143         if (this.timer !== false) {
5144             clearTimeout(this.timer);
5145         }
5146         this.timer = false;
5147         this.merge = false;
5148         this.addEvent();
5149         
5150     },
5151     current_html : '',
5152     timer : false,
5153     merge : false,
5154     
5155     
5156     // this will handle the undo/redo on the element.?
5157     bindEvents : function()
5158     {
5159         var el  = this.scope;
5160         el.undoManager = this;
5161         
5162         
5163         this.scope.addEventListener('keydown', function(e) {
5164             if ((e.ctrlKey || e.metaKey) && e.keyCode === 90) {
5165                 if (e.shiftKey) {
5166                     el.undoManager.redo(); // Ctrl/Command + Shift + Z
5167                 } else {
5168                     el.undoManager.undo(); // Ctrl/Command + Z
5169                 }
5170         
5171                 e.preventDefault();
5172             }
5173         });
5174         /// ignore keyup..
5175         this.scope.addEventListener('keyup', function(e) {
5176             if ((e.ctrlKey || e.metaKey) && e.keyCode === 90) {
5177                 e.preventDefault();
5178             }
5179         });
5180         
5181         
5182         
5183         var t = this;
5184         
5185         el.addEventListener('input', function(e) {
5186             if(el.innerHTML == t.current_html) {
5187                 return;
5188             }
5189             // only record events every second.
5190             if (t.timer !== false) {
5191                clearTimeout(t.timer);
5192                t.timer = false;
5193             }
5194             t.timer = setTimeout(function() { t.merge = false; }, 1000);
5195             
5196             t.addEvent(t.merge);
5197             t.merge = true; // ignore changes happening every second..
5198         });
5199         },
5200     /**
5201      * Manually add an event.
5202      * Normall called without arguements - and it will just get added to the stack.
5203      * 
5204      */
5205     
5206     addEvent : function(merge)
5207     {
5208         //Roo.log("undomanager +" + (merge ? 'Y':'n'));
5209         // not sure if this should clear the timer 
5210         merge = typeof(merge) == 'undefined' ? false : merge; 
5211         
5212         this.scope.undoManager.transact({
5213             scope : this.scope,
5214             oldHTML: this.current_html,
5215             newHTML: this.scope.innerHTML,
5216             // nothing to execute (content already changed when input is fired)
5217             execute: function() { },
5218             undo: function() {
5219                 this.scope.innerHTML = this.current_html = this.oldHTML;
5220             },
5221             redo: function() {
5222                 this.scope.innerHTML = this.current_html = this.newHTML;
5223             }
5224         }, false); //merge);
5225         
5226         this.merge = merge;
5227         
5228         this.current_html = this.scope.innerHTML;
5229     }
5230     
5231     
5232      
5233     
5234     
5235     
5236 };
5237 /**
5238  * @class Roo.lib.Range
5239  * @constructor
5240  * This is a toolkit, normally used to copy features into a Dom Range element
5241  * Roo.lib.Range.wrap(x);
5242  *
5243  *
5244  *
5245  */
5246 Roo.lib.Range = function() { };
5247
5248 /**
5249  * Wrap a Dom Range object, to give it new features...
5250  * @static
5251  * @param {Range} the range to wrap
5252  */
5253 Roo.lib.Range.wrap = function(r) {
5254     return Roo.apply(r, Roo.lib.Range.prototype);
5255 };
5256 /**
5257  * find a parent node eg. LI / OL
5258  * @param {string|Array} node name or array of nodenames
5259  * @return {DomElement|false}
5260  */
5261 Roo.apply(Roo.lib.Range.prototype,
5262 {
5263     
5264     closest : function(str)
5265     {
5266         if (typeof(str) != 'string') {
5267             // assume it's a array.
5268             for(var i = 0;i < str.length;i++) {
5269                 var r = this.closest(str[i]);
5270                 if (r !== false) {
5271                     return r;
5272                 }
5273                 
5274             }
5275             return false;
5276         }
5277         str = str.toLowerCase();
5278         var n = this.commonAncestorContainer; // might not be a node
5279         while (n.nodeType != 1) {
5280             n = n.parentNode;
5281         }
5282         
5283         if (n.nodeName.toLowerCase() == str ) {
5284             return n;
5285         }
5286         if (n.nodeName.toLowerCase() == 'body') {
5287             return false;
5288         }
5289             
5290         return n.closest(str) || false;
5291         
5292     },
5293     cloneRange : function()
5294     {
5295         return Roo.lib.Range.wrap(Range.prototype.cloneRange.call(this));
5296     }
5297 });/**
5298  * @class Roo.lib.Selection
5299  * @constructor
5300  * This is a toolkit, normally used to copy features into a Dom Selection element
5301  * Roo.lib.Selection.wrap(x);
5302  *
5303  *
5304  *
5305  */
5306 Roo.lib.Selection = function() { };
5307
5308 /**
5309  * Wrap a Dom Range object, to give it new features...
5310  * @static
5311  * @param {Range} the range to wrap
5312  */
5313 Roo.lib.Selection.wrap = function(r, doc) {
5314     Roo.apply(r, Roo.lib.Selection.prototype);
5315     r.ownerDocument = doc; // usefull so we dont have to keep referening to it.
5316     return r;
5317 };
5318 /**
5319  * find a parent node eg. LI / OL
5320  * @param {string|Array} node name or array of nodenames
5321  * @return {DomElement|false}
5322  */
5323 Roo.apply(Roo.lib.Selection.prototype,
5324 {
5325     /**
5326      * the owner document
5327      */
5328     ownerDocument : false,
5329     
5330     getRangeAt : function(n)
5331     {
5332         return Roo.lib.Range.wrap(Selection.prototype.getRangeAt.call(this,n));
5333     },
5334     
5335     /**
5336      * insert node at selection 
5337      * @param {DomElement|string} node
5338      * @param {string} cursor (after|in|none) where to place the cursor after inserting.
5339      */
5340     insertNode: function(node, cursor)
5341     {
5342         if (typeof(node) == 'string') {
5343             node = this.ownerDocument.createElement(node);
5344             if (cursor == 'in') {
5345                 node.innerHTML = '&nbsp;';
5346             }
5347         }
5348         
5349         var range = this.getRangeAt(0);
5350         
5351         if (this.type != 'Caret') {
5352             range.deleteContents();
5353         }
5354         var sn = node.childNodes[0]; // select the contents.
5355
5356         
5357         
5358         range.insertNode(node);
5359         if (cursor == 'after') {
5360             node.insertAdjacentHTML('afterend', '&nbsp;');
5361             sn = node.nextSibling;
5362         }
5363         
5364         if (cursor == 'none') {
5365             return;
5366         }
5367         
5368         this.cursorText(sn);
5369     },
5370     
5371     cursorText : function(n)
5372     {
5373        
5374         //var range = this.getRangeAt(0);
5375         range = Roo.lib.Range.wrap(new Range());
5376         //range.selectNode(n);
5377         
5378         var ix = Array.from(n.parentNode.childNodes).indexOf(n);
5379         range.setStart(n.parentNode,ix);
5380         range.setEnd(n.parentNode,ix+1);
5381         //range.collapse(false);
5382          
5383         this.removeAllRanges();
5384         this.addRange(range);
5385         
5386         Roo.log([n, range, this,this.baseOffset,this.extentOffset, this.type]);
5387     },
5388     cursorAfter : function(n)
5389     {
5390         if (!n.nextSibling || n.nextSibling.nodeValue != '&nbsp;') {
5391             n.insertAdjacentHTML('afterend', '&nbsp;');
5392         }
5393         this.cursorText (n.nextSibling);
5394     }
5395         
5396     
5397 });/*
5398  * Based on:
5399  * Ext JS Library 1.1.1
5400  * Copyright(c) 2006-2007, Ext JS, LLC.
5401  *
5402  * Originally Released Under LGPL - original licence link has changed is not relivant.
5403  *
5404  * Fork - LGPL
5405  * <script type="text/javascript">
5406  */
5407
5408
5409 // nasty IE9 hack - what a pile of crap that is..
5410
5411  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
5412     Range.prototype.createContextualFragment = function (html) {
5413         var doc = window.document;
5414         var container = doc.createElement("div");
5415         container.innerHTML = html;
5416         var frag = doc.createDocumentFragment(), n;
5417         while ((n = container.firstChild)) {
5418             frag.appendChild(n);
5419         }
5420         return frag;
5421     };
5422 }
5423
5424 /**
5425  * @class Roo.DomHelper
5426  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
5427  * For more information see <a href="http://web.archive.org/web/20071221063734/http://www.jackslocum.com/blog/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
5428  * @static
5429  */
5430 Roo.DomHelper = function(){
5431     var tempTableEl = null;
5432     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
5433     var tableRe = /^table|tbody|tr|td$/i;
5434     var xmlns = {};
5435     // build as innerHTML where available
5436     /** @ignore */
5437     var createHtml = function(o){
5438         if(typeof o == 'string'){
5439             return o;
5440         }
5441         var b = "";
5442         if(!o.tag){
5443             o.tag = "div";
5444         }
5445         b += "<" + o.tag;
5446         for(var attr in o){
5447             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
5448             if(attr == "style"){
5449                 var s = o["style"];
5450                 if(typeof s == "function"){
5451                     s = s.call();
5452                 }
5453                 if(typeof s == "string"){
5454                     b += ' style="' + s + '"';
5455                 }else if(typeof s == "object"){
5456                     b += ' style="';
5457                     for(var key in s){
5458                         if(typeof s[key] != "function"){
5459                             b += key + ":" + s[key] + ";";
5460                         }
5461                     }
5462                     b += '"';
5463                 }
5464             }else{
5465                 if(attr == "cls"){
5466                     b += ' class="' + o["cls"] + '"';
5467                 }else if(attr == "htmlFor"){
5468                     b += ' for="' + o["htmlFor"] + '"';
5469                 }else{
5470                     b += " " + attr + '="' + o[attr] + '"';
5471                 }
5472             }
5473         }
5474         if(emptyTags.test(o.tag)){
5475             b += "/>";
5476         }else{
5477             b += ">";
5478             var cn = o.children || o.cn;
5479             if(cn){
5480                 //http://bugs.kde.org/show_bug.cgi?id=71506
5481                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
5482                     for(var i = 0, len = cn.length; i < len; i++) {
5483                         b += createHtml(cn[i], b);
5484                     }
5485                 }else{
5486                     b += createHtml(cn, b);
5487                 }
5488             }
5489             if(o.html){
5490                 b += o.html;
5491             }
5492             b += "</" + o.tag + ">";
5493         }
5494         return b;
5495     };
5496
5497     // build as dom
5498     /** @ignore */
5499     var createDom = function(o, parentNode){
5500          
5501         // defininition craeted..
5502         var ns = false;
5503         if (o.ns && o.ns != 'html') {
5504                
5505             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
5506                 xmlns[o.ns] = o.xmlns;
5507                 ns = o.xmlns;
5508             }
5509             if (typeof(xmlns[o.ns]) == 'undefined') {
5510                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
5511             }
5512             ns = xmlns[o.ns];
5513         }
5514         
5515         
5516         if (typeof(o) == 'string') {
5517             return parentNode.appendChild(document.createTextNode(o));
5518         }
5519         o.tag = o.tag || 'div';
5520         if (o.ns && Roo.isIE) {
5521             ns = false;
5522             o.tag = o.ns + ':' + o.tag;
5523             
5524         }
5525         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
5526         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
5527         for(var attr in o){
5528             
5529             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
5530                     attr == "style" || typeof o[attr] == "function") { continue; }
5531                     
5532             if(attr=="cls" && Roo.isIE){
5533                 el.className = o["cls"];
5534             }else{
5535                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
5536                 else { 
5537                     el[attr] = o[attr];
5538                 }
5539             }
5540         }
5541         Roo.DomHelper.applyStyles(el, o.style);
5542         var cn = o.children || o.cn;
5543         if(cn){
5544             //http://bugs.kde.org/show_bug.cgi?id=71506
5545              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
5546                 for(var i = 0, len = cn.length; i < len; i++) {
5547                     createDom(cn[i], el);
5548                 }
5549             }else{
5550                 createDom(cn, el);
5551             }
5552         }
5553         if(o.html){
5554             el.innerHTML = o.html;
5555         }
5556         if(parentNode){
5557            parentNode.appendChild(el);
5558         }
5559         return el;
5560     };
5561
5562     var ieTable = function(depth, s, h, e){
5563         tempTableEl.innerHTML = [s, h, e].join('');
5564         var i = -1, el = tempTableEl;
5565         while(++i < depth && el.firstChild){
5566             el = el.firstChild;
5567         }
5568         return el;
5569     };
5570
5571     // kill repeat to save bytes
5572     var ts = '<table>',
5573         te = '</table>',
5574         tbs = ts+'<tbody>',
5575         tbe = '</tbody>'+te,
5576         trs = tbs + '<tr>',
5577         tre = '</tr>'+tbe;
5578
5579     /**
5580      * @ignore
5581      * Nasty code for IE's broken table implementation
5582      */
5583     var insertIntoTable = function(tag, where, el, html){
5584         if(!tempTableEl){
5585             tempTableEl = document.createElement('div');
5586         }
5587         var node;
5588         var before = null;
5589         if(tag == 'td'){
5590             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
5591                 return;
5592             }
5593             if(where == 'beforebegin'){
5594                 before = el;
5595                 el = el.parentNode;
5596             } else{
5597                 before = el.nextSibling;
5598                 el = el.parentNode;
5599             }
5600             node = ieTable(4, trs, html, tre);
5601         }
5602         else if(tag == 'tr'){
5603             if(where == 'beforebegin'){
5604                 before = el;
5605                 el = el.parentNode;
5606                 node = ieTable(3, tbs, html, tbe);
5607             } else if(where == 'afterend'){
5608                 before = el.nextSibling;
5609                 el = el.parentNode;
5610                 node = ieTable(3, tbs, html, tbe);
5611             } else{ // INTO a TR
5612                 if(where == 'afterbegin'){
5613                     before = el.firstChild;
5614                 }
5615                 node = ieTable(4, trs, html, tre);
5616             }
5617         } else if(tag == 'tbody'){
5618             if(where == 'beforebegin'){
5619                 before = el;
5620                 el = el.parentNode;
5621                 node = ieTable(2, ts, html, te);
5622             } else if(where == 'afterend'){
5623                 before = el.nextSibling;
5624                 el = el.parentNode;
5625                 node = ieTable(2, ts, html, te);
5626             } else{
5627                 if(where == 'afterbegin'){
5628                     before = el.firstChild;
5629                 }
5630                 node = ieTable(3, tbs, html, tbe);
5631             }
5632         } else{ // TABLE
5633             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
5634                 return;
5635             }
5636             if(where == 'afterbegin'){
5637                 before = el.firstChild;
5638             }
5639             node = ieTable(2, ts, html, te);
5640         }
5641         el.insertBefore(node, before);
5642         return node;
5643     };
5644     
5645     // this is a bit like the react update code...
5646     // 
5647     
5648     var updateNode = function(from, to)
5649     {
5650         // should we handle non-standard elements?
5651         Roo.log(["UpdateNode" , from, to]);
5652         if (from.nodeType != to.nodeType) {
5653             Roo.log(["ReplaceChild - mismatch notType" , to, from ]);
5654             from.parentNode.replaceChild(to, from);
5655         }
5656         
5657         if (from.nodeType == 3) {
5658             // assume it's text?!
5659             if (from.data == to.data) {
5660                 return;
5661             }
5662             from.data = to.data;
5663             return;
5664         }
5665         if (!from.parentNode) {
5666             // not sure why this is happening?
5667             return;
5668         }
5669         // assume 'to' doesnt have '1/3 nodetypes!
5670         // not sure why, by from, parent node might not exist?
5671         if (from.nodeType !=1 || from.tagName != to.tagName) {
5672             Roo.log(["ReplaceChild" , from, to ]);
5673             
5674             from.parentNode.replaceChild(to, from);
5675             return;
5676         }
5677         // compare attributes
5678         var ar = Array.from(from.attributes);
5679         for(var i = 0; i< ar.length;i++) {
5680             if (to.hasAttribute(ar[i].name)) {
5681                 continue;
5682             }
5683             if (ar[i].name == 'id') { // always keep ids?
5684                continue;
5685             }
5686             //if (ar[i].name == 'style') {
5687             //   throw "style removed?";
5688             //}
5689             Roo.log("removeAttribute" + ar[i].name);
5690             from.removeAttribute(ar[i].name);
5691         }
5692         ar = to.attributes;
5693         for(var i = 0; i< ar.length;i++) {
5694             if (from.getAttribute(ar[i].name) == to.getAttribute(ar[i].name)) {
5695                 Roo.log("skipAttribute " + ar[i].name  + '=' + to.getAttribute(ar[i].name));
5696                 continue;
5697             }
5698             Roo.log("updateAttribute " + ar[i].name + '=>' + to.getAttribute(ar[i].name));
5699             from.setAttribute(ar[i].name, to.getAttribute(ar[i].name));
5700         }
5701         // children
5702         var far = Array.from(from.childNodes);
5703         var tar = Array.from(to.childNodes);
5704         // if the lengths are different.. then it's probably a editable content change, rather than
5705         // a change of the block definition..
5706         
5707         // this did notwork , as our rebuilt nodes did not include ID's so did not match at all.
5708          /*if (from.innerHTML == to.innerHTML) {
5709             return;
5710         }
5711         if (far.length != tar.length) {
5712             from.innerHTML = to.innerHTML;
5713             return;
5714         }
5715         */
5716         
5717         for(var i = 0; i < Math.max(tar.length, far.length); i++) {
5718             if (i >= far.length) {
5719                 from.appendChild(tar[i]);
5720                 Roo.log(["add", tar[i]]);
5721                 
5722             } else if ( i  >= tar.length) {
5723                 from.removeChild(far[i]);
5724                 Roo.log(["remove", far[i]]);
5725             } else {
5726                 
5727                 updateNode(far[i], tar[i]);
5728             }    
5729         }
5730         
5731         
5732         
5733         
5734     };
5735     
5736     
5737
5738     return {
5739         /** True to force the use of DOM instead of html fragments @type Boolean */
5740         useDom : false,
5741     
5742         /**
5743          * Returns the markup for the passed Element(s) config
5744          * @param {Object} o The Dom object spec (and children)
5745          * @return {String}
5746          */
5747         markup : function(o){
5748             return createHtml(o);
5749         },
5750     
5751         /**
5752          * Applies a style specification to an element
5753          * @param {String/HTMLElement} el The element to apply styles to
5754          * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
5755          * a function which returns such a specification.
5756          */
5757         applyStyles : function(el, styles){
5758             if(styles){
5759                el = Roo.fly(el);
5760                if(typeof styles == "string"){
5761                    var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
5762                    var matches;
5763                    while ((matches = re.exec(styles)) != null){
5764                        el.setStyle(matches[1], matches[2]);
5765                    }
5766                }else if (typeof styles == "object"){
5767                    for (var style in styles){
5768                       el.setStyle(style, styles[style]);
5769                    }
5770                }else if (typeof styles == "function"){
5771                     Roo.DomHelper.applyStyles(el, styles.call());
5772                }
5773             }
5774         },
5775     
5776         /**
5777          * Inserts an HTML fragment into the Dom
5778          * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
5779          * @param {HTMLElement} el The context element
5780          * @param {String} html The HTML fragmenet
5781          * @return {HTMLElement} The new node
5782          */
5783         insertHtml : function(where, el, html){
5784             where = where.toLowerCase();
5785             if(el.insertAdjacentHTML){
5786                 if(tableRe.test(el.tagName)){
5787                     var rs;
5788                     if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
5789                         return rs;
5790                     }
5791                 }
5792                 switch(where){
5793                     case "beforebegin":
5794                         el.insertAdjacentHTML('BeforeBegin', html);
5795                         return el.previousSibling;
5796                     case "afterbegin":
5797                         el.insertAdjacentHTML('AfterBegin', html);
5798                         return el.firstChild;
5799                     case "beforeend":
5800                         el.insertAdjacentHTML('BeforeEnd', html);
5801                         return el.lastChild;
5802                     case "afterend":
5803                         el.insertAdjacentHTML('AfterEnd', html);
5804                         return el.nextSibling;
5805                 }
5806                 throw 'Illegal insertion point -> "' + where + '"';
5807             }
5808             var range = el.ownerDocument.createRange();
5809             var frag;
5810             switch(where){
5811                  case "beforebegin":
5812                     range.setStartBefore(el);
5813                     frag = range.createContextualFragment(html);
5814                     el.parentNode.insertBefore(frag, el);
5815                     return el.previousSibling;
5816                  case "afterbegin":
5817                     if(el.firstChild){
5818                         range.setStartBefore(el.firstChild);
5819                         frag = range.createContextualFragment(html);
5820                         el.insertBefore(frag, el.firstChild);
5821                         return el.firstChild;
5822                     }else{
5823                         el.innerHTML = html;
5824                         return el.firstChild;
5825                     }
5826                 case "beforeend":
5827                     if(el.lastChild){
5828                         range.setStartAfter(el.lastChild);
5829                         frag = range.createContextualFragment(html);
5830                         el.appendChild(frag);
5831                         return el.lastChild;
5832                     }else{
5833                         el.innerHTML = html;
5834                         return el.lastChild;
5835                     }
5836                 case "afterend":
5837                     range.setStartAfter(el);
5838                     frag = range.createContextualFragment(html);
5839                     el.parentNode.insertBefore(frag, el.nextSibling);
5840                     return el.nextSibling;
5841                 }
5842                 throw 'Illegal insertion point -> "' + where + '"';
5843         },
5844     
5845         /**
5846          * Creates new Dom element(s) and inserts them before el
5847          * @param {String/HTMLElement/Element} el The context element
5848          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5849          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5850          * @return {HTMLElement/Roo.Element} The new node
5851          */
5852         insertBefore : function(el, o, returnElement){
5853             return this.doInsert(el, o, returnElement, "beforeBegin");
5854         },
5855     
5856         /**
5857          * Creates new Dom element(s) and inserts them after el
5858          * @param {String/HTMLElement/Element} el The context element
5859          * @param {Object} o The Dom object spec (and children)
5860          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5861          * @return {HTMLElement/Roo.Element} The new node
5862          */
5863         insertAfter : function(el, o, returnElement){
5864             return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
5865         },
5866     
5867         /**
5868          * Creates new Dom element(s) and inserts them as the first child of el
5869          * @param {String/HTMLElement/Element} el The context element
5870          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5871          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5872          * @return {HTMLElement/Roo.Element} The new node
5873          */
5874         insertFirst : function(el, o, returnElement){
5875             return this.doInsert(el, o, returnElement, "afterBegin");
5876         },
5877     
5878         // private
5879         doInsert : function(el, o, returnElement, pos, sibling){
5880             el = Roo.getDom(el);
5881             var newNode;
5882             if(this.useDom || o.ns){
5883                 newNode = createDom(o, null);
5884                 el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
5885             }else{
5886                 var html = createHtml(o);
5887                 newNode = this.insertHtml(pos, el, html);
5888             }
5889             return returnElement ? Roo.get(newNode, true) : newNode;
5890         },
5891     
5892         /**
5893          * Creates new Dom element(s) and appends them to el
5894          * @param {String/HTMLElement/Element} el The context element
5895          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5896          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5897          * @return {HTMLElement/Roo.Element} The new node
5898          */
5899         append : function(el, o, returnElement){
5900             el = Roo.getDom(el);
5901             var newNode;
5902             if(this.useDom || o.ns){
5903                 newNode = createDom(o, null);
5904                 el.appendChild(newNode);
5905             }else{
5906                 var html = createHtml(o);
5907                 newNode = this.insertHtml("beforeEnd", el, html);
5908             }
5909             return returnElement ? Roo.get(newNode, true) : newNode;
5910         },
5911     
5912         /**
5913          * Creates new Dom element(s) and overwrites the contents of el with them
5914          * @param {String/HTMLElement/Element} el The context element
5915          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5916          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5917          * @return {HTMLElement/Roo.Element} The new node
5918          */
5919         overwrite : function(el, o, returnElement)
5920         {
5921             el = Roo.getDom(el);
5922             if (o.ns) {
5923               
5924                 while (el.childNodes.length) {
5925                     el.removeChild(el.firstChild);
5926                 }
5927                 createDom(o, el);
5928             } else {
5929                 el.innerHTML = createHtml(o);   
5930             }
5931             
5932             return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
5933         },
5934     
5935         /**
5936          * Creates a new Roo.DomHelper.Template from the Dom object spec
5937          * @param {Object} o The Dom object spec (and children)
5938          * @return {Roo.DomHelper.Template} The new template
5939          */
5940         createTemplate : function(o){
5941             var html = createHtml(o);
5942             return new Roo.Template(html);
5943         },
5944          /**
5945          * Updates the first element with the spec from the o (replacing if necessary)
5946          * This iterates through the children, and updates attributes / children etc..
5947          * @param {String/HTMLElement/Element} el The context element
5948          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5949          */
5950         
5951         update : function(el, o)
5952         {
5953             updateNode(Roo.getDom(el), createDom(o));
5954             
5955         }
5956         
5957         
5958     };
5959 }();
5960 /*
5961  * Based on:
5962  * Ext JS Library 1.1.1
5963  * Copyright(c) 2006-2007, Ext JS, LLC.
5964  *
5965  * Originally Released Under LGPL - original licence link has changed is not relivant.
5966  *
5967  * Fork - LGPL
5968  * <script type="text/javascript">
5969  */
5970  
5971 /**
5972 * @class Roo.Template
5973 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
5974 * For a list of available format functions, see {@link Roo.util.Format}.<br />
5975 * Usage:
5976 <pre><code>
5977 var t = new Roo.Template({
5978     html :  '&lt;div name="{id}"&gt;' + 
5979         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
5980         '&lt;/div&gt;',
5981     myformat: function (value, allValues) {
5982         return 'XX' + value;
5983     }
5984 });
5985 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
5986 </code></pre>
5987 * For more information see this blog post with examples:
5988 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
5989      - Create Elements using DOM, HTML fragments and Templates</a>. 
5990 * @constructor
5991 * @param {Object} cfg - Configuration object.
5992 */
5993 Roo.Template = function(cfg){
5994     // BC!
5995     if(cfg instanceof Array){
5996         cfg = cfg.join("");
5997     }else if(arguments.length > 1){
5998         cfg = Array.prototype.join.call(arguments, "");
5999     }
6000     
6001     
6002     if (typeof(cfg) == 'object') {
6003         Roo.apply(this,cfg)
6004     } else {
6005         // bc
6006         this.html = cfg;
6007     }
6008     if (this.url) {
6009         this.load();
6010     }
6011     
6012 };
6013 Roo.Template.prototype = {
6014     
6015     /**
6016      * @cfg {Function} onLoad Called after the template has been loaded and complied (usually from a remove source)
6017      */
6018     onLoad : false,
6019     
6020     
6021     /**
6022      * @cfg {String} url  The Url to load the template from. beware if you are loading from a url, the data may not be ready if you use it instantly..
6023      *                    it should be fixed so that template is observable...
6024      */
6025     url : false,
6026     /**
6027      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
6028      */
6029     html : '',
6030     
6031     
6032     compiled : false,
6033     loaded : false,
6034     /**
6035      * Returns an HTML fragment of this template with the specified values applied.
6036      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
6037      * @return {String} The HTML fragment
6038      */
6039     
6040    
6041     
6042     applyTemplate : function(values){
6043         //Roo.log(["applyTemplate", values]);
6044         try {
6045            
6046             if(this.compiled){
6047                 return this.compiled(values);
6048             }
6049             var useF = this.disableFormats !== true;
6050             var fm = Roo.util.Format, tpl = this;
6051             var fn = function(m, name, format, args){
6052                 if(format && useF){
6053                     if(format.substr(0, 5) == "this."){
6054                         return tpl.call(format.substr(5), values[name], values);
6055                     }else{
6056                         if(args){
6057                             // quoted values are required for strings in compiled templates, 
6058                             // but for non compiled we need to strip them
6059                             // quoted reversed for jsmin
6060                             var re = /^\s*['"](.*)["']\s*$/;
6061                             args = args.split(',');
6062                             for(var i = 0, len = args.length; i < len; i++){
6063                                 args[i] = args[i].replace(re, "$1");
6064                             }
6065                             args = [values[name]].concat(args);
6066                         }else{
6067                             args = [values[name]];
6068                         }
6069                         return fm[format].apply(fm, args);
6070                     }
6071                 }else{
6072                     return values[name] !== undefined ? values[name] : "";
6073                 }
6074             };
6075             return this.html.replace(this.re, fn);
6076         } catch (e) {
6077             Roo.log(e);
6078             throw e;
6079         }
6080          
6081     },
6082     
6083     loading : false,
6084       
6085     load : function ()
6086     {
6087          
6088         if (this.loading) {
6089             return;
6090         }
6091         var _t = this;
6092         
6093         this.loading = true;
6094         this.compiled = false;
6095         
6096         var cx = new Roo.data.Connection();
6097         cx.request({
6098             url : this.url,
6099             method : 'GET',
6100             success : function (response) {
6101                 _t.loading = false;
6102                 _t.url = false;
6103                 
6104                 _t.set(response.responseText,true);
6105                 _t.loaded = true;
6106                 if (_t.onLoad) {
6107                     _t.onLoad();
6108                 }
6109              },
6110             failure : function(response) {
6111                 Roo.log("Template failed to load from " + _t.url);
6112                 _t.loading = false;
6113             }
6114         });
6115     },
6116
6117     /**
6118      * Sets the HTML used as the template and optionally compiles it.
6119      * @param {String} html
6120      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
6121      * @return {Roo.Template} this
6122      */
6123     set : function(html, compile){
6124         this.html = html;
6125         this.compiled = false;
6126         if(compile){
6127             this.compile();
6128         }
6129         return this;
6130     },
6131     
6132     /**
6133      * True to disable format functions (defaults to false)
6134      * @type Boolean
6135      */
6136     disableFormats : false,
6137     
6138     /**
6139     * The regular expression used to match template variables 
6140     * @type RegExp
6141     * @property 
6142     */
6143     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
6144     
6145     /**
6146      * Compiles the template into an internal function, eliminating the RegEx overhead.
6147      * @return {Roo.Template} this
6148      */
6149     compile : function(){
6150         var fm = Roo.util.Format;
6151         var useF = this.disableFormats !== true;
6152         var sep = Roo.isGecko ? "+" : ",";
6153         var fn = function(m, name, format, args){
6154             if(format && useF){
6155                 args = args ? ',' + args : "";
6156                 if(format.substr(0, 5) != "this."){
6157                     format = "fm." + format + '(';
6158                 }else{
6159                     format = 'this.call("'+ format.substr(5) + '", ';
6160                     args = ", values";
6161                 }
6162             }else{
6163                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
6164             }
6165             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
6166         };
6167         var body;
6168         // branched to use + in gecko and [].join() in others
6169         if(Roo.isGecko){
6170             body = "this.compiled = function(values){ return '" +
6171                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
6172                     "';};";
6173         }else{
6174             body = ["this.compiled = function(values){ return ['"];
6175             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
6176             body.push("'].join('');};");
6177             body = body.join('');
6178         }
6179         /**
6180          * eval:var:values
6181          * eval:var:fm
6182          */
6183         eval(body);
6184         return this;
6185     },
6186     
6187     // private function used to call members
6188     call : function(fnName, value, allValues){
6189         return this[fnName](value, allValues);
6190     },
6191     
6192     /**
6193      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
6194      * @param {String/HTMLElement/Roo.Element} el The context element
6195      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
6196      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6197      * @return {HTMLElement/Roo.Element} The new node or Element
6198      */
6199     insertFirst: function(el, values, returnElement){
6200         return this.doInsert('afterBegin', el, values, returnElement);
6201     },
6202
6203     /**
6204      * Applies the supplied values to the template and inserts the new node(s) before el.
6205      * @param {String/HTMLElement/Roo.Element} el The context element
6206      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
6207      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6208      * @return {HTMLElement/Roo.Element} The new node or Element
6209      */
6210     insertBefore: function(el, values, returnElement){
6211         return this.doInsert('beforeBegin', el, values, returnElement);
6212     },
6213
6214     /**
6215      * Applies the supplied values to the template and inserts the new node(s) after el.
6216      * @param {String/HTMLElement/Roo.Element} el The context element
6217      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
6218      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6219      * @return {HTMLElement/Roo.Element} The new node or Element
6220      */
6221     insertAfter : function(el, values, returnElement){
6222         return this.doInsert('afterEnd', el, values, returnElement);
6223     },
6224     
6225     /**
6226      * Applies the supplied values to the template and appends the new node(s) to el.
6227      * @param {String/HTMLElement/Roo.Element} el The context element
6228      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
6229      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6230      * @return {HTMLElement/Roo.Element} The new node or Element
6231      */
6232     append : function(el, values, returnElement){
6233         return this.doInsert('beforeEnd', el, values, returnElement);
6234     },
6235
6236     doInsert : function(where, el, values, returnEl){
6237         el = Roo.getDom(el);
6238         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
6239         return returnEl ? Roo.get(newNode, true) : newNode;
6240     },
6241
6242     /**
6243      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
6244      * @param {String/HTMLElement/Roo.Element} el The context element
6245      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
6246      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6247      * @return {HTMLElement/Roo.Element} The new node or Element
6248      */
6249     overwrite : function(el, values, returnElement){
6250         el = Roo.getDom(el);
6251         el.innerHTML = this.applyTemplate(values);
6252         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
6253     }
6254 };
6255 /**
6256  * Alias for {@link #applyTemplate}
6257  * @method
6258  */
6259 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
6260
6261 // backwards compat
6262 Roo.DomHelper.Template = Roo.Template;
6263
6264 /**
6265  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
6266  * @param {String/HTMLElement} el A DOM element or its id
6267  * @returns {Roo.Template} The created template
6268  * @static
6269  */
6270 Roo.Template.from = function(el){
6271     el = Roo.getDom(el);
6272     return new Roo.Template(el.value || el.innerHTML);
6273 };/*
6274  * Based on:
6275  * Ext JS Library 1.1.1
6276  * Copyright(c) 2006-2007, Ext JS, LLC.
6277  *
6278  * Originally Released Under LGPL - original licence link has changed is not relivant.
6279  *
6280  * Fork - LGPL
6281  * <script type="text/javascript">
6282  */
6283  
6284
6285 /*
6286  * This is code is also distributed under MIT license for use
6287  * with jQuery and prototype JavaScript libraries.
6288  */
6289 /**
6290  * @class Roo.DomQuery
6291 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
6292 <p>
6293 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
6294
6295 <p>
6296 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
6297 </p>
6298 <h4>Element Selectors:</h4>
6299 <ul class="list">
6300     <li> <b>*</b> any element</li>
6301     <li> <b>E</b> an element with the tag E</li>
6302     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
6303     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
6304     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
6305     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
6306 </ul>
6307 <h4>Attribute Selectors:</h4>
6308 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
6309 <ul class="list">
6310     <li> <b>E[foo]</b> has an attribute "foo"</li>
6311     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
6312     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
6313     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
6314     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
6315     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
6316     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
6317 </ul>
6318 <h4>Pseudo Classes:</h4>
6319 <ul class="list">
6320     <li> <b>E:first-child</b> E is the first child of its parent</li>
6321     <li> <b>E:last-child</b> E is the last child of its parent</li>
6322     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
6323     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
6324     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
6325     <li> <b>E:only-child</b> E is the only child of its parent</li>
6326     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
6327     <li> <b>E:first</b> the first E in the resultset</li>
6328     <li> <b>E:last</b> the last E in the resultset</li>
6329     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
6330     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
6331     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
6332     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
6333     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
6334     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
6335     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
6336     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
6337     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
6338 </ul>
6339 <h4>CSS Value Selectors:</h4>
6340 <ul class="list">
6341     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
6342     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
6343     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
6344     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
6345     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
6346     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
6347 </ul>
6348  * @static
6349  */
6350 Roo.DomQuery = function(){
6351     var cache = {}, simpleCache = {}, valueCache = {};
6352     var nonSpace = /\S/;
6353     var trimRe = /^\s+|\s+$/g;
6354     var tplRe = /\{(\d+)\}/g;
6355     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
6356     var tagTokenRe = /^(#)?([\w-\*]+)/;
6357     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
6358
6359     function child(p, index){
6360         var i = 0;
6361         var n = p.firstChild;
6362         while(n){
6363             if(n.nodeType == 1){
6364                if(++i == index){
6365                    return n;
6366                }
6367             }
6368             n = n.nextSibling;
6369         }
6370         return null;
6371     };
6372
6373     function next(n){
6374         while((n = n.nextSibling) && n.nodeType != 1);
6375         return n;
6376     };
6377
6378     function prev(n){
6379         while((n = n.previousSibling) && n.nodeType != 1);
6380         return n;
6381     };
6382
6383     function children(d){
6384         var n = d.firstChild, ni = -1;
6385             while(n){
6386                 var nx = n.nextSibling;
6387                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
6388                     d.removeChild(n);
6389                 }else{
6390                     n.nodeIndex = ++ni;
6391                 }
6392                 n = nx;
6393             }
6394             return this;
6395         };
6396
6397     function byClassName(c, a, v){
6398         if(!v){
6399             return c;
6400         }
6401         var r = [], ri = -1, cn;
6402         for(var i = 0, ci; ci = c[i]; i++){
6403             
6404             
6405             if((' '+
6406                 ( (ci instanceof SVGElement) ? ci.className.baseVal : ci.className)
6407                  +' ').indexOf(v) != -1){
6408                 r[++ri] = ci;
6409             }
6410         }
6411         return r;
6412     };
6413
6414     function attrValue(n, attr){
6415         if(!n.tagName && typeof n.length != "undefined"){
6416             n = n[0];
6417         }
6418         if(!n){
6419             return null;
6420         }
6421         if(attr == "for"){
6422             return n.htmlFor;
6423         }
6424         if(attr == "class" || attr == "className"){
6425             return (n instanceof SVGElement) ? n.className.baseVal : n.className;
6426         }
6427         return n.getAttribute(attr) || n[attr];
6428
6429     };
6430
6431     function getNodes(ns, mode, tagName){
6432         var result = [], ri = -1, cs;
6433         if(!ns){
6434             return result;
6435         }
6436         tagName = tagName || "*";
6437         if(typeof ns.getElementsByTagName != "undefined"){
6438             ns = [ns];
6439         }
6440         if(!mode){
6441             for(var i = 0, ni; ni = ns[i]; i++){
6442                 cs = ni.getElementsByTagName(tagName);
6443                 for(var j = 0, ci; ci = cs[j]; j++){
6444                     result[++ri] = ci;
6445                 }
6446             }
6447         }else if(mode == "/" || mode == ">"){
6448             var utag = tagName.toUpperCase();
6449             for(var i = 0, ni, cn; ni = ns[i]; i++){
6450                 cn = ni.children || ni.childNodes;
6451                 for(var j = 0, cj; cj = cn[j]; j++){
6452                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
6453                         result[++ri] = cj;
6454                     }
6455                 }
6456             }
6457         }else if(mode == "+"){
6458             var utag = tagName.toUpperCase();
6459             for(var i = 0, n; n = ns[i]; i++){
6460                 while((n = n.nextSibling) && n.nodeType != 1);
6461                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
6462                     result[++ri] = n;
6463                 }
6464             }
6465         }else if(mode == "~"){
6466             for(var i = 0, n; n = ns[i]; i++){
6467                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
6468                 if(n){
6469                     result[++ri] = n;
6470                 }
6471             }
6472         }
6473         return result;
6474     };
6475
6476     function concat(a, b){
6477         if(b.slice){
6478             return a.concat(b);
6479         }
6480         for(var i = 0, l = b.length; i < l; i++){
6481             a[a.length] = b[i];
6482         }
6483         return a;
6484     }
6485
6486     function byTag(cs, tagName){
6487         if(cs.tagName || cs == document){
6488             cs = [cs];
6489         }
6490         if(!tagName){
6491             return cs;
6492         }
6493         var r = [], ri = -1;
6494         tagName = tagName.toLowerCase();
6495         for(var i = 0, ci; ci = cs[i]; i++){
6496             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
6497                 r[++ri] = ci;
6498             }
6499         }
6500         return r;
6501     };
6502
6503     function byId(cs, attr, id){
6504         if(cs.tagName || cs == document){
6505             cs = [cs];
6506         }
6507         if(!id){
6508             return cs;
6509         }
6510         var r = [], ri = -1;
6511         for(var i = 0,ci; ci = cs[i]; i++){
6512             if(ci && ci.id == id){
6513                 r[++ri] = ci;
6514                 return r;
6515             }
6516         }
6517         return r;
6518     };
6519
6520     function byAttribute(cs, attr, value, op, custom){
6521         var r = [], ri = -1, st = custom=="{";
6522         var f = Roo.DomQuery.operators[op];
6523         for(var i = 0, ci; ci = cs[i]; i++){
6524             var a;
6525             if(st){
6526                 a = Roo.DomQuery.getStyle(ci, attr);
6527             }
6528             else if(attr == "class" || attr == "className"){
6529                 a = (ci instanceof SVGElement) ? ci.className.baseVal : ci.className;
6530             }else if(attr == "for"){
6531                 a = ci.htmlFor;
6532             }else if(attr == "href"){
6533                 a = ci.getAttribute("href", 2);
6534             }else{
6535                 a = ci.getAttribute(attr);
6536             }
6537             if((f && f(a, value)) || (!f && a)){
6538                 r[++ri] = ci;
6539             }
6540         }
6541         return r;
6542     };
6543
6544     function byPseudo(cs, name, value){
6545         return Roo.DomQuery.pseudos[name](cs, value);
6546     };
6547
6548     // This is for IE MSXML which does not support expandos.
6549     // IE runs the same speed using setAttribute, however FF slows way down
6550     // and Safari completely fails so they need to continue to use expandos.
6551     var isIE = window.ActiveXObject ? true : false;
6552
6553     // this eval is stop the compressor from
6554     // renaming the variable to something shorter
6555     
6556     /** eval:var:batch */
6557     var batch = 30803; 
6558
6559     var key = 30803;
6560
6561     function nodupIEXml(cs){
6562         var d = ++key;
6563         cs[0].setAttribute("_nodup", d);
6564         var r = [cs[0]];
6565         for(var i = 1, len = cs.length; i < len; i++){
6566             var c = cs[i];
6567             if(!c.getAttribute("_nodup") != d){
6568                 c.setAttribute("_nodup", d);
6569                 r[r.length] = c;
6570             }
6571         }
6572         for(var i = 0, len = cs.length; i < len; i++){
6573             cs[i].removeAttribute("_nodup");
6574         }
6575         return r;
6576     }
6577
6578     function nodup(cs){
6579         if(!cs){
6580             return [];
6581         }
6582         var len = cs.length, c, i, r = cs, cj, ri = -1;
6583         if(!len || typeof cs.nodeType != "undefined" || len == 1){
6584             return cs;
6585         }
6586         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
6587             return nodupIEXml(cs);
6588         }
6589         var d = ++key;
6590         cs[0]._nodup = d;
6591         for(i = 1; c = cs[i]; i++){
6592             if(c._nodup != d){
6593                 c._nodup = d;
6594             }else{
6595                 r = [];
6596                 for(var j = 0; j < i; j++){
6597                     r[++ri] = cs[j];
6598                 }
6599                 for(j = i+1; cj = cs[j]; j++){
6600                     if(cj._nodup != d){
6601                         cj._nodup = d;
6602                         r[++ri] = cj;
6603                     }
6604                 }
6605                 return r;
6606             }
6607         }
6608         return r;
6609     }
6610
6611     function quickDiffIEXml(c1, c2){
6612         var d = ++key;
6613         for(var i = 0, len = c1.length; i < len; i++){
6614             c1[i].setAttribute("_qdiff", d);
6615         }
6616         var r = [];
6617         for(var i = 0, len = c2.length; i < len; i++){
6618             if(c2[i].getAttribute("_qdiff") != d){
6619                 r[r.length] = c2[i];
6620             }
6621         }
6622         for(var i = 0, len = c1.length; i < len; i++){
6623            c1[i].removeAttribute("_qdiff");
6624         }
6625         return r;
6626     }
6627
6628     function quickDiff(c1, c2){
6629         var len1 = c1.length;
6630         if(!len1){
6631             return c2;
6632         }
6633         if(isIE && c1[0].selectSingleNode){
6634             return quickDiffIEXml(c1, c2);
6635         }
6636         var d = ++key;
6637         for(var i = 0; i < len1; i++){
6638             c1[i]._qdiff = d;
6639         }
6640         var r = [];
6641         for(var i = 0, len = c2.length; i < len; i++){
6642             if(c2[i]._qdiff != d){
6643                 r[r.length] = c2[i];
6644             }
6645         }
6646         return r;
6647     }
6648
6649     function quickId(ns, mode, root, id){
6650         if(ns == root){
6651            var d = root.ownerDocument || root;
6652            return d.getElementById(id);
6653         }
6654         ns = getNodes(ns, mode, "*");
6655         return byId(ns, null, id);
6656     }
6657
6658     return {
6659         getStyle : function(el, name){
6660             return Roo.fly(el).getStyle(name);
6661         },
6662         /**
6663          * Compiles a selector/xpath query into a reusable function. The returned function
6664          * takes one parameter "root" (optional), which is the context node from where the query should start.
6665          * @param {String} selector The selector/xpath query
6666          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
6667          * @return {Function}
6668          */
6669         compile : function(path, type){
6670             type = type || "select";
6671             
6672             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
6673             var q = path, mode, lq;
6674             var tk = Roo.DomQuery.matchers;
6675             var tklen = tk.length;
6676             var mm;
6677
6678             // accept leading mode switch
6679             var lmode = q.match(modeRe);
6680             if(lmode && lmode[1]){
6681                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
6682                 q = q.replace(lmode[1], "");
6683             }
6684             // strip leading slashes
6685             while(path.substr(0, 1)=="/"){
6686                 path = path.substr(1);
6687             }
6688
6689             while(q && lq != q){
6690                 lq = q;
6691                 var tm = q.match(tagTokenRe);
6692                 if(type == "select"){
6693                     if(tm){
6694                         if(tm[1] == "#"){
6695                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
6696                         }else{
6697                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
6698                         }
6699                         q = q.replace(tm[0], "");
6700                     }else if(q.substr(0, 1) != '@'){
6701                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
6702                     }
6703                 }else{
6704                     if(tm){
6705                         if(tm[1] == "#"){
6706                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
6707                         }else{
6708                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
6709                         }
6710                         q = q.replace(tm[0], "");
6711                     }
6712                 }
6713                 while(!(mm = q.match(modeRe))){
6714                     var matched = false;
6715                     for(var j = 0; j < tklen; j++){
6716                         var t = tk[j];
6717                         var m = q.match(t.re);
6718                         if(m){
6719                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
6720                                                     return m[i];
6721                                                 });
6722                             q = q.replace(m[0], "");
6723                             matched = true;
6724                             break;
6725                         }
6726                     }
6727                     // prevent infinite loop on bad selector
6728                     if(!matched){
6729                         throw 'Error parsing selector, parsing failed at "' + q + '"';
6730                     }
6731                 }
6732                 if(mm[1]){
6733                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
6734                     q = q.replace(mm[1], "");
6735                 }
6736             }
6737             fn[fn.length] = "return nodup(n);\n}";
6738             
6739              /** 
6740               * list of variables that need from compression as they are used by eval.
6741              *  eval:var:batch 
6742              *  eval:var:nodup
6743              *  eval:var:byTag
6744              *  eval:var:ById
6745              *  eval:var:getNodes
6746              *  eval:var:quickId
6747              *  eval:var:mode
6748              *  eval:var:root
6749              *  eval:var:n
6750              *  eval:var:byClassName
6751              *  eval:var:byPseudo
6752              *  eval:var:byAttribute
6753              *  eval:var:attrValue
6754              * 
6755              **/ 
6756             eval(fn.join(""));
6757             return f;
6758         },
6759
6760         /**
6761          * Selects a group of elements.
6762          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
6763          * @param {Node} root (optional) The start of the query (defaults to document).
6764          * @return {Array}
6765          */
6766         select : function(path, root, type){
6767             if(!root || root == document){
6768                 root = document;
6769             }
6770             if(typeof root == "string"){
6771                 root = document.getElementById(root);
6772             }
6773             var paths = path.split(",");
6774             var results = [];
6775             for(var i = 0, len = paths.length; i < len; i++){
6776                 var p = paths[i].replace(trimRe, "");
6777                 if(!cache[p]){
6778                     cache[p] = Roo.DomQuery.compile(p);
6779                     if(!cache[p]){
6780                         throw p + " is not a valid selector";
6781                     }
6782                 }
6783                 var result = cache[p](root);
6784                 if(result && result != document){
6785                     results = results.concat(result);
6786                 }
6787             }
6788             if(paths.length > 1){
6789                 return nodup(results);
6790             }
6791             return results;
6792         },
6793
6794         /**
6795          * Selects a single element.
6796          * @param {String} selector The selector/xpath query
6797          * @param {Node} root (optional) The start of the query (defaults to document).
6798          * @return {Element}
6799          */
6800         selectNode : function(path, root){
6801             return Roo.DomQuery.select(path, root)[0];
6802         },
6803
6804         /**
6805          * Selects the value of a node, optionally replacing null with the defaultValue.
6806          * @param {String} selector The selector/xpath query
6807          * @param {Node} root (optional) The start of the query (defaults to document).
6808          * @param {String} defaultValue
6809          */
6810         selectValue : function(path, root, defaultValue){
6811             path = path.replace(trimRe, "");
6812             if(!valueCache[path]){
6813                 valueCache[path] = Roo.DomQuery.compile(path, "select");
6814             }
6815             var n = valueCache[path](root);
6816             n = n[0] ? n[0] : n;
6817             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
6818             return ((v === null||v === undefined||v==='') ? defaultValue : v);
6819         },
6820
6821         /**
6822          * Selects the value of a node, parsing integers and floats.
6823          * @param {String} selector The selector/xpath query
6824          * @param {Node} root (optional) The start of the query (defaults to document).
6825          * @param {Number} defaultValue
6826          * @return {Number}
6827          */
6828         selectNumber : function(path, root, defaultValue){
6829             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
6830             return parseFloat(v);
6831         },
6832
6833         /**
6834          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
6835          * @param {String/HTMLElement/Array} el An element id, element or array of elements
6836          * @param {String} selector The simple selector to test
6837          * @return {Boolean}
6838          */
6839         is : function(el, ss){
6840             if(typeof el == "string"){
6841                 el = document.getElementById(el);
6842             }
6843             var isArray = (el instanceof Array);
6844             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
6845             return isArray ? (result.length == el.length) : (result.length > 0);
6846         },
6847
6848         /**
6849          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
6850          * @param {Array} el An array of elements to filter
6851          * @param {String} selector The simple selector to test
6852          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
6853          * the selector instead of the ones that match
6854          * @return {Array}
6855          */
6856         filter : function(els, ss, nonMatches){
6857             ss = ss.replace(trimRe, "");
6858             if(!simpleCache[ss]){
6859                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
6860             }
6861             var result = simpleCache[ss](els);
6862             return nonMatches ? quickDiff(result, els) : result;
6863         },
6864
6865         /**
6866          * Collection of matching regular expressions and code snippets.
6867          */
6868         matchers : [{
6869                 re: /^\.([\w-]+)/,
6870                 select: 'n = byClassName(n, null, " {1} ");'
6871             }, {
6872                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
6873                 select: 'n = byPseudo(n, "{1}", "{2}");'
6874             },{
6875                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
6876                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
6877             }, {
6878                 re: /^#([\w-]+)/,
6879                 select: 'n = byId(n, null, "{1}");'
6880             },{
6881                 re: /^@([\w-]+)/,
6882                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
6883             }
6884         ],
6885
6886         /**
6887          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
6888          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
6889          */
6890         operators : {
6891             "=" : function(a, v){
6892                 return a == v;
6893             },
6894             "!=" : function(a, v){
6895                 return a != v;
6896             },
6897             "^=" : function(a, v){
6898                 return a && a.substr(0, v.length) == v;
6899             },
6900             "$=" : function(a, v){
6901                 return a && a.substr(a.length-v.length) == v;
6902             },
6903             "*=" : function(a, v){
6904                 return a && a.indexOf(v) !== -1;
6905             },
6906             "%=" : function(a, v){
6907                 return (a % v) == 0;
6908             },
6909             "|=" : function(a, v){
6910                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
6911             },
6912             "~=" : function(a, v){
6913                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
6914             }
6915         },
6916
6917         /**
6918          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
6919          * and the argument (if any) supplied in the selector.
6920          */
6921         pseudos : {
6922             "first-child" : function(c){
6923                 var r = [], ri = -1, n;
6924                 for(var i = 0, ci; ci = n = c[i]; i++){
6925                     while((n = n.previousSibling) && n.nodeType != 1);
6926                     if(!n){
6927                         r[++ri] = ci;
6928                     }
6929                 }
6930                 return r;
6931             },
6932
6933             "last-child" : function(c){
6934                 var r = [], ri = -1, n;
6935                 for(var i = 0, ci; ci = n = c[i]; i++){
6936                     while((n = n.nextSibling) && n.nodeType != 1);
6937                     if(!n){
6938                         r[++ri] = ci;
6939                     }
6940                 }
6941                 return r;
6942             },
6943
6944             "nth-child" : function(c, a) {
6945                 var r = [], ri = -1;
6946                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
6947                 var f = (m[1] || 1) - 0, l = m[2] - 0;
6948                 for(var i = 0, n; n = c[i]; i++){
6949                     var pn = n.parentNode;
6950                     if (batch != pn._batch) {
6951                         var j = 0;
6952                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
6953                             if(cn.nodeType == 1){
6954                                cn.nodeIndex = ++j;
6955                             }
6956                         }
6957                         pn._batch = batch;
6958                     }
6959                     if (f == 1) {
6960                         if (l == 0 || n.nodeIndex == l){
6961                             r[++ri] = n;
6962                         }
6963                     } else if ((n.nodeIndex + l) % f == 0){
6964                         r[++ri] = n;
6965                     }
6966                 }
6967
6968                 return r;
6969             },
6970
6971             "only-child" : function(c){
6972                 var r = [], ri = -1;;
6973                 for(var i = 0, ci; ci = c[i]; i++){
6974                     if(!prev(ci) && !next(ci)){
6975                         r[++ri] = ci;
6976                     }
6977                 }
6978                 return r;
6979             },
6980
6981             "empty" : function(c){
6982                 var r = [], ri = -1;
6983                 for(var i = 0, ci; ci = c[i]; i++){
6984                     var cns = ci.childNodes, j = 0, cn, empty = true;
6985                     while(cn = cns[j]){
6986                         ++j;
6987                         if(cn.nodeType == 1 || cn.nodeType == 3){
6988                             empty = false;
6989                             break;
6990                         }
6991                     }
6992                     if(empty){
6993                         r[++ri] = ci;
6994                     }
6995                 }
6996                 return r;
6997             },
6998
6999             "contains" : function(c, v){
7000                 var r = [], ri = -1;
7001                 for(var i = 0, ci; ci = c[i]; i++){
7002                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
7003                         r[++ri] = ci;
7004                     }
7005                 }
7006                 return r;
7007             },
7008
7009             "nodeValue" : function(c, v){
7010                 var r = [], ri = -1;
7011                 for(var i = 0, ci; ci = c[i]; i++){
7012                     if(ci.firstChild && ci.firstChild.nodeValue == v){
7013                         r[++ri] = ci;
7014                     }
7015                 }
7016                 return r;
7017             },
7018
7019             "checked" : function(c){
7020                 var r = [], ri = -1;
7021                 for(var i = 0, ci; ci = c[i]; i++){
7022                     if(ci.checked == true){
7023                         r[++ri] = ci;
7024                     }
7025                 }
7026                 return r;
7027             },
7028
7029             "not" : function(c, ss){
7030                 return Roo.DomQuery.filter(c, ss, true);
7031             },
7032
7033             "odd" : function(c){
7034                 return this["nth-child"](c, "odd");
7035             },
7036
7037             "even" : function(c){
7038                 return this["nth-child"](c, "even");
7039             },
7040
7041             "nth" : function(c, a){
7042                 return c[a-1] || [];
7043             },
7044
7045             "first" : function(c){
7046                 return c[0] || [];
7047             },
7048
7049             "last" : function(c){
7050                 return c[c.length-1] || [];
7051             },
7052
7053             "has" : function(c, ss){
7054                 var s = Roo.DomQuery.select;
7055                 var r = [], ri = -1;
7056                 for(var i = 0, ci; ci = c[i]; i++){
7057                     if(s(ss, ci).length > 0){
7058                         r[++ri] = ci;
7059                     }
7060                 }
7061                 return r;
7062             },
7063
7064             "next" : function(c, ss){
7065                 var is = Roo.DomQuery.is;
7066                 var r = [], ri = -1;
7067                 for(var i = 0, ci; ci = c[i]; i++){
7068                     var n = next(ci);
7069                     if(n && is(n, ss)){
7070                         r[++ri] = ci;
7071                     }
7072                 }
7073                 return r;
7074             },
7075
7076             "prev" : function(c, ss){
7077                 var is = Roo.DomQuery.is;
7078                 var r = [], ri = -1;
7079                 for(var i = 0, ci; ci = c[i]; i++){
7080                     var n = prev(ci);
7081                     if(n && is(n, ss)){
7082                         r[++ri] = ci;
7083                     }
7084                 }
7085                 return r;
7086             }
7087         }
7088     };
7089 }();
7090
7091 /**
7092  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
7093  * @param {String} path The selector/xpath query
7094  * @param {Node} root (optional) The start of the query (defaults to document).
7095  * @return {Array}
7096  * @member Roo
7097  * @method query
7098  */
7099 Roo.query = Roo.DomQuery.select;
7100 /*
7101  * Based on:
7102  * Ext JS Library 1.1.1
7103  * Copyright(c) 2006-2007, Ext JS, LLC.
7104  *
7105  * Originally Released Under LGPL - original licence link has changed is not relivant.
7106  *
7107  * Fork - LGPL
7108  * <script type="text/javascript">
7109  */
7110
7111 /**
7112  * @class Roo.util.Observable
7113  * Base class that provides a common interface for publishing events. Subclasses are expected to
7114  * to have a property "events" with all the events defined.<br>
7115  * For example:
7116  * <pre><code>
7117  Employee = function(name){
7118     this.name = name;
7119     this.addEvents({
7120         "fired" : true,
7121         "quit" : true
7122     });
7123  }
7124  Roo.extend(Employee, Roo.util.Observable);
7125 </code></pre>
7126  * @param {Object} config properties to use (incuding events / listeners)
7127  */
7128
7129 Roo.util.Observable = function(cfg){
7130     
7131     cfg = cfg|| {};
7132     this.addEvents(cfg.events || {});
7133     if (cfg.events) {
7134         delete cfg.events; // make sure
7135     }
7136      
7137     Roo.apply(this, cfg);
7138     
7139     if(this.listeners){
7140         this.on(this.listeners);
7141         delete this.listeners;
7142     }
7143 };
7144 Roo.util.Observable.prototype = {
7145     /** 
7146  * @cfg {Object} listeners  list of events and functions to call for this object, 
7147  * For example :
7148  * <pre><code>
7149     listeners :  { 
7150        'click' : function(e) {
7151            ..... 
7152         } ,
7153         .... 
7154     } 
7155   </code></pre>
7156  */
7157     
7158     
7159     /**
7160      * Fires the specified event with the passed parameters (minus the event name).
7161      * @param {String} eventName
7162      * @param {Object...} args Variable number of parameters are passed to handlers
7163      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
7164      */
7165     fireEvent : function(){
7166         var ce = this.events[arguments[0].toLowerCase()];
7167         if(typeof ce == "object"){
7168             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
7169         }else{
7170             return true;
7171         }
7172     },
7173
7174     // private
7175     filterOptRe : /^(?:scope|delay|buffer|single)$/,
7176
7177     /**
7178      * Appends an event handler to this component
7179      * @param {String}   eventName The type of event to listen for
7180      * @param {Function} handler The method the event invokes
7181      * @param {Object}   scope (optional) The scope in which to execute the handler
7182      * function. The handler function's "this" context.
7183      * @param {Object}   options (optional) An object containing handler configuration
7184      * properties. This may contain any of the following properties:<ul>
7185      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7186      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7187      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7188      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7189      * by the specified number of milliseconds. If the event fires again within that time, the original
7190      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7191      * </ul><br>
7192      * <p>
7193      * <b>Combining Options</b><br>
7194      * Using the options argument, it is possible to combine different types of listeners:<br>
7195      * <br>
7196      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
7197                 <pre><code>
7198                 el.on('click', this.onClick, this, {
7199                         single: true,
7200                 delay: 100,
7201                 forumId: 4
7202                 });
7203                 </code></pre>
7204      * <p>
7205      * <b>Attaching multiple handlers in 1 call</b><br>
7206      * The method also allows for a single argument to be passed which is a config object containing properties
7207      * which specify multiple handlers.
7208      * <pre><code>
7209                 el.on({
7210                         'click': {
7211                         fn: this.onClick,
7212                         scope: this,
7213                         delay: 100
7214                 }, 
7215                 'mouseover': {
7216                         fn: this.onMouseOver,
7217                         scope: this
7218                 },
7219                 'mouseout': {
7220                         fn: this.onMouseOut,
7221                         scope: this
7222                 }
7223                 });
7224                 </code></pre>
7225      * <p>
7226      * Or a shorthand syntax which passes the same scope object to all handlers:
7227         <pre><code>
7228                 el.on({
7229                         'click': this.onClick,
7230                 'mouseover': this.onMouseOver,
7231                 'mouseout': this.onMouseOut,
7232                 scope: this
7233                 });
7234                 </code></pre>
7235      */
7236     addListener : function(eventName, fn, scope, o){
7237         if(typeof eventName == "object"){
7238             o = eventName;
7239             for(var e in o){
7240                 if(this.filterOptRe.test(e)){
7241                     continue;
7242                 }
7243                 if(typeof o[e] == "function"){
7244                     // shared options
7245                     this.addListener(e, o[e], o.scope,  o);
7246                 }else{
7247                     // individual options
7248                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
7249                 }
7250             }
7251             return;
7252         }
7253         o = (!o || typeof o == "boolean") ? {} : o;
7254         eventName = eventName.toLowerCase();
7255         var ce = this.events[eventName] || true;
7256         if(typeof ce == "boolean"){
7257             ce = new Roo.util.Event(this, eventName);
7258             this.events[eventName] = ce;
7259         }
7260         ce.addListener(fn, scope, o);
7261     },
7262
7263     /**
7264      * Removes a listener
7265      * @param {String}   eventName     The type of event to listen for
7266      * @param {Function} handler        The handler to remove
7267      * @param {Object}   scope  (optional) The scope (this object) for the handler
7268      */
7269     removeListener : function(eventName, fn, scope){
7270         var ce = this.events[eventName.toLowerCase()];
7271         if(typeof ce == "object"){
7272             ce.removeListener(fn, scope);
7273         }
7274     },
7275
7276     /**
7277      * Removes all listeners for this object
7278      */
7279     purgeListeners : function(){
7280         for(var evt in this.events){
7281             if(typeof this.events[evt] == "object"){
7282                  this.events[evt].clearListeners();
7283             }
7284         }
7285     },
7286
7287     relayEvents : function(o, events){
7288         var createHandler = function(ename){
7289             return function(){
7290                  
7291                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
7292             };
7293         };
7294         for(var i = 0, len = events.length; i < len; i++){
7295             var ename = events[i];
7296             if(!this.events[ename]){
7297                 this.events[ename] = true;
7298             };
7299             o.on(ename, createHandler(ename), this);
7300         }
7301     },
7302
7303     /**
7304      * Used to define events on this Observable
7305      * @param {Object} object The object with the events defined
7306      */
7307     addEvents : function(o){
7308         if(!this.events){
7309             this.events = {};
7310         }
7311         Roo.applyIf(this.events, o);
7312     },
7313
7314     /**
7315      * Checks to see if this object has any listeners for a specified event
7316      * @param {String} eventName The name of the event to check for
7317      * @return {Boolean} True if the event is being listened for, else false
7318      */
7319     hasListener : function(eventName){
7320         var e = this.events[eventName];
7321         return typeof e == "object" && e.listeners.length > 0;
7322     }
7323 };
7324 /**
7325  * Appends an event handler to this element (shorthand for addListener)
7326  * @param {String}   eventName     The type of event to listen for
7327  * @param {Function} handler        The method the event invokes
7328  * @param {Object}   scope (optional) The scope in which to execute the handler
7329  * function. The handler function's "this" context.
7330  * @param {Object}   options  (optional)
7331  * @method
7332  */
7333 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
7334 /**
7335  * Removes a listener (shorthand for removeListener)
7336  * @param {String}   eventName     The type of event to listen for
7337  * @param {Function} handler        The handler to remove
7338  * @param {Object}   scope  (optional) The scope (this object) for the handler
7339  * @method
7340  */
7341 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
7342
7343 /**
7344  * Starts capture on the specified Observable. All events will be passed
7345  * to the supplied function with the event name + standard signature of the event
7346  * <b>before</b> the event is fired. If the supplied function returns false,
7347  * the event will not fire.
7348  * @param {Observable} o The Observable to capture
7349  * @param {Function} fn The function to call
7350  * @param {Object} scope (optional) The scope (this object) for the fn
7351  * @static
7352  */
7353 Roo.util.Observable.capture = function(o, fn, scope){
7354     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
7355 };
7356
7357 /**
7358  * Removes <b>all</b> added captures from the Observable.
7359  * @param {Observable} o The Observable to release
7360  * @static
7361  */
7362 Roo.util.Observable.releaseCapture = function(o){
7363     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
7364 };
7365
7366 (function(){
7367
7368     var createBuffered = function(h, o, scope){
7369         var task = new Roo.util.DelayedTask();
7370         return function(){
7371             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
7372         };
7373     };
7374
7375     var createSingle = function(h, e, fn, scope){
7376         return function(){
7377             e.removeListener(fn, scope);
7378             return h.apply(scope, arguments);
7379         };
7380     };
7381
7382     var createDelayed = function(h, o, scope){
7383         return function(){
7384             var args = Array.prototype.slice.call(arguments, 0);
7385             setTimeout(function(){
7386                 h.apply(scope, args);
7387             }, o.delay || 10);
7388         };
7389     };
7390
7391     Roo.util.Event = function(obj, name){
7392         this.name = name;
7393         this.obj = obj;
7394         this.listeners = [];
7395     };
7396
7397     Roo.util.Event.prototype = {
7398         addListener : function(fn, scope, options){
7399             var o = options || {};
7400             scope = scope || this.obj;
7401             if(!this.isListening(fn, scope)){
7402                 var l = {fn: fn, scope: scope, options: o};
7403                 var h = fn;
7404                 if(o.delay){
7405                     h = createDelayed(h, o, scope);
7406                 }
7407                 if(o.single){
7408                     h = createSingle(h, this, fn, scope);
7409                 }
7410                 if(o.buffer){
7411                     h = createBuffered(h, o, scope);
7412                 }
7413                 l.fireFn = h;
7414                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
7415                     this.listeners.push(l);
7416                 }else{
7417                     this.listeners = this.listeners.slice(0);
7418                     this.listeners.push(l);
7419                 }
7420             }
7421         },
7422
7423         findListener : function(fn, scope){
7424             scope = scope || this.obj;
7425             var ls = this.listeners;
7426             for(var i = 0, len = ls.length; i < len; i++){
7427                 var l = ls[i];
7428                 if(l.fn == fn && l.scope == scope){
7429                     return i;
7430                 }
7431             }
7432             return -1;
7433         },
7434
7435         isListening : function(fn, scope){
7436             return this.findListener(fn, scope) != -1;
7437         },
7438
7439         removeListener : function(fn, scope){
7440             var index;
7441             if((index = this.findListener(fn, scope)) != -1){
7442                 if(!this.firing){
7443                     this.listeners.splice(index, 1);
7444                 }else{
7445                     this.listeners = this.listeners.slice(0);
7446                     this.listeners.splice(index, 1);
7447                 }
7448                 return true;
7449             }
7450             return false;
7451         },
7452
7453         clearListeners : function(){
7454             this.listeners = [];
7455         },
7456
7457         fire : function(){
7458             var ls = this.listeners, scope, len = ls.length;
7459             if(len > 0){
7460                 this.firing = true;
7461                 var args = Array.prototype.slice.call(arguments, 0);                
7462                 for(var i = 0; i < len; i++){
7463                     var l = ls[i];
7464                     if(l.fireFn.apply(l.scope||this.obj||window, args) === false){
7465                         this.firing = false;
7466                         return false;
7467                     }
7468                 }
7469                 this.firing = false;
7470             }
7471             return true;
7472         }
7473     };
7474 })();/*
7475  * RooJS Library 
7476  * Copyright(c) 2007-2017, Roo J Solutions Ltd
7477  *
7478  * Licence LGPL 
7479  *
7480  */
7481  
7482 /**
7483  * @class Roo.Document
7484  * @extends Roo.util.Observable
7485  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
7486  * 
7487  * @param {Object} config the methods and properties of the 'base' class for the application.
7488  * 
7489  *  Generic Page handler - implement this to start your app..
7490  * 
7491  * eg.
7492  *  MyProject = new Roo.Document({
7493         events : {
7494             'load' : true // your events..
7495         },
7496         listeners : {
7497             'ready' : function() {
7498                 // fired on Roo.onReady()
7499             }
7500         }
7501  * 
7502  */
7503 Roo.Document = function(cfg) {
7504      
7505     this.addEvents({ 
7506         'ready' : true
7507     });
7508     Roo.util.Observable.call(this,cfg);
7509     
7510     var _this = this;
7511     
7512     Roo.onReady(function() {
7513         _this.fireEvent('ready');
7514     },null,false);
7515     
7516     
7517 }
7518
7519 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
7520  * Based on:
7521  * Ext JS Library 1.1.1
7522  * Copyright(c) 2006-2007, Ext JS, LLC.
7523  *
7524  * Originally Released Under LGPL - original licence link has changed is not relivant.
7525  *
7526  * Fork - LGPL
7527  * <script type="text/javascript">
7528  */
7529
7530 /**
7531  * @class Roo.EventManager
7532  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
7533  * several useful events directly.
7534  * See {@link Roo.EventObject} for more details on normalized event objects.
7535  * @static
7536  */
7537 Roo.EventManager = function(){
7538     var docReadyEvent, docReadyProcId, docReadyState = false;
7539     var resizeEvent, resizeTask, textEvent, textSize;
7540     var E = Roo.lib.Event;
7541     var D = Roo.lib.Dom;
7542
7543     
7544     
7545
7546     var fireDocReady = function(){
7547         if(!docReadyState){
7548             docReadyState = true;
7549             Roo.isReady = true;
7550             if(docReadyProcId){
7551                 clearInterval(docReadyProcId);
7552             }
7553             if(Roo.isGecko || Roo.isOpera) {
7554                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
7555             }
7556             if(Roo.isIE){
7557                 var defer = document.getElementById("ie-deferred-loader");
7558                 if(defer){
7559                     defer.onreadystatechange = null;
7560                     defer.parentNode.removeChild(defer);
7561                 }
7562             }
7563             if(docReadyEvent){
7564                 docReadyEvent.fire();
7565                 docReadyEvent.clearListeners();
7566             }
7567         }
7568     };
7569     
7570     var initDocReady = function(){
7571         docReadyEvent = new Roo.util.Event();
7572         if(Roo.isGecko || Roo.isOpera) {
7573             document.addEventListener("DOMContentLoaded", fireDocReady, false);
7574         }else if(Roo.isIE){
7575             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
7576             var defer = document.getElementById("ie-deferred-loader");
7577             defer.onreadystatechange = function(){
7578                 if(this.readyState == "complete"){
7579                     fireDocReady();
7580                 }
7581             };
7582         }else if(Roo.isSafari){ 
7583             docReadyProcId = setInterval(function(){
7584                 var rs = document.readyState;
7585                 if(rs == "complete") {
7586                     fireDocReady();     
7587                  }
7588             }, 10);
7589         }
7590         // no matter what, make sure it fires on load
7591         E.on(window, "load", fireDocReady);
7592     };
7593
7594     var createBuffered = function(h, o){
7595         var task = new Roo.util.DelayedTask(h);
7596         return function(e){
7597             // create new event object impl so new events don't wipe out properties
7598             e = new Roo.EventObjectImpl(e);
7599             task.delay(o.buffer, h, null, [e]);
7600         };
7601     };
7602
7603     var createSingle = function(h, el, ename, fn){
7604         return function(e){
7605             Roo.EventManager.removeListener(el, ename, fn);
7606             h(e);
7607         };
7608     };
7609
7610     var createDelayed = function(h, o){
7611         return function(e){
7612             // create new event object impl so new events don't wipe out properties
7613             e = new Roo.EventObjectImpl(e);
7614             setTimeout(function(){
7615                 h(e);
7616             }, o.delay || 10);
7617         };
7618     };
7619     var transitionEndVal = false;
7620     
7621     var transitionEnd = function()
7622     {
7623         if (transitionEndVal) {
7624             return transitionEndVal;
7625         }
7626         var el = document.createElement('div');
7627
7628         var transEndEventNames = {
7629             WebkitTransition : 'webkitTransitionEnd',
7630             MozTransition    : 'transitionend',
7631             OTransition      : 'oTransitionEnd otransitionend',
7632             transition       : 'transitionend'
7633         };
7634     
7635         for (var name in transEndEventNames) {
7636             if (el.style[name] !== undefined) {
7637                 transitionEndVal = transEndEventNames[name];
7638                 return  transitionEndVal ;
7639             }
7640         }
7641     }
7642     
7643   
7644
7645     var listen = function(element, ename, opt, fn, scope)
7646     {
7647         var o = (!opt || typeof opt == "boolean") ? {} : opt;
7648         fn = fn || o.fn; scope = scope || o.scope;
7649         var el = Roo.getDom(element);
7650         
7651         
7652         if(!el){
7653             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
7654         }
7655         
7656         if (ename == 'transitionend') {
7657             ename = transitionEnd();
7658         }
7659         var h = function(e){
7660             e = Roo.EventObject.setEvent(e);
7661             var t;
7662             if(o.delegate){
7663                 t = e.getTarget(o.delegate, el);
7664                 if(!t){
7665                     return;
7666                 }
7667             }else{
7668                 t = e.target;
7669             }
7670             if(o.stopEvent === true){
7671                 e.stopEvent();
7672             }
7673             if(o.preventDefault === true){
7674                e.preventDefault();
7675             }
7676             if(o.stopPropagation === true){
7677                 e.stopPropagation();
7678             }
7679
7680             if(o.normalized === false){
7681                 e = e.browserEvent;
7682             }
7683
7684             fn.call(scope || el, e, t, o);
7685         };
7686         if(o.delay){
7687             h = createDelayed(h, o);
7688         }
7689         if(o.single){
7690             h = createSingle(h, el, ename, fn);
7691         }
7692         if(o.buffer){
7693             h = createBuffered(h, o);
7694         }
7695         
7696         fn._handlers = fn._handlers || [];
7697         
7698         
7699         fn._handlers.push([Roo.id(el), ename, h]);
7700         
7701         
7702          
7703         E.on(el, ename, h); // this adds the actuall listener to the object..
7704         
7705         
7706         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
7707             el.addEventListener("DOMMouseScroll", h, false);
7708             E.on(window, 'unload', function(){
7709                 el.removeEventListener("DOMMouseScroll", h, false);
7710             });
7711         }
7712         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7713             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
7714         }
7715         return h;
7716     };
7717
7718     var stopListening = function(el, ename, fn){
7719         var id = Roo.id(el), hds = fn._handlers, hd = fn;
7720         if(hds){
7721             for(var i = 0, len = hds.length; i < len; i++){
7722                 var h = hds[i];
7723                 if(h[0] == id && h[1] == ename){
7724                     hd = h[2];
7725                     hds.splice(i, 1);
7726                     break;
7727                 }
7728             }
7729         }
7730         E.un(el, ename, hd);
7731         el = Roo.getDom(el);
7732         if(ename == "mousewheel" && el.addEventListener){
7733             el.removeEventListener("DOMMouseScroll", hd, false);
7734         }
7735         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7736             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
7737         }
7738     };
7739
7740     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
7741     
7742     var pub = {
7743         
7744         
7745         /** 
7746          * Fix for doc tools
7747          * @scope Roo.EventManager
7748          */
7749         
7750         
7751         /** 
7752          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
7753          * object with a Roo.EventObject
7754          * @param {Function} fn        The method the event invokes
7755          * @param {Object}   scope    An object that becomes the scope of the handler
7756          * @param {boolean}  override If true, the obj passed in becomes
7757          *                             the execution scope of the listener
7758          * @return {Function} The wrapped function
7759          * @deprecated
7760          */
7761         wrap : function(fn, scope, override){
7762             return function(e){
7763                 Roo.EventObject.setEvent(e);
7764                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
7765             };
7766         },
7767         
7768         /**
7769      * Appends an event handler to an element (shorthand for addListener)
7770      * @param {String/HTMLElement}   element        The html element or id to assign the
7771      * @param {String}   eventName The type of event to listen for
7772      * @param {Function} handler The method the event invokes
7773      * @param {Object}   scope (optional) The scope in which to execute the handler
7774      * function. The handler function's "this" context.
7775      * @param {Object}   options (optional) An object containing handler configuration
7776      * properties. This may contain any of the following properties:<ul>
7777      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7778      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7779      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7780      * <li>preventDefault {Boolean} True to prevent the default action</li>
7781      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7782      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7783      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7784      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7785      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7786      * by the specified number of milliseconds. If the event fires again within that time, the original
7787      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7788      * </ul><br>
7789      * <p>
7790      * <b>Combining Options</b><br>
7791      * Using the options argument, it is possible to combine different types of listeners:<br>
7792      * <br>
7793      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7794      * Code:<pre><code>
7795 el.on('click', this.onClick, this, {
7796     single: true,
7797     delay: 100,
7798     stopEvent : true,
7799     forumId: 4
7800 });</code></pre>
7801      * <p>
7802      * <b>Attaching multiple handlers in 1 call</b><br>
7803       * The method also allows for a single argument to be passed which is a config object containing properties
7804      * which specify multiple handlers.
7805      * <p>
7806      * Code:<pre><code>
7807 el.on({
7808     'click' : {
7809         fn: this.onClick
7810         scope: this,
7811         delay: 100
7812     },
7813     'mouseover' : {
7814         fn: this.onMouseOver
7815         scope: this
7816     },
7817     'mouseout' : {
7818         fn: this.onMouseOut
7819         scope: this
7820     }
7821 });</code></pre>
7822      * <p>
7823      * Or a shorthand syntax:<br>
7824      * Code:<pre><code>
7825 el.on({
7826     'click' : this.onClick,
7827     'mouseover' : this.onMouseOver,
7828     'mouseout' : this.onMouseOut
7829     scope: this
7830 });</code></pre>
7831      */
7832         addListener : function(element, eventName, fn, scope, options){
7833             if(typeof eventName == "object"){
7834                 var o = eventName;
7835                 for(var e in o){
7836                     if(propRe.test(e)){
7837                         continue;
7838                     }
7839                     if(typeof o[e] == "function"){
7840                         // shared options
7841                         listen(element, e, o, o[e], o.scope);
7842                     }else{
7843                         // individual options
7844                         listen(element, e, o[e]);
7845                     }
7846                 }
7847                 return;
7848             }
7849             return listen(element, eventName, options, fn, scope);
7850         },
7851         
7852         /**
7853          * Removes an event handler
7854          *
7855          * @param {String/HTMLElement}   element        The id or html element to remove the 
7856          *                             event from
7857          * @param {String}   eventName     The type of event
7858          * @param {Function} fn
7859          * @return {Boolean} True if a listener was actually removed
7860          */
7861         removeListener : function(element, eventName, fn){
7862             return stopListening(element, eventName, fn);
7863         },
7864         
7865         /**
7866          * Fires when the document is ready (before onload and before images are loaded). Can be 
7867          * accessed shorthanded Roo.onReady().
7868          * @param {Function} fn        The method the event invokes
7869          * @param {Object}   scope    An  object that becomes the scope of the handler
7870          * @param {boolean}  options
7871          */
7872         onDocumentReady : function(fn, scope, options){
7873             if(docReadyState){ // if it already fired
7874                 docReadyEvent.addListener(fn, scope, options);
7875                 docReadyEvent.fire();
7876                 docReadyEvent.clearListeners();
7877                 return;
7878             }
7879             if(!docReadyEvent){
7880                 initDocReady();
7881             }
7882             docReadyEvent.addListener(fn, scope, options);
7883         },
7884         
7885         /**
7886          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
7887          * @param {Function} fn        The method the event invokes
7888          * @param {Object}   scope    An object that becomes the scope of the handler
7889          * @param {boolean}  options
7890          */
7891         onWindowResize : function(fn, scope, options)
7892         {
7893             if(!resizeEvent){
7894                 resizeEvent = new Roo.util.Event();
7895                 resizeTask = new Roo.util.DelayedTask(function(){
7896                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7897                 });
7898                 E.on(window, "resize", function()
7899                 {
7900                     if (Roo.isIE) {
7901                         resizeTask.delay(50);
7902                     } else {
7903                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7904                     }
7905                 });
7906             }
7907             resizeEvent.addListener(fn, scope, options);
7908         },
7909
7910         /**
7911          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
7912          * @param {Function} fn        The method the event invokes
7913          * @param {Object}   scope    An object that becomes the scope of the handler
7914          * @param {boolean}  options
7915          */
7916         onTextResize : function(fn, scope, options){
7917             if(!textEvent){
7918                 textEvent = new Roo.util.Event();
7919                 var textEl = new Roo.Element(document.createElement('div'));
7920                 textEl.dom.className = 'x-text-resize';
7921                 textEl.dom.innerHTML = 'X';
7922                 textEl.appendTo(document.body);
7923                 textSize = textEl.dom.offsetHeight;
7924                 setInterval(function(){
7925                     if(textEl.dom.offsetHeight != textSize){
7926                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
7927                     }
7928                 }, this.textResizeInterval);
7929             }
7930             textEvent.addListener(fn, scope, options);
7931         },
7932
7933         /**
7934          * Removes the passed window resize listener.
7935          * @param {Function} fn        The method the event invokes
7936          * @param {Object}   scope    The scope of handler
7937          */
7938         removeResizeListener : function(fn, scope){
7939             if(resizeEvent){
7940                 resizeEvent.removeListener(fn, scope);
7941             }
7942         },
7943
7944         // private
7945         fireResize : function(){
7946             if(resizeEvent){
7947                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7948             }   
7949         },
7950         /**
7951          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
7952          */
7953         ieDeferSrc : false,
7954         /**
7955          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
7956          */
7957         textResizeInterval : 50
7958     };
7959     
7960     /**
7961      * Fix for doc tools
7962      * @scopeAlias pub=Roo.EventManager
7963      */
7964     
7965      /**
7966      * Appends an event handler to an element (shorthand for addListener)
7967      * @param {String/HTMLElement}   element        The html element or id to assign the
7968      * @param {String}   eventName The type of event to listen for
7969      * @param {Function} handler The method the event invokes
7970      * @param {Object}   scope (optional) The scope in which to execute the handler
7971      * function. The handler function's "this" context.
7972      * @param {Object}   options (optional) An object containing handler configuration
7973      * properties. This may contain any of the following properties:<ul>
7974      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7975      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7976      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7977      * <li>preventDefault {Boolean} True to prevent the default action</li>
7978      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7979      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7980      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7981      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7982      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7983      * by the specified number of milliseconds. If the event fires again within that time, the original
7984      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7985      * </ul><br>
7986      * <p>
7987      * <b>Combining Options</b><br>
7988      * Using the options argument, it is possible to combine different types of listeners:<br>
7989      * <br>
7990      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7991      * Code:<pre><code>
7992 el.on('click', this.onClick, this, {
7993     single: true,
7994     delay: 100,
7995     stopEvent : true,
7996     forumId: 4
7997 });</code></pre>
7998      * <p>
7999      * <b>Attaching multiple handlers in 1 call</b><br>
8000       * The method also allows for a single argument to be passed which is a config object containing properties
8001      * which specify multiple handlers.
8002      * <p>
8003      * Code:<pre><code>
8004 el.on({
8005     'click' : {
8006         fn: this.onClick
8007         scope: this,
8008         delay: 100
8009     },
8010     'mouseover' : {
8011         fn: this.onMouseOver
8012         scope: this
8013     },
8014     'mouseout' : {
8015         fn: this.onMouseOut
8016         scope: this
8017     }
8018 });</code></pre>
8019      * <p>
8020      * Or a shorthand syntax:<br>
8021      * Code:<pre><code>
8022 el.on({
8023     'click' : this.onClick,
8024     'mouseover' : this.onMouseOver,
8025     'mouseout' : this.onMouseOut
8026     scope: this
8027 });</code></pre>
8028      */
8029     pub.on = pub.addListener;
8030     pub.un = pub.removeListener;
8031
8032     pub.stoppedMouseDownEvent = new Roo.util.Event();
8033     return pub;
8034 }();
8035 /**
8036   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
8037   * @param {Function} fn        The method the event invokes
8038   * @param {Object}   scope    An  object that becomes the scope of the handler
8039   * @param {boolean}  override If true, the obj passed in becomes
8040   *                             the execution scope of the listener
8041   * @member Roo
8042   * @method onReady
8043  */
8044 Roo.onReady = Roo.EventManager.onDocumentReady;
8045
8046 Roo.onReady(function(){
8047     var bd = Roo.get(document.body);
8048     if(!bd){ return; }
8049
8050     var cls = [
8051             Roo.isIE ? "roo-ie"
8052             : Roo.isIE11 ? "roo-ie11"
8053             : Roo.isEdge ? "roo-edge"
8054             : Roo.isGecko ? "roo-gecko"
8055             : Roo.isOpera ? "roo-opera"
8056             : Roo.isSafari ? "roo-safari" : ""];
8057
8058     if(Roo.isMac){
8059         cls.push("roo-mac");
8060     }
8061     if(Roo.isLinux){
8062         cls.push("roo-linux");
8063     }
8064     if(Roo.isIOS){
8065         cls.push("roo-ios");
8066     }
8067     if(Roo.isTouch){
8068         cls.push("roo-touch");
8069     }
8070     if(Roo.isBorderBox){
8071         cls.push('roo-border-box');
8072     }
8073     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
8074         var p = bd.dom.parentNode;
8075         if(p){
8076             p.className += ' roo-strict';
8077         }
8078     }
8079     bd.addClass(cls.join(' '));
8080 });
8081
8082 /**
8083  * @class Roo.EventObject
8084  * EventObject exposes the Yahoo! UI Event functionality directly on the object
8085  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
8086  * Example:
8087  * <pre><code>
8088  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
8089     e.preventDefault();
8090     var target = e.getTarget();
8091     ...
8092  }
8093  var myDiv = Roo.get("myDiv");
8094  myDiv.on("click", handleClick);
8095  //or
8096  Roo.EventManager.on("myDiv", 'click', handleClick);
8097  Roo.EventManager.addListener("myDiv", 'click', handleClick);
8098  </code></pre>
8099  * @static
8100  */
8101 Roo.EventObject = function(){
8102     
8103     var E = Roo.lib.Event;
8104     
8105     // safari keypress events for special keys return bad keycodes
8106     var safariKeys = {
8107         63234 : 37, // left
8108         63235 : 39, // right
8109         63232 : 38, // up
8110         63233 : 40, // down
8111         63276 : 33, // page up
8112         63277 : 34, // page down
8113         63272 : 46, // delete
8114         63273 : 36, // home
8115         63275 : 35  // end
8116     };
8117
8118     // normalize button clicks
8119     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
8120                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
8121
8122     Roo.EventObjectImpl = function(e){
8123         if(e){
8124             this.setEvent(e.browserEvent || e);
8125         }
8126     };
8127     Roo.EventObjectImpl.prototype = {
8128         /**
8129          * Used to fix doc tools.
8130          * @scope Roo.EventObject.prototype
8131          */
8132             
8133
8134         
8135         
8136         /** The normal browser event */
8137         browserEvent : null,
8138         /** The button pressed in a mouse event */
8139         button : -1,
8140         /** True if the shift key was down during the event */
8141         shiftKey : false,
8142         /** True if the control key was down during the event */
8143         ctrlKey : false,
8144         /** True if the alt key was down during the event */
8145         altKey : false,
8146
8147         /** Key constant 
8148         * @type Number */
8149         BACKSPACE : 8,
8150         /** Key constant 
8151         * @type Number */
8152         TAB : 9,
8153         /** Key constant 
8154         * @type Number */
8155         RETURN : 13,
8156         /** Key constant 
8157         * @type Number */
8158         ENTER : 13,
8159         /** Key constant 
8160         * @type Number */
8161         SHIFT : 16,
8162         /** Key constant 
8163         * @type Number */
8164         CONTROL : 17,
8165         /** Key constant 
8166         * @type Number */
8167         ESC : 27,
8168         /** Key constant 
8169         * @type Number */
8170         SPACE : 32,
8171         /** Key constant 
8172         * @type Number */
8173         PAGEUP : 33,
8174         /** Key constant 
8175         * @type Number */
8176         PAGEDOWN : 34,
8177         /** Key constant 
8178         * @type Number */
8179         END : 35,
8180         /** Key constant 
8181         * @type Number */
8182         HOME : 36,
8183         /** Key constant 
8184         * @type Number */
8185         LEFT : 37,
8186         /** Key constant 
8187         * @type Number */
8188         UP : 38,
8189         /** Key constant 
8190         * @type Number */
8191         RIGHT : 39,
8192         /** Key constant 
8193         * @type Number */
8194         DOWN : 40,
8195         /** Key constant 
8196         * @type Number */
8197         DELETE : 46,
8198         /** Key constant 
8199         * @type Number */
8200         F5 : 116,
8201
8202            /** @private */
8203         setEvent : function(e){
8204             if(e == this || (e && e.browserEvent)){ // already wrapped
8205                 return e;
8206             }
8207             this.browserEvent = e;
8208             if(e){
8209                 // normalize buttons
8210                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
8211                 if(e.type == 'click' && this.button == -1){
8212                     this.button = 0;
8213                 }
8214                 this.type = e.type;
8215                 this.shiftKey = e.shiftKey;
8216                 // mac metaKey behaves like ctrlKey
8217                 this.ctrlKey = e.ctrlKey || e.metaKey;
8218                 this.altKey = e.altKey;
8219                 // in getKey these will be normalized for the mac
8220                 this.keyCode = e.keyCode;
8221                 // keyup warnings on firefox.
8222                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
8223                 // cache the target for the delayed and or buffered events
8224                 this.target = E.getTarget(e);
8225                 // same for XY
8226                 this.xy = E.getXY(e);
8227             }else{
8228                 this.button = -1;
8229                 this.shiftKey = false;
8230                 this.ctrlKey = false;
8231                 this.altKey = false;
8232                 this.keyCode = 0;
8233                 this.charCode =0;
8234                 this.target = null;
8235                 this.xy = [0, 0];
8236             }
8237             return this;
8238         },
8239
8240         /**
8241          * Stop the event (preventDefault and stopPropagation)
8242          */
8243         stopEvent : function(){
8244             if(this.browserEvent){
8245                 if(this.browserEvent.type == 'mousedown'){
8246                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
8247                 }
8248                 E.stopEvent(this.browserEvent);
8249             }
8250         },
8251
8252         /**
8253          * Prevents the browsers default handling of the event.
8254          */
8255         preventDefault : function(){
8256             if(this.browserEvent){
8257                 E.preventDefault(this.browserEvent);
8258             }
8259         },
8260
8261         /** @private */
8262         isNavKeyPress : function(){
8263             var k = this.keyCode;
8264             k = Roo.isSafari ? (safariKeys[k] || k) : k;
8265             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
8266         },
8267
8268         isSpecialKey : function(){
8269             var k = this.keyCode;
8270             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
8271             (k == 16) || (k == 17) ||
8272             (k >= 18 && k <= 20) ||
8273             (k >= 33 && k <= 35) ||
8274             (k >= 36 && k <= 39) ||
8275             (k >= 44 && k <= 45);
8276         },
8277         /**
8278          * Cancels bubbling of the event.
8279          */
8280         stopPropagation : function(){
8281             if(this.browserEvent){
8282                 if(this.type == 'mousedown'){
8283                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
8284                 }
8285                 E.stopPropagation(this.browserEvent);
8286             }
8287         },
8288
8289         /**
8290          * Gets the key code for the event.
8291          * @return {Number}
8292          */
8293         getCharCode : function(){
8294             return this.charCode || this.keyCode;
8295         },
8296
8297         /**
8298          * Returns a normalized keyCode for the event.
8299          * @return {Number} The key code
8300          */
8301         getKey : function(){
8302             var k = this.keyCode || this.charCode;
8303             return Roo.isSafari ? (safariKeys[k] || k) : k;
8304         },
8305
8306         /**
8307          * Gets the x coordinate of the event.
8308          * @return {Number}
8309          */
8310         getPageX : function(){
8311             return this.xy[0];
8312         },
8313
8314         /**
8315          * Gets the y coordinate of the event.
8316          * @return {Number}
8317          */
8318         getPageY : function(){
8319             return this.xy[1];
8320         },
8321
8322         /**
8323          * Gets the time of the event.
8324          * @return {Number}
8325          */
8326         getTime : function(){
8327             if(this.browserEvent){
8328                 return E.getTime(this.browserEvent);
8329             }
8330             return null;
8331         },
8332
8333         /**
8334          * Gets the page coordinates of the event.
8335          * @return {Array} The xy values like [x, y]
8336          */
8337         getXY : function(){
8338             return this.xy;
8339         },
8340
8341         /**
8342          * Gets the target for the event.
8343          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
8344          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8345                 search as a number or element (defaults to 10 || document.body)
8346          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8347          * @return {HTMLelement}
8348          */
8349         getTarget : function(selector, maxDepth, returnEl){
8350             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
8351         },
8352         /**
8353          * Gets the related target.
8354          * @return {HTMLElement}
8355          */
8356         getRelatedTarget : function(){
8357             if(this.browserEvent){
8358                 return E.getRelatedTarget(this.browserEvent);
8359             }
8360             return null;
8361         },
8362
8363         /**
8364          * Normalizes mouse wheel delta across browsers
8365          * @return {Number} The delta
8366          */
8367         getWheelDelta : function(){
8368             var e = this.browserEvent;
8369             var delta = 0;
8370             if(e.wheelDelta){ /* IE/Opera. */
8371                 delta = e.wheelDelta/120;
8372             }else if(e.detail){ /* Mozilla case. */
8373                 delta = -e.detail/3;
8374             }
8375             return delta;
8376         },
8377
8378         /**
8379          * Returns true if the control, meta, shift or alt key was pressed during this event.
8380          * @return {Boolean}
8381          */
8382         hasModifier : function(){
8383             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
8384         },
8385
8386         /**
8387          * Returns true if the target of this event equals el or is a child of el
8388          * @param {String/HTMLElement/Element} el
8389          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
8390          * @return {Boolean}
8391          */
8392         within : function(el, related){
8393             var t = this[related ? "getRelatedTarget" : "getTarget"]();
8394             return t && Roo.fly(el).contains(t);
8395         },
8396
8397         getPoint : function(){
8398             return new Roo.lib.Point(this.xy[0], this.xy[1]);
8399         }
8400     };
8401
8402     return new Roo.EventObjectImpl();
8403 }();
8404             
8405     /*
8406  * Based on:
8407  * Ext JS Library 1.1.1
8408  * Copyright(c) 2006-2007, Ext JS, LLC.
8409  *
8410  * Originally Released Under LGPL - original licence link has changed is not relivant.
8411  *
8412  * Fork - LGPL
8413  * <script type="text/javascript">
8414  */
8415
8416  
8417 // was in Composite Element!??!?!
8418  
8419 (function(){
8420     var D = Roo.lib.Dom;
8421     var E = Roo.lib.Event;
8422     var A = Roo.lib.Anim;
8423
8424     // local style camelizing for speed
8425     var propCache = {};
8426     var camelRe = /(-[a-z])/gi;
8427     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
8428     var view = document.defaultView;
8429
8430 /**
8431  * @class Roo.Element
8432  * Represents an Element in the DOM.<br><br>
8433  * Usage:<br>
8434 <pre><code>
8435 var el = Roo.get("my-div");
8436
8437 // or with getEl
8438 var el = getEl("my-div");
8439
8440 // or with a DOM element
8441 var el = Roo.get(myDivElement);
8442 </code></pre>
8443  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
8444  * each call instead of constructing a new one.<br><br>
8445  * <b>Animations</b><br />
8446  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
8447  * should either be a boolean (true) or an object literal with animation options. The animation options are:
8448 <pre>
8449 Option    Default   Description
8450 --------- --------  ---------------------------------------------
8451 duration  .35       The duration of the animation in seconds
8452 easing    easeOut   The YUI easing method
8453 callback  none      A function to execute when the anim completes
8454 scope     this      The scope (this) of the callback function
8455 </pre>
8456 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
8457 * manipulate the animation. Here's an example:
8458 <pre><code>
8459 var el = Roo.get("my-div");
8460
8461 // no animation
8462 el.setWidth(100);
8463
8464 // default animation
8465 el.setWidth(100, true);
8466
8467 // animation with some options set
8468 el.setWidth(100, {
8469     duration: 1,
8470     callback: this.foo,
8471     scope: this
8472 });
8473
8474 // using the "anim" property to get the Anim object
8475 var opt = {
8476     duration: 1,
8477     callback: this.foo,
8478     scope: this
8479 };
8480 el.setWidth(100, opt);
8481 ...
8482 if(opt.anim.isAnimated()){
8483     opt.anim.stop();
8484 }
8485 </code></pre>
8486 * <b> Composite (Collections of) Elements</b><br />
8487  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
8488  * @constructor Create a new Element directly.
8489  * @param {String/HTMLElement} element
8490  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
8491  */
8492     Roo.Element = function(element, forceNew)
8493     {
8494         var dom = typeof element == "string" ?
8495                 document.getElementById(element) : element;
8496         
8497         this.listeners = {};
8498         
8499         if(!dom){ // invalid id/element
8500             return null;
8501         }
8502         var id = dom.id;
8503         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
8504             return Roo.Element.cache[id];
8505         }
8506
8507         /**
8508          * The DOM element
8509          * @type HTMLElement
8510          */
8511         this.dom = dom;
8512
8513         /**
8514          * The DOM element ID
8515          * @type String
8516          */
8517         this.id = id || Roo.id(dom);
8518         
8519         return this; // assumed for cctor?
8520     };
8521
8522     var El = Roo.Element;
8523
8524     El.prototype = {
8525         /**
8526          * The element's default display mode  (defaults to "") 
8527          * @type String
8528          */
8529         originalDisplay : "",
8530
8531         
8532         // note this is overridden in BS version..
8533         visibilityMode : 1, 
8534         /**
8535          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
8536          * @type String
8537          */
8538         defaultUnit : "px",
8539         
8540         /**
8541          * Sets the element's visibility mode. When setVisible() is called it
8542          * will use this to determine whether to set the visibility or the display property.
8543          * @param visMode Element.VISIBILITY or Element.DISPLAY
8544          * @return {Roo.Element} this
8545          */
8546         setVisibilityMode : function(visMode){
8547             this.visibilityMode = visMode;
8548             return this;
8549         },
8550         /**
8551          * Convenience method for setVisibilityMode(Element.DISPLAY)
8552          * @param {String} display (optional) What to set display to when visible
8553          * @return {Roo.Element} this
8554          */
8555         enableDisplayMode : function(display){
8556             this.setVisibilityMode(El.DISPLAY);
8557             if(typeof display != "undefined") { this.originalDisplay = display; }
8558             return this;
8559         },
8560
8561         /**
8562          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
8563          * @param {String} selector The simple selector to test
8564          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8565                 search as a number or element (defaults to 10 || document.body)
8566          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8567          * @return {HTMLElement} The matching DOM node (or null if no match was found)
8568          */
8569         findParent : function(simpleSelector, maxDepth, returnEl){
8570             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
8571             maxDepth = maxDepth || 50;
8572             if(typeof maxDepth != "number"){
8573                 stopEl = Roo.getDom(maxDepth);
8574                 maxDepth = 10;
8575             }
8576             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
8577                 if(dq.is(p, simpleSelector)){
8578                     return returnEl ? Roo.get(p) : p;
8579                 }
8580                 depth++;
8581                 p = p.parentNode;
8582             }
8583             return null;
8584         },
8585
8586
8587         /**
8588          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
8589          * @param {String} selector The simple selector to test
8590          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8591                 search as a number or element (defaults to 10 || document.body)
8592          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8593          * @return {HTMLElement} The matching DOM node (or null if no match was found)
8594          */
8595         findParentNode : function(simpleSelector, maxDepth, returnEl){
8596             var p = Roo.fly(this.dom.parentNode, '_internal');
8597             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
8598         },
8599         
8600         /**
8601          * Looks at  the scrollable parent element
8602          */
8603         findScrollableParent : function()
8604         {
8605             var overflowRegex = /(auto|scroll)/;
8606             
8607             if(this.getStyle('position') === 'fixed'){
8608                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8609             }
8610             
8611             var excludeStaticParent = this.getStyle('position') === "absolute";
8612             
8613             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
8614                 
8615                 if (excludeStaticParent && parent.getStyle('position') === "static") {
8616                     continue;
8617                 }
8618                 
8619                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
8620                     return parent;
8621                 }
8622                 
8623                 if(parent.dom.nodeName.toLowerCase() == 'body'){
8624                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8625                 }
8626             }
8627             
8628             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8629         },
8630
8631         /**
8632          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
8633          * This is a shortcut for findParentNode() that always returns an Roo.Element.
8634          * @param {String} selector The simple selector to test
8635          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8636                 search as a number or element (defaults to 10 || document.body)
8637          * @return {Roo.Element} The matching DOM node (or null if no match was found)
8638          */
8639         up : function(simpleSelector, maxDepth){
8640             return this.findParentNode(simpleSelector, maxDepth, true);
8641         },
8642
8643
8644
8645         /**
8646          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
8647          * @param {String} selector The simple selector to test
8648          * @return {Boolean} True if this element matches the selector, else false
8649          */
8650         is : function(simpleSelector){
8651             return Roo.DomQuery.is(this.dom, simpleSelector);
8652         },
8653
8654         /**
8655          * Perform animation on this element.
8656          * @param {Object} args The YUI animation control args
8657          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
8658          * @param {Function} onComplete (optional) Function to call when animation completes
8659          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
8660          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
8661          * @return {Roo.Element} this
8662          */
8663         animate : function(args, duration, onComplete, easing, animType){
8664             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
8665             return this;
8666         },
8667
8668         /*
8669          * @private Internal animation call
8670          */
8671         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
8672             animType = animType || 'run';
8673             opt = opt || {};
8674             var anim = Roo.lib.Anim[animType](
8675                 this.dom, args,
8676                 (opt.duration || defaultDur) || .35,
8677                 (opt.easing || defaultEase) || 'easeOut',
8678                 function(){
8679                     Roo.callback(cb, this);
8680                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
8681                 },
8682                 this
8683             );
8684             opt.anim = anim;
8685             return anim;
8686         },
8687
8688         // private legacy anim prep
8689         preanim : function(a, i){
8690             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
8691         },
8692
8693         /**
8694          * Removes worthless text nodes
8695          * @param {Boolean} forceReclean (optional) By default the element
8696          * keeps track if it has been cleaned already so
8697          * you can call this over and over. However, if you update the element and
8698          * need to force a reclean, you can pass true.
8699          */
8700         clean : function(forceReclean){
8701             if(this.isCleaned && forceReclean !== true){
8702                 return this;
8703             }
8704             var ns = /\S/;
8705             var d = this.dom, n = d.firstChild, ni = -1;
8706             while(n){
8707                 var nx = n.nextSibling;
8708                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
8709                     d.removeChild(n);
8710                 }else{
8711                     n.nodeIndex = ++ni;
8712                 }
8713                 n = nx;
8714             }
8715             this.isCleaned = true;
8716             return this;
8717         },
8718
8719         // private
8720         calcOffsetsTo : function(el){
8721             el = Roo.get(el);
8722             var d = el.dom;
8723             var restorePos = false;
8724             if(el.getStyle('position') == 'static'){
8725                 el.position('relative');
8726                 restorePos = true;
8727             }
8728             var x = 0, y =0;
8729             var op = this.dom;
8730             while(op && op != d && op.tagName != 'HTML'){
8731                 x+= op.offsetLeft;
8732                 y+= op.offsetTop;
8733                 op = op.offsetParent;
8734             }
8735             if(restorePos){
8736                 el.position('static');
8737             }
8738             return [x, y];
8739         },
8740
8741         /**
8742          * Scrolls this element into view within the passed container.
8743          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
8744          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
8745          * @return {Roo.Element} this
8746          */
8747         scrollIntoView : function(container, hscroll){
8748             var c = Roo.getDom(container) || document.body;
8749             var el = this.dom;
8750
8751             var o = this.calcOffsetsTo(c),
8752                 l = o[0],
8753                 t = o[1],
8754                 b = t+el.offsetHeight,
8755                 r = l+el.offsetWidth;
8756
8757             var ch = c.clientHeight;
8758             var ct = parseInt(c.scrollTop, 10);
8759             var cl = parseInt(c.scrollLeft, 10);
8760             var cb = ct + ch;
8761             var cr = cl + c.clientWidth;
8762
8763             if(t < ct){
8764                 c.scrollTop = t;
8765             }else if(b > cb){
8766                 c.scrollTop = b-ch;
8767             }
8768
8769             if(hscroll !== false){
8770                 if(l < cl){
8771                     c.scrollLeft = l;
8772                 }else if(r > cr){
8773                     c.scrollLeft = r-c.clientWidth;
8774                 }
8775             }
8776             return this;
8777         },
8778
8779         // private
8780         scrollChildIntoView : function(child, hscroll){
8781             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
8782         },
8783
8784         /**
8785          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
8786          * the new height may not be available immediately.
8787          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
8788          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
8789          * @param {Function} onComplete (optional) Function to call when animation completes
8790          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
8791          * @return {Roo.Element} this
8792          */
8793         autoHeight : function(animate, duration, onComplete, easing){
8794             var oldHeight = this.getHeight();
8795             this.clip();
8796             this.setHeight(1); // force clipping
8797             setTimeout(function(){
8798                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
8799                 if(!animate){
8800                     this.setHeight(height);
8801                     this.unclip();
8802                     if(typeof onComplete == "function"){
8803                         onComplete();
8804                     }
8805                 }else{
8806                     this.setHeight(oldHeight); // restore original height
8807                     this.setHeight(height, animate, duration, function(){
8808                         this.unclip();
8809                         if(typeof onComplete == "function") { onComplete(); }
8810                     }.createDelegate(this), easing);
8811                 }
8812             }.createDelegate(this), 0);
8813             return this;
8814         },
8815
8816         /**
8817          * Returns true if this element is an ancestor of the passed element
8818          * @param {HTMLElement/String} el The element to check
8819          * @return {Boolean} True if this element is an ancestor of el, else false
8820          */
8821         contains : function(el){
8822             if(!el){return false;}
8823             return D.isAncestor(this.dom, el.dom ? el.dom : el);
8824         },
8825
8826         /**
8827          * Checks whether the element is currently visible using both visibility and display properties.
8828          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
8829          * @return {Boolean} True if the element is currently visible, else false
8830          */
8831         isVisible : function(deep) {
8832             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
8833             if(deep !== true || !vis){
8834                 return vis;
8835             }
8836             var p = this.dom.parentNode;
8837             while(p && p.tagName.toLowerCase() != "body"){
8838                 if(!Roo.fly(p, '_isVisible').isVisible()){
8839                     return false;
8840                 }
8841                 p = p.parentNode;
8842             }
8843             return true;
8844         },
8845
8846         /**
8847          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
8848          * @param {String} selector The CSS selector
8849          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
8850          * @return {CompositeElement/CompositeElementLite} The composite element
8851          */
8852         select : function(selector, unique){
8853             return El.select(selector, unique, this.dom);
8854         },
8855
8856         /**
8857          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
8858          * @param {String} selector The CSS selector
8859          * @return {Array} An array of the matched nodes
8860          */
8861         query : function(selector, unique){
8862             return Roo.DomQuery.select(selector, this.dom);
8863         },
8864
8865         /**
8866          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
8867          * @param {String} selector The CSS selector
8868          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8869          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8870          */
8871         child : function(selector, returnDom){
8872             var n = Roo.DomQuery.selectNode(selector, this.dom);
8873             return returnDom ? n : Roo.get(n);
8874         },
8875
8876         /**
8877          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
8878          * @param {String} selector The CSS selector
8879          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8880          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8881          */
8882         down : function(selector, returnDom){
8883             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
8884             return returnDom ? n : Roo.get(n);
8885         },
8886
8887         /**
8888          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
8889          * @param {String} group The group the DD object is member of
8890          * @param {Object} config The DD config object
8891          * @param {Object} overrides An object containing methods to override/implement on the DD object
8892          * @return {Roo.dd.DD} The DD object
8893          */
8894         initDD : function(group, config, overrides){
8895             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
8896             return Roo.apply(dd, overrides);
8897         },
8898
8899         /**
8900          * Initializes a {@link Roo.dd.DDProxy} object for this element.
8901          * @param {String} group The group the DDProxy object is member of
8902          * @param {Object} config The DDProxy config object
8903          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
8904          * @return {Roo.dd.DDProxy} The DDProxy object
8905          */
8906         initDDProxy : function(group, config, overrides){
8907             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
8908             return Roo.apply(dd, overrides);
8909         },
8910
8911         /**
8912          * Initializes a {@link Roo.dd.DDTarget} object for this element.
8913          * @param {String} group The group the DDTarget object is member of
8914          * @param {Object} config The DDTarget config object
8915          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
8916          * @return {Roo.dd.DDTarget} The DDTarget object
8917          */
8918         initDDTarget : function(group, config, overrides){
8919             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
8920             return Roo.apply(dd, overrides);
8921         },
8922
8923         /**
8924          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
8925          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
8926          * @param {Boolean} visible Whether the element is visible
8927          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8928          * @return {Roo.Element} this
8929          */
8930          setVisible : function(visible, animate){
8931             if(!animate || !A){
8932                 if(this.visibilityMode == El.DISPLAY){
8933                     this.setDisplayed(visible);
8934                 }else{
8935                     this.fixDisplay();
8936                     this.dom.style.visibility = visible ? "visible" : "hidden";
8937                 }
8938             }else{
8939                 // closure for composites
8940                 var dom = this.dom;
8941                 var visMode = this.visibilityMode;
8942                 if(visible){
8943                     this.setOpacity(.01);
8944                     this.setVisible(true);
8945                 }
8946                 this.anim({opacity: { to: (visible?1:0) }},
8947                       this.preanim(arguments, 1),
8948                       null, .35, 'easeIn', function(){
8949                          if(!visible){
8950                              if(visMode == El.DISPLAY){
8951                                  dom.style.display = "none";
8952                              }else{
8953                                  dom.style.visibility = "hidden";
8954                              }
8955                              Roo.get(dom).setOpacity(1);
8956                          }
8957                      });
8958             }
8959             return this;
8960         },
8961
8962         /**
8963          * Returns true if display is not "none"
8964          * @return {Boolean}
8965          */
8966         isDisplayed : function() {
8967             return this.getStyle("display") != "none";
8968         },
8969
8970         /**
8971          * Toggles the element's visibility or display, depending on visibility mode.
8972          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8973          * @return {Roo.Element} this
8974          */
8975         toggle : function(animate){
8976             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
8977             return this;
8978         },
8979
8980         /**
8981          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
8982          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
8983          * @return {Roo.Element} this
8984          */
8985         setDisplayed : function(value) {
8986             if(typeof value == "boolean"){
8987                value = value ? this.originalDisplay : "none";
8988             }
8989             this.setStyle("display", value);
8990             return this;
8991         },
8992
8993         /**
8994          * Tries to focus the element. Any exceptions are caught and ignored.
8995          * @return {Roo.Element} this
8996          */
8997         focus : function() {
8998             try{
8999                 this.dom.focus();
9000             }catch(e){}
9001             return this;
9002         },
9003
9004         /**
9005          * Tries to blur the element. Any exceptions are caught and ignored.
9006          * @return {Roo.Element} this
9007          */
9008         blur : function() {
9009             try{
9010                 this.dom.blur();
9011             }catch(e){}
9012             return this;
9013         },
9014
9015         /**
9016          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
9017          * @param {String/Array} className The CSS class to add, or an array of classes
9018          * @return {Roo.Element} this
9019          */
9020         addClass : function(className){
9021             if(className instanceof Array){
9022                 for(var i = 0, len = className.length; i < len; i++) {
9023                     this.addClass(className[i]);
9024                 }
9025             }else{
9026                 if(className && !this.hasClass(className)){
9027                     if (this.dom instanceof SVGElement) {
9028                         this.dom.className.baseVal =this.dom.className.baseVal  + " " + className;
9029                     } else {
9030                         this.dom.className = this.dom.className + " " + className;
9031                     }
9032                 }
9033             }
9034             return this;
9035         },
9036
9037         /**
9038          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
9039          * @param {String/Array} className The CSS class to add, or an array of classes
9040          * @return {Roo.Element} this
9041          */
9042         radioClass : function(className){
9043             var siblings = this.dom.parentNode.childNodes;
9044             for(var i = 0; i < siblings.length; i++) {
9045                 var s = siblings[i];
9046                 if(s.nodeType == 1){
9047                     Roo.get(s).removeClass(className);
9048                 }
9049             }
9050             this.addClass(className);
9051             return this;
9052         },
9053
9054         /**
9055          * Removes one or more CSS classes from the element.
9056          * @param {String/Array} className The CSS class to remove, or an array of classes
9057          * @return {Roo.Element} this
9058          */
9059         removeClass : function(className){
9060             
9061             var cn = this.dom instanceof SVGElement ? this.dom.className.baseVal : this.dom.className;
9062             if(!className || !cn){
9063                 return this;
9064             }
9065             if(className instanceof Array){
9066                 for(var i = 0, len = className.length; i < len; i++) {
9067                     this.removeClass(className[i]);
9068                 }
9069             }else{
9070                 if(this.hasClass(className)){
9071                     var re = this.classReCache[className];
9072                     if (!re) {
9073                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
9074                        this.classReCache[className] = re;
9075                     }
9076                     if (this.dom instanceof SVGElement) {
9077                         this.dom.className.baseVal = cn.replace(re, " ");
9078                     } else {
9079                         this.dom.className = cn.replace(re, " ");
9080                     }
9081                 }
9082             }
9083             return this;
9084         },
9085
9086         // private
9087         classReCache: {},
9088
9089         /**
9090          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
9091          * @param {String} className The CSS class to toggle
9092          * @return {Roo.Element} this
9093          */
9094         toggleClass : function(className){
9095             if(this.hasClass(className)){
9096                 this.removeClass(className);
9097             }else{
9098                 this.addClass(className);
9099             }
9100             return this;
9101         },
9102
9103         /**
9104          * Checks if the specified CSS class exists on this element's DOM node.
9105          * @param {String} className The CSS class to check for
9106          * @return {Boolean} True if the class exists, else false
9107          */
9108         hasClass : function(className){
9109             if (this.dom instanceof SVGElement) {
9110                 return className && (' '+this.dom.className.baseVal +' ').indexOf(' '+className+' ') != -1; 
9111             } 
9112             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
9113         },
9114
9115         /**
9116          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
9117          * @param {String} oldClassName The CSS class to replace
9118          * @param {String} newClassName The replacement CSS class
9119          * @return {Roo.Element} this
9120          */
9121         replaceClass : function(oldClassName, newClassName){
9122             this.removeClass(oldClassName);
9123             this.addClass(newClassName);
9124             return this;
9125         },
9126
9127         /**
9128          * Returns an object with properties matching the styles requested.
9129          * For example, el.getStyles('color', 'font-size', 'width') might return
9130          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
9131          * @param {String} style1 A style name
9132          * @param {String} style2 A style name
9133          * @param {String} etc.
9134          * @return {Object} The style object
9135          */
9136         getStyles : function(){
9137             var a = arguments, len = a.length, r = {};
9138             for(var i = 0; i < len; i++){
9139                 r[a[i]] = this.getStyle(a[i]);
9140             }
9141             return r;
9142         },
9143
9144         /**
9145          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
9146          * @param {String} property The style property whose value is returned.
9147          * @return {String} The current value of the style property for this element.
9148          */
9149         getStyle : function(){
9150             return view && view.getComputedStyle ?
9151                 function(prop){
9152                     var el = this.dom, v, cs, camel;
9153                     if(prop == 'float'){
9154                         prop = "cssFloat";
9155                     }
9156                     if(el.style && (v = el.style[prop])){
9157                         return v;
9158                     }
9159                     if(cs = view.getComputedStyle(el, "")){
9160                         if(!(camel = propCache[prop])){
9161                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
9162                         }
9163                         return cs[camel];
9164                     }
9165                     return null;
9166                 } :
9167                 function(prop){
9168                     var el = this.dom, v, cs, camel;
9169                     if(prop == 'opacity'){
9170                         if(typeof el.style.filter == 'string'){
9171                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
9172                             if(m){
9173                                 var fv = parseFloat(m[1]);
9174                                 if(!isNaN(fv)){
9175                                     return fv ? fv / 100 : 0;
9176                                 }
9177                             }
9178                         }
9179                         return 1;
9180                     }else if(prop == 'float'){
9181                         prop = "styleFloat";
9182                     }
9183                     if(!(camel = propCache[prop])){
9184                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
9185                     }
9186                     if(v = el.style[camel]){
9187                         return v;
9188                     }
9189                     if(cs = el.currentStyle){
9190                         return cs[camel];
9191                     }
9192                     return null;
9193                 };
9194         }(),
9195
9196         /**
9197          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
9198          * @param {String/Object} property The style property to be set, or an object of multiple styles.
9199          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
9200          * @return {Roo.Element} this
9201          */
9202         setStyle : function(prop, value){
9203             if(typeof prop == "string"){
9204                 
9205                 if (prop == 'float') {
9206                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
9207                     return this;
9208                 }
9209                 
9210                 var camel;
9211                 if(!(camel = propCache[prop])){
9212                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
9213                 }
9214                 
9215                 if(camel == 'opacity') {
9216                     this.setOpacity(value);
9217                 }else{
9218                     this.dom.style[camel] = value;
9219                 }
9220             }else{
9221                 for(var style in prop){
9222                     if(typeof prop[style] != "function"){
9223                        this.setStyle(style, prop[style]);
9224                     }
9225                 }
9226             }
9227             return this;
9228         },
9229
9230         /**
9231          * More flexible version of {@link #setStyle} for setting style properties.
9232          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
9233          * a function which returns such a specification.
9234          * @return {Roo.Element} this
9235          */
9236         applyStyles : function(style){
9237             Roo.DomHelper.applyStyles(this.dom, style);
9238             return this;
9239         },
9240
9241         /**
9242           * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9243           * @return {Number} The X position of the element
9244           */
9245         getX : function(){
9246             return D.getX(this.dom);
9247         },
9248
9249         /**
9250           * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9251           * @return {Number} The Y position of the element
9252           */
9253         getY : function(){
9254             return D.getY(this.dom);
9255         },
9256
9257         /**
9258           * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9259           * @return {Array} The XY position of the element
9260           */
9261         getXY : function(){
9262             return D.getXY(this.dom);
9263         },
9264
9265         /**
9266          * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9267          * @param {Number} The X position of the element
9268          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9269          * @return {Roo.Element} this
9270          */
9271         setX : function(x, animate){
9272             if(!animate || !A){
9273                 D.setX(this.dom, x);
9274             }else{
9275                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
9276             }
9277             return this;
9278         },
9279
9280         /**
9281          * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9282          * @param {Number} The Y position of the element
9283          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9284          * @return {Roo.Element} this
9285          */
9286         setY : function(y, animate){
9287             if(!animate || !A){
9288                 D.setY(this.dom, y);
9289             }else{
9290                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
9291             }
9292             return this;
9293         },
9294
9295         /**
9296          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
9297          * @param {String} left The left CSS property value
9298          * @return {Roo.Element} this
9299          */
9300         setLeft : function(left){
9301             this.setStyle("left", this.addUnits(left));
9302             return this;
9303         },
9304
9305         /**
9306          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
9307          * @param {String} top The top CSS property value
9308          * @return {Roo.Element} this
9309          */
9310         setTop : function(top){
9311             this.setStyle("top", this.addUnits(top));
9312             return this;
9313         },
9314
9315         /**
9316          * Sets the element's CSS right style.
9317          * @param {String} right The right CSS property value
9318          * @return {Roo.Element} this
9319          */
9320         setRight : function(right){
9321             this.setStyle("right", this.addUnits(right));
9322             return this;
9323         },
9324
9325         /**
9326          * Sets the element's CSS bottom style.
9327          * @param {String} bottom The bottom CSS property value
9328          * @return {Roo.Element} this
9329          */
9330         setBottom : function(bottom){
9331             this.setStyle("bottom", this.addUnits(bottom));
9332             return this;
9333         },
9334
9335         /**
9336          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
9337          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9338          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
9339          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9340          * @return {Roo.Element} this
9341          */
9342         setXY : function(pos, animate){
9343             if(!animate || !A){
9344                 D.setXY(this.dom, pos);
9345             }else{
9346                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
9347             }
9348             return this;
9349         },
9350
9351         /**
9352          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
9353          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9354          * @param {Number} x X value for new position (coordinates are page-based)
9355          * @param {Number} y Y value for new position (coordinates are page-based)
9356          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9357          * @return {Roo.Element} this
9358          */
9359         setLocation : function(x, y, animate){
9360             this.setXY([x, y], this.preanim(arguments, 2));
9361             return this;
9362         },
9363
9364         /**
9365          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
9366          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9367          * @param {Number} x X value for new position (coordinates are page-based)
9368          * @param {Number} y Y value for new position (coordinates are page-based)
9369          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9370          * @return {Roo.Element} this
9371          */
9372         moveTo : function(x, y, animate){
9373             this.setXY([x, y], this.preanim(arguments, 2));
9374             return this;
9375         },
9376
9377         /**
9378          * Returns the region of the given element.
9379          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
9380          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
9381          */
9382         getRegion : function(){
9383             return D.getRegion(this.dom);
9384         },
9385
9386         /**
9387          * Returns the offset height of the element
9388          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
9389          * @return {Number} The element's height
9390          */
9391         getHeight : function(contentHeight){
9392             var h = this.dom.offsetHeight || 0;
9393             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
9394         },
9395
9396         /**
9397          * Returns the offset width of the element
9398          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
9399          * @return {Number} The element's width
9400          */
9401         getWidth : function(contentWidth){
9402             var w = this.dom.offsetWidth || 0;
9403             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
9404         },
9405
9406         /**
9407          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
9408          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
9409          * if a height has not been set using CSS.
9410          * @return {Number}
9411          */
9412         getComputedHeight : function(){
9413             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
9414             if(!h){
9415                 h = parseInt(this.getStyle('height'), 10) || 0;
9416                 if(!this.isBorderBox()){
9417                     h += this.getFrameWidth('tb');
9418                 }
9419             }
9420             return h;
9421         },
9422
9423         /**
9424          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
9425          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
9426          * if a width has not been set using CSS.
9427          * @return {Number}
9428          */
9429         getComputedWidth : function(){
9430             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
9431             if(!w){
9432                 w = parseInt(this.getStyle('width'), 10) || 0;
9433                 if(!this.isBorderBox()){
9434                     w += this.getFrameWidth('lr');
9435                 }
9436             }
9437             return w;
9438         },
9439
9440         /**
9441          * Returns the size of the element.
9442          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
9443          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
9444          */
9445         getSize : function(contentSize){
9446             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
9447         },
9448
9449         /**
9450          * Returns the width and height of the viewport.
9451          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
9452          */
9453         getViewSize : function(){
9454             var d = this.dom, doc = document, aw = 0, ah = 0;
9455             if(d == doc || d == doc.body){
9456                 return {width : D.getViewWidth(), height: D.getViewHeight()};
9457             }else{
9458                 return {
9459                     width : d.clientWidth,
9460                     height: d.clientHeight
9461                 };
9462             }
9463         },
9464
9465         /**
9466          * Returns the value of the "value" attribute
9467          * @param {Boolean} asNumber true to parse the value as a number
9468          * @return {String/Number}
9469          */
9470         getValue : function(asNumber){
9471             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
9472         },
9473
9474         // private
9475         adjustWidth : function(width){
9476             if(typeof width == "number"){
9477                 if(this.autoBoxAdjust && !this.isBorderBox()){
9478                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9479                 }
9480                 if(width < 0){
9481                     width = 0;
9482                 }
9483             }
9484             return width;
9485         },
9486
9487         // private
9488         adjustHeight : function(height){
9489             if(typeof height == "number"){
9490                if(this.autoBoxAdjust && !this.isBorderBox()){
9491                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9492                }
9493                if(height < 0){
9494                    height = 0;
9495                }
9496             }
9497             return height;
9498         },
9499
9500         /**
9501          * Set the width of the element
9502          * @param {Number} width The new width
9503          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9504          * @return {Roo.Element} this
9505          */
9506         setWidth : function(width, animate){
9507             width = this.adjustWidth(width);
9508             if(!animate || !A){
9509                 this.dom.style.width = this.addUnits(width);
9510             }else{
9511                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
9512             }
9513             return this;
9514         },
9515
9516         /**
9517          * Set the height of the element
9518          * @param {Number} height The new height
9519          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9520          * @return {Roo.Element} this
9521          */
9522          setHeight : function(height, animate){
9523             height = this.adjustHeight(height);
9524             if(!animate || !A){
9525                 this.dom.style.height = this.addUnits(height);
9526             }else{
9527                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
9528             }
9529             return this;
9530         },
9531
9532         /**
9533          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
9534          * @param {Number} width The new width
9535          * @param {Number} height The new height
9536          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9537          * @return {Roo.Element} this
9538          */
9539          setSize : function(width, height, animate){
9540             if(typeof width == "object"){ // in case of object from getSize()
9541                 height = width.height; width = width.width;
9542             }
9543             width = this.adjustWidth(width); height = this.adjustHeight(height);
9544             if(!animate || !A){
9545                 this.dom.style.width = this.addUnits(width);
9546                 this.dom.style.height = this.addUnits(height);
9547             }else{
9548                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
9549             }
9550             return this;
9551         },
9552
9553         /**
9554          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
9555          * @param {Number} x X value for new position (coordinates are page-based)
9556          * @param {Number} y Y value for new position (coordinates are page-based)
9557          * @param {Number} width The new width
9558          * @param {Number} height The new height
9559          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9560          * @return {Roo.Element} this
9561          */
9562         setBounds : function(x, y, width, height, animate){
9563             if(!animate || !A){
9564                 this.setSize(width, height);
9565                 this.setLocation(x, y);
9566             }else{
9567                 width = this.adjustWidth(width); height = this.adjustHeight(height);
9568                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
9569                               this.preanim(arguments, 4), 'motion');
9570             }
9571             return this;
9572         },
9573
9574         /**
9575          * Sets the element's position and size the the specified region. If animation is true then width, height, x and y will be animated concurrently.
9576          * @param {Roo.lib.Region} region The region to fill
9577          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9578          * @return {Roo.Element} this
9579          */
9580         setRegion : function(region, animate){
9581             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
9582             return this;
9583         },
9584
9585         /**
9586          * Appends an event handler
9587          *
9588          * @param {String}   eventName     The type of event to append
9589          * @param {Function} fn        The method the event invokes
9590          * @param {Object} scope       (optional) The scope (this object) of the fn
9591          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9592          */
9593         addListener : function(eventName, fn, scope, options)
9594         {
9595             if (eventName == 'dblclick') { // doublclick (touchstart) - faked on touch.
9596                 this.addListener('touchstart', this.onTapHandler, this);
9597             }
9598             
9599             // we need to handle a special case where dom element is a svg element.
9600             // in this case we do not actua
9601             if (!this.dom) {
9602                 return;
9603             }
9604             
9605             if (this.dom instanceof SVGElement && !(this.dom instanceof SVGSVGElement)) {
9606                 if (typeof(this.listeners[eventName]) == 'undefined') {
9607                     this.listeners[eventName] =  new Roo.util.Event(this, eventName);
9608                 }
9609                 this.listeners[eventName].addListener(fn, scope, options);
9610                 return;
9611             }
9612             
9613                 
9614             Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
9615             
9616             
9617         },
9618         tapedTwice : false,
9619         onTapHandler : function(event)
9620         {
9621             if(!this.tapedTwice) {
9622                 this.tapedTwice = true;
9623                 var s = this;
9624                 setTimeout( function() {
9625                     s.tapedTwice = false;
9626                 }, 300 );
9627                 return;
9628             }
9629             event.preventDefault();
9630             var revent = new MouseEvent('dblclick',  {
9631                 view: window,
9632                 bubbles: true,
9633                 cancelable: true
9634             });
9635              
9636             this.dom.dispatchEvent(revent);
9637             //action on double tap goes below
9638              
9639         }, 
9640  
9641         /**
9642          * Removes an event handler from this element
9643          * @param {String} eventName the type of event to remove
9644          * @param {Function} fn the method the event invokes
9645          * @param {Function} scope (needed for svg fake listeners)
9646          * @return {Roo.Element} this
9647          */
9648         removeListener : function(eventName, fn, scope){
9649             Roo.EventManager.removeListener(this.dom,  eventName, fn);
9650             if (typeof(this.listeners) == 'undefined'  || typeof(this.listeners[eventName]) == 'undefined') {
9651                 return this;
9652             }
9653             this.listeners[eventName].removeListener(fn, scope);
9654             return this;
9655         },
9656
9657         /**
9658          * Removes all previous added listeners from this element
9659          * @return {Roo.Element} this
9660          */
9661         removeAllListeners : function(){
9662             E.purgeElement(this.dom);
9663             this.listeners = {};
9664             return this;
9665         },
9666
9667         relayEvent : function(eventName, observable){
9668             this.on(eventName, function(e){
9669                 observable.fireEvent(eventName, e);
9670             });
9671         },
9672
9673         
9674         /**
9675          * Set the opacity of the element
9676          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
9677          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9678          * @return {Roo.Element} this
9679          */
9680          setOpacity : function(opacity, animate){
9681             if(!animate || !A){
9682                 var s = this.dom.style;
9683                 if(Roo.isIE){
9684                     s.zoom = 1;
9685                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
9686                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
9687                 }else{
9688                     s.opacity = opacity;
9689                 }
9690             }else{
9691                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
9692             }
9693             return this;
9694         },
9695
9696         /**
9697          * Gets the left X coordinate
9698          * @param {Boolean} local True to get the local css position instead of page coordinate
9699          * @return {Number}
9700          */
9701         getLeft : function(local){
9702             if(!local){
9703                 return this.getX();
9704             }else{
9705                 return parseInt(this.getStyle("left"), 10) || 0;
9706             }
9707         },
9708
9709         /**
9710          * Gets the right X coordinate of the element (element X position + element width)
9711          * @param {Boolean} local True to get the local css position instead of page coordinate
9712          * @return {Number}
9713          */
9714         getRight : function(local){
9715             if(!local){
9716                 return this.getX() + this.getWidth();
9717             }else{
9718                 return (this.getLeft(true) + this.getWidth()) || 0;
9719             }
9720         },
9721
9722         /**
9723          * Gets the top Y coordinate
9724          * @param {Boolean} local True to get the local css position instead of page coordinate
9725          * @return {Number}
9726          */
9727         getTop : function(local) {
9728             if(!local){
9729                 return this.getY();
9730             }else{
9731                 return parseInt(this.getStyle("top"), 10) || 0;
9732             }
9733         },
9734
9735         /**
9736          * Gets the bottom Y coordinate of the element (element Y position + element height)
9737          * @param {Boolean} local True to get the local css position instead of page coordinate
9738          * @return {Number}
9739          */
9740         getBottom : function(local){
9741             if(!local){
9742                 return this.getY() + this.getHeight();
9743             }else{
9744                 return (this.getTop(true) + this.getHeight()) || 0;
9745             }
9746         },
9747
9748         /**
9749         * Initializes positioning on this element. If a desired position is not passed, it will make the
9750         * the element positioned relative IF it is not already positioned.
9751         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
9752         * @param {Number} zIndex (optional) The zIndex to apply
9753         * @param {Number} x (optional) Set the page X position
9754         * @param {Number} y (optional) Set the page Y position
9755         */
9756         position : function(pos, zIndex, x, y){
9757             if(!pos){
9758                if(this.getStyle('position') == 'static'){
9759                    this.setStyle('position', 'relative');
9760                }
9761             }else{
9762                 this.setStyle("position", pos);
9763             }
9764             if(zIndex){
9765                 this.setStyle("z-index", zIndex);
9766             }
9767             if(x !== undefined && y !== undefined){
9768                 this.setXY([x, y]);
9769             }else if(x !== undefined){
9770                 this.setX(x);
9771             }else if(y !== undefined){
9772                 this.setY(y);
9773             }
9774         },
9775
9776         /**
9777         * Clear positioning back to the default when the document was loaded
9778         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
9779         * @return {Roo.Element} this
9780          */
9781         clearPositioning : function(value){
9782             value = value ||'';
9783             this.setStyle({
9784                 "left": value,
9785                 "right": value,
9786                 "top": value,
9787                 "bottom": value,
9788                 "z-index": "",
9789                 "position" : "static"
9790             });
9791             return this;
9792         },
9793
9794         /**
9795         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
9796         * snapshot before performing an update and then restoring the element.
9797         * @return {Object}
9798         */
9799         getPositioning : function(){
9800             var l = this.getStyle("left");
9801             var t = this.getStyle("top");
9802             return {
9803                 "position" : this.getStyle("position"),
9804                 "left" : l,
9805                 "right" : l ? "" : this.getStyle("right"),
9806                 "top" : t,
9807                 "bottom" : t ? "" : this.getStyle("bottom"),
9808                 "z-index" : this.getStyle("z-index")
9809             };
9810         },
9811
9812         /**
9813          * Gets the width of the border(s) for the specified side(s)
9814          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9815          * passing lr would get the border (l)eft width + the border (r)ight width.
9816          * @return {Number} The width of the sides passed added together
9817          */
9818         getBorderWidth : function(side){
9819             return this.addStyles(side, El.borders);
9820         },
9821
9822         /**
9823          * Gets the width of the padding(s) for the specified side(s)
9824          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9825          * passing lr would get the padding (l)eft + the padding (r)ight.
9826          * @return {Number} The padding of the sides passed added together
9827          */
9828         getPadding : function(side){
9829             return this.addStyles(side, El.paddings);
9830         },
9831
9832         /**
9833         * Set positioning with an object returned by getPositioning().
9834         * @param {Object} posCfg
9835         * @return {Roo.Element} this
9836          */
9837         setPositioning : function(pc){
9838             this.applyStyles(pc);
9839             if(pc.right == "auto"){
9840                 this.dom.style.right = "";
9841             }
9842             if(pc.bottom == "auto"){
9843                 this.dom.style.bottom = "";
9844             }
9845             return this;
9846         },
9847
9848         // private
9849         fixDisplay : function(){
9850             if(this.getStyle("display") == "none"){
9851                 this.setStyle("visibility", "hidden");
9852                 this.setStyle("display", this.originalDisplay); // first try reverting to default
9853                 if(this.getStyle("display") == "none"){ // if that fails, default to block
9854                     this.setStyle("display", "block");
9855                 }
9856             }
9857         },
9858
9859         /**
9860          * Quick set left and top adding default units
9861          * @param {String} left The left CSS property value
9862          * @param {String} top The top CSS property value
9863          * @return {Roo.Element} this
9864          */
9865          setLeftTop : function(left, top){
9866             this.dom.style.left = this.addUnits(left);
9867             this.dom.style.top = this.addUnits(top);
9868             return this;
9869         },
9870
9871         /**
9872          * Move this element relative to its current position.
9873          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9874          * @param {Number} distance How far to move the element in pixels
9875          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9876          * @return {Roo.Element} this
9877          */
9878          move : function(direction, distance, animate){
9879             var xy = this.getXY();
9880             direction = direction.toLowerCase();
9881             switch(direction){
9882                 case "l":
9883                 case "left":
9884                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
9885                     break;
9886                case "r":
9887                case "right":
9888                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
9889                     break;
9890                case "t":
9891                case "top":
9892                case "up":
9893                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
9894                     break;
9895                case "b":
9896                case "bottom":
9897                case "down":
9898                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
9899                     break;
9900             }
9901             return this;
9902         },
9903
9904         /**
9905          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
9906          * @return {Roo.Element} this
9907          */
9908         clip : function(){
9909             if(!this.isClipped){
9910                this.isClipped = true;
9911                this.originalClip = {
9912                    "o": this.getStyle("overflow"),
9913                    "x": this.getStyle("overflow-x"),
9914                    "y": this.getStyle("overflow-y")
9915                };
9916                this.setStyle("overflow", "hidden");
9917                this.setStyle("overflow-x", "hidden");
9918                this.setStyle("overflow-y", "hidden");
9919             }
9920             return this;
9921         },
9922
9923         /**
9924          *  Return clipping (overflow) to original clipping before clip() was called
9925          * @return {Roo.Element} this
9926          */
9927         unclip : function(){
9928             if(this.isClipped){
9929                 this.isClipped = false;
9930                 var o = this.originalClip;
9931                 if(o.o){this.setStyle("overflow", o.o);}
9932                 if(o.x){this.setStyle("overflow-x", o.x);}
9933                 if(o.y){this.setStyle("overflow-y", o.y);}
9934             }
9935             return this;
9936         },
9937
9938
9939         /**
9940          * Gets the x,y coordinates specified by the anchor position on the element.
9941          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
9942          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
9943          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
9944          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
9945          * @return {Array} [x, y] An array containing the element's x and y coordinates
9946          */
9947         getAnchorXY : function(anchor, local, s){
9948             //Passing a different size is useful for pre-calculating anchors,
9949             //especially for anchored animations that change the el size.
9950
9951             var w, h, vp = false;
9952             if(!s){
9953                 var d = this.dom;
9954                 if(d == document.body || d == document){
9955                     vp = true;
9956                     w = D.getViewWidth(); h = D.getViewHeight();
9957                 }else{
9958                     w = this.getWidth(); h = this.getHeight();
9959                 }
9960             }else{
9961                 w = s.width;  h = s.height;
9962             }
9963             var x = 0, y = 0, r = Math.round;
9964             switch((anchor || "tl").toLowerCase()){
9965                 case "c":
9966                     x = r(w*.5);
9967                     y = r(h*.5);
9968                 break;
9969                 case "t":
9970                     x = r(w*.5);
9971                     y = 0;
9972                 break;
9973                 case "l":
9974                     x = 0;
9975                     y = r(h*.5);
9976                 break;
9977                 case "r":
9978                     x = w;
9979                     y = r(h*.5);
9980                 break;
9981                 case "b":
9982                     x = r(w*.5);
9983                     y = h;
9984                 break;
9985                 case "tl":
9986                     x = 0;
9987                     y = 0;
9988                 break;
9989                 case "bl":
9990                     x = 0;
9991                     y = h;
9992                 break;
9993                 case "br":
9994                     x = w;
9995                     y = h;
9996                 break;
9997                 case "tr":
9998                     x = w;
9999                     y = 0;
10000                 break;
10001             }
10002             if(local === true){
10003                 return [x, y];
10004             }
10005             if(vp){
10006                 var sc = this.getScroll();
10007                 return [x + sc.left, y + sc.top];
10008             }
10009             //Add the element's offset xy
10010             var o = this.getXY();
10011             return [x+o[0], y+o[1]];
10012         },
10013
10014         /**
10015          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
10016          * supported position values.
10017          * @param {String/HTMLElement/Roo.Element} element The element to align to.
10018          * @param {String} position The position to align to.
10019          * @param {Array} offsets (optional) Offset the positioning by [x, y]
10020          * @return {Array} [x, y]
10021          */
10022         getAlignToXY : function(el, p, o)
10023         {
10024             el = Roo.get(el);
10025             var d = this.dom;
10026             if(!el.dom){
10027                 throw "Element.alignTo with an element that doesn't exist";
10028             }
10029             var c = false; //constrain to viewport
10030             var p1 = "", p2 = "";
10031             o = o || [0,0];
10032
10033             if(!p){
10034                 p = "tl-bl";
10035             }else if(p == "?"){
10036                 p = "tl-bl?";
10037             }else if(p.indexOf("-") == -1){
10038                 p = "tl-" + p;
10039             }
10040             p = p.toLowerCase();
10041             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
10042             if(!m){
10043                throw "Element.alignTo with an invalid alignment " + p;
10044             }
10045             p1 = m[1]; p2 = m[2]; c = !!m[3];
10046
10047             //Subtract the aligned el's internal xy from the target's offset xy
10048             //plus custom offset to get the aligned el's new offset xy
10049             var a1 = this.getAnchorXY(p1, true);
10050             var a2 = el.getAnchorXY(p2, false);
10051             var x = a2[0] - a1[0] + o[0];
10052             var y = a2[1] - a1[1] + o[1];
10053             if(c){
10054                 //constrain the aligned el to viewport if necessary
10055                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
10056                 // 5px of margin for ie
10057                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
10058
10059                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
10060                 //perpendicular to the vp border, allow the aligned el to slide on that border,
10061                 //otherwise swap the aligned el to the opposite border of the target.
10062                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
10063                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
10064                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t")  );
10065                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
10066
10067                var doc = document;
10068                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
10069                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
10070
10071                if((x+w) > dw + scrollX){
10072                     x = swapX ? r.left-w : dw+scrollX-w;
10073                 }
10074                if(x < scrollX){
10075                    x = swapX ? r.right : scrollX;
10076                }
10077                if((y+h) > dh + scrollY){
10078                     y = swapY ? r.top-h : dh+scrollY-h;
10079                 }
10080                if (y < scrollY){
10081                    y = swapY ? r.bottom : scrollY;
10082                }
10083             }
10084             return [x,y];
10085         },
10086
10087         // private
10088         getConstrainToXY : function(){
10089             var os = {top:0, left:0, bottom:0, right: 0};
10090
10091             return function(el, local, offsets, proposedXY){
10092                 el = Roo.get(el);
10093                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
10094
10095                 var vw, vh, vx = 0, vy = 0;
10096                 if(el.dom == document.body || el.dom == document){
10097                     vw = Roo.lib.Dom.getViewWidth();
10098                     vh = Roo.lib.Dom.getViewHeight();
10099                 }else{
10100                     vw = el.dom.clientWidth;
10101                     vh = el.dom.clientHeight;
10102                     if(!local){
10103                         var vxy = el.getXY();
10104                         vx = vxy[0];
10105                         vy = vxy[1];
10106                     }
10107                 }
10108
10109                 var s = el.getScroll();
10110
10111                 vx += offsets.left + s.left;
10112                 vy += offsets.top + s.top;
10113
10114                 vw -= offsets.right;
10115                 vh -= offsets.bottom;
10116
10117                 var vr = vx+vw;
10118                 var vb = vy+vh;
10119
10120                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
10121                 var x = xy[0], y = xy[1];
10122                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
10123
10124                 // only move it if it needs it
10125                 var moved = false;
10126
10127                 // first validate right/bottom
10128                 if((x + w) > vr){
10129                     x = vr - w;
10130                     moved = true;
10131                 }
10132                 if((y + h) > vb){
10133                     y = vb - h;
10134                     moved = true;
10135                 }
10136                 // then make sure top/left isn't negative
10137                 if(x < vx){
10138                     x = vx;
10139                     moved = true;
10140                 }
10141                 if(y < vy){
10142                     y = vy;
10143                     moved = true;
10144                 }
10145                 return moved ? [x, y] : false;
10146             };
10147         }(),
10148
10149         // private
10150         adjustForConstraints : function(xy, parent, offsets){
10151             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
10152         },
10153
10154         /**
10155          * Aligns this element with another element relative to the specified anchor points. If the other element is the
10156          * document it aligns it to the viewport.
10157          * The position parameter is optional, and can be specified in any one of the following formats:
10158          * <ul>
10159          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
10160          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
10161          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
10162          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
10163          *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
10164          *       element's anchor point, and the second value is used as the target's anchor point.</li>
10165          * </ul>
10166          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
10167          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
10168          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
10169          * that specified in order to enforce the viewport constraints.
10170          * Following are all of the supported anchor positions:
10171     <pre>
10172     Value  Description
10173     -----  -----------------------------
10174     tl     The top left corner (default)
10175     t      The center of the top edge
10176     tr     The top right corner
10177     l      The center of the left edge
10178     c      In the center of the element
10179     r      The center of the right edge
10180     bl     The bottom left corner
10181     b      The center of the bottom edge
10182     br     The bottom right corner
10183     </pre>
10184     Example Usage:
10185     <pre><code>
10186     // align el to other-el using the default positioning ("tl-bl", non-constrained)
10187     el.alignTo("other-el");
10188
10189     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
10190     el.alignTo("other-el", "tr?");
10191
10192     // align the bottom right corner of el with the center left edge of other-el
10193     el.alignTo("other-el", "br-l?");
10194
10195     // align the center of el with the bottom left corner of other-el and
10196     // adjust the x position by -6 pixels (and the y position by 0)
10197     el.alignTo("other-el", "c-bl", [-6, 0]);
10198     </code></pre>
10199          * @param {String/HTMLElement/Roo.Element} element The element to align to.
10200          * @param {String} position The position to align to.
10201          * @param {Array} offsets (optional) Offset the positioning by [x, y]
10202          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10203          * @return {Roo.Element} this
10204          */
10205         alignTo : function(element, position, offsets, animate){
10206             var xy = this.getAlignToXY(element, position, offsets);
10207             this.setXY(xy, this.preanim(arguments, 3));
10208             return this;
10209         },
10210
10211         /**
10212          * Anchors an element to another element and realigns it when the window is resized.
10213          * @param {String/HTMLElement/Roo.Element} element The element to align to.
10214          * @param {String} position The position to align to.
10215          * @param {Array} offsets (optional) Offset the positioning by [x, y]
10216          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
10217          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
10218          * is a number, it is used as the buffer delay (defaults to 50ms).
10219          * @param {Function} callback The function to call after the animation finishes
10220          * @return {Roo.Element} this
10221          */
10222         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
10223             var action = function(){
10224                 this.alignTo(el, alignment, offsets, animate);
10225                 Roo.callback(callback, this);
10226             };
10227             Roo.EventManager.onWindowResize(action, this);
10228             var tm = typeof monitorScroll;
10229             if(tm != 'undefined'){
10230                 Roo.EventManager.on(window, 'scroll', action, this,
10231                     {buffer: tm == 'number' ? monitorScroll : 50});
10232             }
10233             action.call(this); // align immediately
10234             return this;
10235         },
10236         /**
10237          * Clears any opacity settings from this element. Required in some cases for IE.
10238          * @return {Roo.Element} this
10239          */
10240         clearOpacity : function(){
10241             if (window.ActiveXObject) {
10242                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
10243                     this.dom.style.filter = "";
10244                 }
10245             } else {
10246                 this.dom.style.opacity = "";
10247                 this.dom.style["-moz-opacity"] = "";
10248                 this.dom.style["-khtml-opacity"] = "";
10249             }
10250             return this;
10251         },
10252
10253         /**
10254          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
10255          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10256          * @return {Roo.Element} this
10257          */
10258         hide : function(animate){
10259             this.setVisible(false, this.preanim(arguments, 0));
10260             return this;
10261         },
10262
10263         /**
10264         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
10265         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10266          * @return {Roo.Element} this
10267          */
10268         show : function(animate){
10269             this.setVisible(true, this.preanim(arguments, 0));
10270             return this;
10271         },
10272
10273         /**
10274          * @private Test if size has a unit, otherwise appends the default
10275          */
10276         addUnits : function(size){
10277             return Roo.Element.addUnits(size, this.defaultUnit);
10278         },
10279
10280         /**
10281          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
10282          * @return {Roo.Element} this
10283          */
10284         beginMeasure : function(){
10285             var el = this.dom;
10286             if(el.offsetWidth || el.offsetHeight){
10287                 return this; // offsets work already
10288             }
10289             var changed = [];
10290             var p = this.dom, b = document.body; // start with this element
10291             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
10292                 var pe = Roo.get(p);
10293                 if(pe.getStyle('display') == 'none'){
10294                     changed.push({el: p, visibility: pe.getStyle("visibility")});
10295                     p.style.visibility = "hidden";
10296                     p.style.display = "block";
10297                 }
10298                 p = p.parentNode;
10299             }
10300             this._measureChanged = changed;
10301             return this;
10302
10303         },
10304
10305         /**
10306          * Restores displays to before beginMeasure was called
10307          * @return {Roo.Element} this
10308          */
10309         endMeasure : function(){
10310             var changed = this._measureChanged;
10311             if(changed){
10312                 for(var i = 0, len = changed.length; i < len; i++) {
10313                     var r = changed[i];
10314                     r.el.style.visibility = r.visibility;
10315                     r.el.style.display = "none";
10316                 }
10317                 this._measureChanged = null;
10318             }
10319             return this;
10320         },
10321
10322         /**
10323         * Update the innerHTML of this element, optionally searching for and processing scripts
10324         * @param {String} html The new HTML
10325         * @param {Boolean} loadScripts (optional) true to look for and process scripts
10326         * @param {Function} callback For async script loading you can be noticed when the update completes
10327         * @return {Roo.Element} this
10328          */
10329         update : function(html, loadScripts, callback){
10330             if(typeof html == "undefined"){
10331                 html = "";
10332             }
10333             if(loadScripts !== true){
10334                 this.dom.innerHTML = html;
10335                 if(typeof callback == "function"){
10336                     callback();
10337                 }
10338                 return this;
10339             }
10340             var id = Roo.id();
10341             var dom = this.dom;
10342
10343             html += '<span id="' + id + '"></span>';
10344
10345             E.onAvailable(id, function(){
10346                 var hd = document.getElementsByTagName("head")[0];
10347                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
10348                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
10349                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
10350
10351                 var match;
10352                 while(match = re.exec(html)){
10353                     var attrs = match[1];
10354                     var srcMatch = attrs ? attrs.match(srcRe) : false;
10355                     if(srcMatch && srcMatch[2]){
10356                        var s = document.createElement("script");
10357                        s.src = srcMatch[2];
10358                        var typeMatch = attrs.match(typeRe);
10359                        if(typeMatch && typeMatch[2]){
10360                            s.type = typeMatch[2];
10361                        }
10362                        hd.appendChild(s);
10363                     }else if(match[2] && match[2].length > 0){
10364                         if(window.execScript) {
10365                            window.execScript(match[2]);
10366                         } else {
10367                             /**
10368                              * eval:var:id
10369                              * eval:var:dom
10370                              * eval:var:html
10371                              * 
10372                              */
10373                            window.eval(match[2]);
10374                         }
10375                     }
10376                 }
10377                 var el = document.getElementById(id);
10378                 if(el){el.parentNode.removeChild(el);}
10379                 if(typeof callback == "function"){
10380                     callback();
10381                 }
10382             });
10383             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
10384             return this;
10385         },
10386
10387         /**
10388          * Direct access to the UpdateManager update() method (takes the same parameters).
10389          * @param {String/Function} url The url for this request or a function to call to get the url
10390          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
10391          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
10392          * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
10393          * @return {Roo.Element} this
10394          */
10395         load : function(){
10396             var um = this.getUpdateManager();
10397             um.update.apply(um, arguments);
10398             return this;
10399         },
10400
10401         /**
10402         * Gets this element's UpdateManager
10403         * @return {Roo.UpdateManager} The UpdateManager
10404         */
10405         getUpdateManager : function(){
10406             if(!this.updateManager){
10407                 this.updateManager = new Roo.UpdateManager(this);
10408             }
10409             return this.updateManager;
10410         },
10411
10412         /**
10413          * Disables text selection for this element (normalized across browsers)
10414          * @return {Roo.Element} this
10415          */
10416         unselectable : function(){
10417             this.dom.unselectable = "on";
10418             this.swallowEvent("selectstart", true);
10419             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
10420             this.addClass("x-unselectable");
10421             return this;
10422         },
10423
10424         /**
10425         * Calculates the x, y to center this element on the screen
10426         * @return {Array} The x, y values [x, y]
10427         */
10428         getCenterXY : function(){
10429             return this.getAlignToXY(document, 'c-c');
10430         },
10431
10432         /**
10433         * Centers the Element in either the viewport, or another Element.
10434         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
10435         */
10436         center : function(centerIn){
10437             this.alignTo(centerIn || document, 'c-c');
10438             return this;
10439         },
10440
10441         /**
10442          * Tests various css rules/browsers to determine if this element uses a border box
10443          * @return {Boolean}
10444          */
10445         isBorderBox : function(){
10446             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
10447         },
10448
10449         /**
10450          * Return a box {x, y, width, height} that can be used to set another elements
10451          * size/location to match this element.
10452          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
10453          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
10454          * @return {Object} box An object in the format {x, y, width, height}
10455          */
10456         getBox : function(contentBox, local){
10457             var xy;
10458             if(!local){
10459                 xy = this.getXY();
10460             }else{
10461                 var left = parseInt(this.getStyle("left"), 10) || 0;
10462                 var top = parseInt(this.getStyle("top"), 10) || 0;
10463                 xy = [left, top];
10464             }
10465             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
10466             if(!contentBox){
10467                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
10468             }else{
10469                 var l = this.getBorderWidth("l")+this.getPadding("l");
10470                 var r = this.getBorderWidth("r")+this.getPadding("r");
10471                 var t = this.getBorderWidth("t")+this.getPadding("t");
10472                 var b = this.getBorderWidth("b")+this.getPadding("b");
10473                 bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
10474             }
10475             bx.right = bx.x + bx.width;
10476             bx.bottom = bx.y + bx.height;
10477             return bx;
10478         },
10479
10480         /**
10481          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
10482          for more information about the sides.
10483          * @param {String} sides
10484          * @return {Number}
10485          */
10486         getFrameWidth : function(sides, onlyContentBox){
10487             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
10488         },
10489
10490         /**
10491          * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
10492          * @param {Object} box The box to fill {x, y, width, height}
10493          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
10494          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10495          * @return {Roo.Element} this
10496          */
10497         setBox : function(box, adjust, animate){
10498             var w = box.width, h = box.height;
10499             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
10500                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
10501                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
10502             }
10503             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
10504             return this;
10505         },
10506
10507         /**
10508          * Forces the browser to repaint this element
10509          * @return {Roo.Element} this
10510          */
10511          repaint : function(){
10512             var dom = this.dom;
10513             this.addClass("x-repaint");
10514             setTimeout(function(){
10515                 Roo.get(dom).removeClass("x-repaint");
10516             }, 1);
10517             return this;
10518         },
10519
10520         /**
10521          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
10522          * then it returns the calculated width of the sides (see getPadding)
10523          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
10524          * @return {Object/Number}
10525          */
10526         getMargins : function(side){
10527             if(!side){
10528                 return {
10529                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
10530                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
10531                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
10532                     right: parseInt(this.getStyle("margin-right"), 10) || 0
10533                 };
10534             }else{
10535                 return this.addStyles(side, El.margins);
10536              }
10537         },
10538
10539         // private
10540         addStyles : function(sides, styles){
10541             var val = 0, v, w;
10542             for(var i = 0, len = sides.length; i < len; i++){
10543                 v = this.getStyle(styles[sides.charAt(i)]);
10544                 if(v){
10545                      w = parseInt(v, 10);
10546                      if(w){ val += w; }
10547                 }
10548             }
10549             return val;
10550         },
10551
10552         /**
10553          * Creates a proxy element of this element
10554          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
10555          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
10556          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
10557          * @return {Roo.Element} The new proxy element
10558          */
10559         createProxy : function(config, renderTo, matchBox){
10560             if(renderTo){
10561                 renderTo = Roo.getDom(renderTo);
10562             }else{
10563                 renderTo = document.body;
10564             }
10565             config = typeof config == "object" ?
10566                 config : {tag : "div", cls: config};
10567             var proxy = Roo.DomHelper.append(renderTo, config, true);
10568             if(matchBox){
10569                proxy.setBox(this.getBox());
10570             }
10571             return proxy;
10572         },
10573
10574         /**
10575          * Puts a mask over this element to disable user interaction. Requires core.css.
10576          * This method can only be applied to elements which accept child nodes.
10577          * @param {String} msg (optional) A message to display in the mask
10578          * @param {String} msgCls (optional) A css class to apply to the msg element - use no-spinner to hide the spinner on bootstrap
10579          * @return {Element} The mask  element
10580          */
10581         mask : function(msg, msgCls)
10582         {
10583             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
10584                 this.setStyle("position", "relative");
10585             }
10586             if(!this._mask){
10587                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
10588             }
10589             
10590             this.addClass("x-masked");
10591             this._mask.setDisplayed(true);
10592             
10593             // we wander
10594             var z = 0;
10595             var dom = this.dom;
10596             while (dom && dom.style) {
10597                 if (!isNaN(parseInt(dom.style.zIndex))) {
10598                     z = Math.max(z, parseInt(dom.style.zIndex));
10599                 }
10600                 dom = dom.parentNode;
10601             }
10602             // if we are masking the body - then it hides everything..
10603             if (this.dom == document.body) {
10604                 z = 1000000;
10605                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
10606                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
10607             }
10608            
10609             if(typeof msg == 'string'){
10610                 if(!this._maskMsg){
10611                     this._maskMsg = Roo.DomHelper.append(this.dom, {
10612                         cls: "roo-el-mask-msg", 
10613                         cn: [
10614                             {
10615                                 tag: 'i',
10616                                 cls: 'fa fa-spinner fa-spin'
10617                             },
10618                             {
10619                                 tag: 'div'
10620                             }   
10621                         ]
10622                     }, true);
10623                 }
10624                 var mm = this._maskMsg;
10625                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
10626                 if (mm.dom.lastChild) { // weird IE issue?
10627                     mm.dom.lastChild.innerHTML = msg;
10628                 }
10629                 mm.setDisplayed(true);
10630                 mm.center(this);
10631                 mm.setStyle('z-index', z + 102);
10632             }
10633             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
10634                 this._mask.setHeight(this.getHeight());
10635             }
10636             this._mask.setStyle('z-index', z + 100);
10637             
10638             return this._mask;
10639         },
10640
10641         /**
10642          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
10643          * it is cached for reuse.
10644          */
10645         unmask : function(removeEl){
10646             if(this._mask){
10647                 if(removeEl === true){
10648                     this._mask.remove();
10649                     delete this._mask;
10650                     if(this._maskMsg){
10651                         this._maskMsg.remove();
10652                         delete this._maskMsg;
10653                     }
10654                 }else{
10655                     this._mask.setDisplayed(false);
10656                     if(this._maskMsg){
10657                         this._maskMsg.setDisplayed(false);
10658                     }
10659                 }
10660             }
10661             this.removeClass("x-masked");
10662         },
10663
10664         /**
10665          * Returns true if this element is masked
10666          * @return {Boolean}
10667          */
10668         isMasked : function(){
10669             return this._mask && this._mask.isVisible();
10670         },
10671
10672         /**
10673          * Creates an iframe shim for this element to keep selects and other windowed objects from
10674          * showing through.
10675          * @return {Roo.Element} The new shim element
10676          */
10677         createShim : function(){
10678             var el = document.createElement('iframe');
10679             el.frameBorder = 'no';
10680             el.className = 'roo-shim';
10681             if(Roo.isIE && Roo.isSecure){
10682                 el.src = Roo.SSL_SECURE_URL;
10683             }
10684             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
10685             shim.autoBoxAdjust = false;
10686             return shim;
10687         },
10688
10689         /**
10690          * Removes this element from the DOM and deletes it from the cache
10691          */
10692         remove : function(){
10693             if(this.dom.parentNode){
10694                 this.dom.parentNode.removeChild(this.dom);
10695             }
10696             delete El.cache[this.dom.id];
10697         },
10698
10699         /**
10700          * Sets up event handlers to add and remove a css class when the mouse is over this element
10701          * @param {String} className
10702          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
10703          * mouseout events for children elements
10704          * @return {Roo.Element} this
10705          */
10706         addClassOnOver : function(className, preventFlicker){
10707             this.on("mouseover", function(){
10708                 Roo.fly(this, '_internal').addClass(className);
10709             }, this.dom);
10710             var removeFn = function(e){
10711                 if(preventFlicker !== true || !e.within(this, true)){
10712                     Roo.fly(this, '_internal').removeClass(className);
10713                 }
10714             };
10715             this.on("mouseout", removeFn, this.dom);
10716             return this;
10717         },
10718
10719         /**
10720          * Sets up event handlers to add and remove a css class when this element has the focus
10721          * @param {String} className
10722          * @return {Roo.Element} this
10723          */
10724         addClassOnFocus : function(className){
10725             this.on("focus", function(){
10726                 Roo.fly(this, '_internal').addClass(className);
10727             }, this.dom);
10728             this.on("blur", function(){
10729                 Roo.fly(this, '_internal').removeClass(className);
10730             }, this.dom);
10731             return this;
10732         },
10733         /**
10734          * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)
10735          * @param {String} className
10736          * @return {Roo.Element} this
10737          */
10738         addClassOnClick : function(className){
10739             var dom = this.dom;
10740             this.on("mousedown", function(){
10741                 Roo.fly(dom, '_internal').addClass(className);
10742                 var d = Roo.get(document);
10743                 var fn = function(){
10744                     Roo.fly(dom, '_internal').removeClass(className);
10745                     d.removeListener("mouseup", fn);
10746                 };
10747                 d.on("mouseup", fn);
10748             });
10749             return this;
10750         },
10751
10752         /**
10753          * Stops the specified event from bubbling and optionally prevents the default action
10754          * @param {String} eventName
10755          * @param {Boolean} preventDefault (optional) true to prevent the default action too
10756          * @return {Roo.Element} this
10757          */
10758         swallowEvent : function(eventName, preventDefault){
10759             var fn = function(e){
10760                 e.stopPropagation();
10761                 if(preventDefault){
10762                     e.preventDefault();
10763                 }
10764             };
10765             if(eventName instanceof Array){
10766                 for(var i = 0, len = eventName.length; i < len; i++){
10767                      this.on(eventName[i], fn);
10768                 }
10769                 return this;
10770             }
10771             this.on(eventName, fn);
10772             return this;
10773         },
10774
10775         /**
10776          * @private
10777          */
10778         fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
10779
10780         /**
10781          * Sizes this element to its parent element's dimensions performing
10782          * neccessary box adjustments.
10783          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
10784          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
10785          * @return {Roo.Element} this
10786          */
10787         fitToParent : function(monitorResize, targetParent) {
10788           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
10789           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
10790           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
10791             return this;
10792           }
10793           var p = Roo.get(targetParent || this.dom.parentNode);
10794           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
10795           if (monitorResize === true) {
10796             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
10797             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
10798           }
10799           return this;
10800         },
10801
10802         /**
10803          * Gets the next sibling, skipping text nodes
10804          * @return {HTMLElement} The next sibling or null
10805          */
10806         getNextSibling : function(){
10807             var n = this.dom.nextSibling;
10808             while(n && n.nodeType != 1){
10809                 n = n.nextSibling;
10810             }
10811             return n;
10812         },
10813
10814         /**
10815          * Gets the previous sibling, skipping text nodes
10816          * @return {HTMLElement} The previous sibling or null
10817          */
10818         getPrevSibling : function(){
10819             var n = this.dom.previousSibling;
10820             while(n && n.nodeType != 1){
10821                 n = n.previousSibling;
10822             }
10823             return n;
10824         },
10825
10826
10827         /**
10828          * Appends the passed element(s) to this element
10829          * @param {String/HTMLElement/Array/Element/CompositeElement} el
10830          * @return {Roo.Element} this
10831          */
10832         appendChild: function(el){
10833             el = Roo.get(el);
10834             el.appendTo(this);
10835             return this;
10836         },
10837
10838         /**
10839          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
10840          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
10841          * automatically generated with the specified attributes.
10842          * @param {HTMLElement} insertBefore (optional) a child element of this element
10843          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
10844          * @return {Roo.Element} The new child element
10845          */
10846         createChild: function(config, insertBefore, returnDom){
10847             config = config || {tag:'div'};
10848             if(insertBefore){
10849                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
10850             }
10851             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
10852         },
10853
10854         /**
10855          * Appends this element to the passed element
10856          * @param {String/HTMLElement/Element} el The new parent element
10857          * @return {Roo.Element} this
10858          */
10859         appendTo: function(el){
10860             el = Roo.getDom(el);
10861             el.appendChild(this.dom);
10862             return this;
10863         },
10864
10865         /**
10866          * Inserts this element before the passed element in the DOM
10867          * @param {String/HTMLElement/Element} el The element to insert before
10868          * @return {Roo.Element} this
10869          */
10870         insertBefore: function(el){
10871             el = Roo.getDom(el);
10872             el.parentNode.insertBefore(this.dom, el);
10873             return this;
10874         },
10875
10876         /**
10877          * Inserts this element after the passed element in the DOM
10878          * @param {String/HTMLElement/Element} el The element to insert after
10879          * @return {Roo.Element} this
10880          */
10881         insertAfter: function(el){
10882             el = Roo.getDom(el);
10883             el.parentNode.insertBefore(this.dom, el.nextSibling);
10884             return this;
10885         },
10886
10887         /**
10888          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
10889          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10890          * @return {Roo.Element} The new child
10891          */
10892         insertFirst: function(el, returnDom){
10893             el = el || {};
10894             if(typeof el == 'object' && !el.nodeType){ // dh config
10895                 return this.createChild(el, this.dom.firstChild, returnDom);
10896             }else{
10897                 el = Roo.getDom(el);
10898                 this.dom.insertBefore(el, this.dom.firstChild);
10899                 return !returnDom ? Roo.get(el) : el;
10900             }
10901         },
10902
10903         /**
10904          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
10905          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10906          * @param {String} where (optional) 'before' or 'after' defaults to before
10907          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10908          * @return {Roo.Element} the inserted Element
10909          */
10910         insertSibling: function(el, where, returnDom){
10911             where = where ? where.toLowerCase() : 'before';
10912             el = el || {};
10913             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
10914
10915             if(typeof el == 'object' && !el.nodeType){ // dh config
10916                 if(where == 'after' && !this.dom.nextSibling){
10917                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
10918                 }else{
10919                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
10920                 }
10921
10922             }else{
10923                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
10924                             where == 'before' ? this.dom : this.dom.nextSibling);
10925                 if(!returnDom){
10926                     rt = Roo.get(rt);
10927                 }
10928             }
10929             return rt;
10930         },
10931
10932         /**
10933          * Creates and wraps this element with another element
10934          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
10935          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10936          * @return {HTMLElement/Element} The newly created wrapper element
10937          */
10938         wrap: function(config, returnDom){
10939             if(!config){
10940                 config = {tag: "div"};
10941             }
10942             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
10943             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
10944             return newEl;
10945         },
10946
10947         /**
10948          * Replaces the passed element with this element
10949          * @param {String/HTMLElement/Element} el The element to replace
10950          * @return {Roo.Element} this
10951          */
10952         replace: function(el){
10953             el = Roo.get(el);
10954             this.insertBefore(el);
10955             el.remove();
10956             return this;
10957         },
10958
10959         /**
10960          * Inserts an html fragment into this element
10961          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
10962          * @param {String} html The HTML fragment
10963          * @param {Boolean} returnEl True to return an Roo.Element
10964          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
10965          */
10966         insertHtml : function(where, html, returnEl){
10967             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
10968             return returnEl ? Roo.get(el) : el;
10969         },
10970
10971         /**
10972          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
10973          * @param {Object} o The object with the attributes
10974          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
10975          * @return {Roo.Element} this
10976          */
10977         set : function(o, useSet){
10978             var el = this.dom;
10979             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
10980             for(var attr in o){
10981                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
10982                 if(attr=="cls"){
10983                     el.className = o["cls"];
10984                 }else{
10985                     if(useSet) {
10986                         el.setAttribute(attr, o[attr]);
10987                     } else {
10988                         el[attr] = o[attr];
10989                     }
10990                 }
10991             }
10992             if(o.style){
10993                 Roo.DomHelper.applyStyles(el, o.style);
10994             }
10995             return this;
10996         },
10997
10998         /**
10999          * Convenience method for constructing a KeyMap
11000          * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options:
11001          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
11002          * @param {Function} fn The function to call
11003          * @param {Object} scope (optional) The scope of the function
11004          * @return {Roo.KeyMap} The KeyMap created
11005          */
11006         addKeyListener : function(key, fn, scope){
11007             var config;
11008             if(typeof key != "object" || key instanceof Array){
11009                 config = {
11010                     key: key,
11011                     fn: fn,
11012                     scope: scope
11013                 };
11014             }else{
11015                 config = {
11016                     key : key.key,
11017                     shift : key.shift,
11018                     ctrl : key.ctrl,
11019                     alt : key.alt,
11020                     fn: fn,
11021                     scope: scope
11022                 };
11023             }
11024             return new Roo.KeyMap(this, config);
11025         },
11026
11027         /**
11028          * Creates a KeyMap for this element
11029          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
11030          * @return {Roo.KeyMap} The KeyMap created
11031          */
11032         addKeyMap : function(config){
11033             return new Roo.KeyMap(this, config);
11034         },
11035
11036         /**
11037          * Returns true if this element is scrollable.
11038          * @return {Boolean}
11039          */
11040          isScrollable : function(){
11041             var dom = this.dom;
11042             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
11043         },
11044
11045         /**
11046          * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().
11047          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
11048          * @param {Number} value The new scroll value
11049          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
11050          * @return {Element} this
11051          */
11052
11053         scrollTo : function(side, value, animate){
11054             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
11055             if(!animate || !A){
11056                 this.dom[prop] = value;
11057             }else{
11058                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
11059                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
11060             }
11061             return this;
11062         },
11063
11064         /**
11065          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
11066          * within this element's scrollable range.
11067          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
11068          * @param {Number} distance How far to scroll the element in pixels
11069          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
11070          * @return {Boolean} Returns true if a scroll was triggered or false if the element
11071          * was scrolled as far as it could go.
11072          */
11073          scroll : function(direction, distance, animate){
11074              if(!this.isScrollable()){
11075                  return;
11076              }
11077              var el = this.dom;
11078              var l = el.scrollLeft, t = el.scrollTop;
11079              var w = el.scrollWidth, h = el.scrollHeight;
11080              var cw = el.clientWidth, ch = el.clientHeight;
11081              direction = direction.toLowerCase();
11082              var scrolled = false;
11083              var a = this.preanim(arguments, 2);
11084              switch(direction){
11085                  case "l":
11086                  case "left":
11087                      if(w - l > cw){
11088                          var v = Math.min(l + distance, w-cw);
11089                          this.scrollTo("left", v, a);
11090                          scrolled = true;
11091                      }
11092                      break;
11093                 case "r":
11094                 case "right":
11095                      if(l > 0){
11096                          var v = Math.max(l - distance, 0);
11097                          this.scrollTo("left", v, a);
11098                          scrolled = true;
11099                      }
11100                      break;
11101                 case "t":
11102                 case "top":
11103                 case "up":
11104                      if(t > 0){
11105                          var v = Math.max(t - distance, 0);
11106                          this.scrollTo("top", v, a);
11107                          scrolled = true;
11108                      }
11109                      break;
11110                 case "b":
11111                 case "bottom":
11112                 case "down":
11113                      if(h - t > ch){
11114                          var v = Math.min(t + distance, h-ch);
11115                          this.scrollTo("top", v, a);
11116                          scrolled = true;
11117                      }
11118                      break;
11119              }
11120              return scrolled;
11121         },
11122
11123         /**
11124          * Translates the passed page coordinates into left/top css values for this element
11125          * @param {Number/Array} x The page x or an array containing [x, y]
11126          * @param {Number} y The page y
11127          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
11128          */
11129         translatePoints : function(x, y){
11130             if(typeof x == 'object' || x instanceof Array){
11131                 y = x[1]; x = x[0];
11132             }
11133             var p = this.getStyle('position');
11134             var o = this.getXY();
11135
11136             var l = parseInt(this.getStyle('left'), 10);
11137             var t = parseInt(this.getStyle('top'), 10);
11138
11139             if(isNaN(l)){
11140                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
11141             }
11142             if(isNaN(t)){
11143                 t = (p == "relative") ? 0 : this.dom.offsetTop;
11144             }
11145
11146             return {left: (x - o[0] + l), top: (y - o[1] + t)};
11147         },
11148
11149         /**
11150          * Returns the current scroll position of the element.
11151          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
11152          */
11153         getScroll : function(){
11154             var d = this.dom, doc = document;
11155             if(d == doc || d == doc.body){
11156                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
11157                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
11158                 return {left: l, top: t};
11159             }else{
11160                 return {left: d.scrollLeft, top: d.scrollTop};
11161             }
11162         },
11163
11164         /**
11165          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
11166          * are convert to standard 6 digit hex color.
11167          * @param {String} attr The css attribute
11168          * @param {String} defaultValue The default value to use when a valid color isn't found
11169          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
11170          * YUI color anims.
11171          */
11172         getColor : function(attr, defaultValue, prefix){
11173             var v = this.getStyle(attr);
11174             if(!v || v == "transparent" || v == "inherit") {
11175                 return defaultValue;
11176             }
11177             var color = typeof prefix == "undefined" ? "#" : prefix;
11178             if(v.substr(0, 4) == "rgb("){
11179                 var rvs = v.slice(4, v.length -1).split(",");
11180                 for(var i = 0; i < 3; i++){
11181                     var h = parseInt(rvs[i]).toString(16);
11182                     if(h < 16){
11183                         h = "0" + h;
11184                     }
11185                     color += h;
11186                 }
11187             } else {
11188                 if(v.substr(0, 1) == "#"){
11189                     if(v.length == 4) {
11190                         for(var i = 1; i < 4; i++){
11191                             var c = v.charAt(i);
11192                             color +=  c + c;
11193                         }
11194                     }else if(v.length == 7){
11195                         color += v.substr(1);
11196                     }
11197                 }
11198             }
11199             return(color.length > 5 ? color.toLowerCase() : defaultValue);
11200         },
11201
11202         /**
11203          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
11204          * gradient background, rounded corners and a 4-way shadow.
11205          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
11206          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
11207          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
11208          * @return {Roo.Element} this
11209          */
11210         boxWrap : function(cls){
11211             cls = cls || 'x-box';
11212             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
11213             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
11214             return el;
11215         },
11216
11217         /**
11218          * Returns the value of a namespaced attribute from the element's underlying DOM node.
11219          * @param {String} namespace The namespace in which to look for the attribute
11220          * @param {String} name The attribute name
11221          * @return {String} The attribute value
11222          */
11223         getAttributeNS : Roo.isIE ? function(ns, name){
11224             var d = this.dom;
11225             var type = typeof d[ns+":"+name];
11226             if(type != 'undefined' && type != 'unknown'){
11227                 return d[ns+":"+name];
11228             }
11229             return d[name];
11230         } : function(ns, name){
11231             var d = this.dom;
11232             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
11233         },
11234         
11235         
11236         /**
11237          * Sets or Returns the value the dom attribute value
11238          * @param {String|Object} name The attribute name (or object to set multiple attributes)
11239          * @param {String} value (optional) The value to set the attribute to
11240          * @return {String} The attribute value
11241          */
11242         attr : function(name){
11243             if (arguments.length > 1) {
11244                 this.dom.setAttribute(name, arguments[1]);
11245                 return arguments[1];
11246             }
11247             if (typeof(name) == 'object') {
11248                 for(var i in name) {
11249                     this.attr(i, name[i]);
11250                 }
11251                 return name;
11252             }
11253             
11254             
11255             if (!this.dom.hasAttribute(name)) {
11256                 return undefined;
11257             }
11258             return this.dom.getAttribute(name);
11259         }
11260         
11261         
11262         
11263     };
11264
11265     var ep = El.prototype;
11266
11267     /**
11268      * Appends an event handler (Shorthand for addListener)
11269      * @param {String}   eventName     The type of event to append
11270      * @param {Function} fn        The method the event invokes
11271      * @param {Object} scope       (optional) The scope (this object) of the fn
11272      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
11273      * @method
11274      */
11275     ep.on = ep.addListener;
11276         // backwards compat
11277     ep.mon = ep.addListener;
11278
11279     /**
11280      * Removes an event handler from this element (shorthand for removeListener)
11281      * @param {String} eventName the type of event to remove
11282      * @param {Function} fn the method the event invokes
11283      * @return {Roo.Element} this
11284      * @method
11285      */
11286     ep.un = ep.removeListener;
11287
11288     /**
11289      * true to automatically adjust width and height settings for box-model issues (default to true)
11290      */
11291     ep.autoBoxAdjust = true;
11292
11293     // private
11294     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
11295
11296     // private
11297     El.addUnits = function(v, defaultUnit){
11298         if(v === "" || v == "auto"){
11299             return v;
11300         }
11301         if(v === undefined){
11302             return '';
11303         }
11304         if(typeof v == "number" || !El.unitPattern.test(v)){
11305             return v + (defaultUnit || 'px');
11306         }
11307         return v;
11308     };
11309
11310     // special markup used throughout Roo when box wrapping elements
11311     El.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
11312     /**
11313      * Visibility mode constant - Use visibility to hide element
11314      * @static
11315      * @type Number
11316      */
11317     El.VISIBILITY = 1;
11318     /**
11319      * Visibility mode constant - Use display to hide element
11320      * @static
11321      * @type Number
11322      */
11323     El.DISPLAY = 2;
11324
11325     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
11326     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
11327     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
11328
11329
11330
11331     /**
11332      * @private
11333      */
11334     El.cache = {};
11335
11336     var docEl;
11337
11338     /**
11339      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
11340      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
11341      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
11342      * @return {Element} The Element object
11343      * @static
11344      */
11345     El.get = function(el){
11346         var ex, elm, id;
11347         if(!el){ return null; }
11348         if(typeof el == "string"){ // element id
11349             if(!(elm = document.getElementById(el))){
11350                 return null;
11351             }
11352             if(ex = El.cache[el]){
11353                 ex.dom = elm;
11354             }else{
11355                 ex = El.cache[el] = new El(elm);
11356             }
11357             return ex;
11358         }else if(el.tagName){ // dom element
11359             if(!(id = el.id)){
11360                 id = Roo.id(el);
11361             }
11362             if(ex = El.cache[id]){
11363                 ex.dom = el;
11364             }else{
11365                 ex = El.cache[id] = new El(el);
11366             }
11367             return ex;
11368         }else if(el instanceof El){
11369             if(el != docEl){
11370                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
11371                                                               // catch case where it hasn't been appended
11372                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
11373             }
11374             return el;
11375         }else if(el.isComposite){
11376             return el;
11377         }else if(el instanceof Array){
11378             return El.select(el);
11379         }else if(el == document){
11380             // create a bogus element object representing the document object
11381             if(!docEl){
11382                 var f = function(){};
11383                 f.prototype = El.prototype;
11384                 docEl = new f();
11385                 docEl.dom = document;
11386             }
11387             return docEl;
11388         }
11389         return null;
11390     };
11391
11392     // private
11393     El.uncache = function(el){
11394         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
11395             if(a[i]){
11396                 delete El.cache[a[i].id || a[i]];
11397             }
11398         }
11399     };
11400
11401     // private
11402     // Garbage collection - uncache elements/purge listeners on orphaned elements
11403     // so we don't hold a reference and cause the browser to retain them
11404     El.garbageCollect = function(){
11405         if(!Roo.enableGarbageCollector){
11406             clearInterval(El.collectorThread);
11407             return;
11408         }
11409         for(var eid in El.cache){
11410             var el = El.cache[eid], d = el.dom;
11411             // -------------------------------------------------------
11412             // Determining what is garbage:
11413             // -------------------------------------------------------
11414             // !d
11415             // dom node is null, definitely garbage
11416             // -------------------------------------------------------
11417             // !d.parentNode
11418             // no parentNode == direct orphan, definitely garbage
11419             // -------------------------------------------------------
11420             // !d.offsetParent && !document.getElementById(eid)
11421             // display none elements have no offsetParent so we will
11422             // also try to look it up by it's id. However, check
11423             // offsetParent first so we don't do unneeded lookups.
11424             // This enables collection of elements that are not orphans
11425             // directly, but somewhere up the line they have an orphan
11426             // parent.
11427             // -------------------------------------------------------
11428             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
11429                 delete El.cache[eid];
11430                 if(d && Roo.enableListenerCollection){
11431                     E.purgeElement(d);
11432                 }
11433             }
11434         }
11435     }
11436     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
11437
11438
11439     // dom is optional
11440     El.Flyweight = function(dom){
11441         this.dom = dom;
11442     };
11443     El.Flyweight.prototype = El.prototype;
11444
11445     El._flyweights = {};
11446     /**
11447      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
11448      * the dom node can be overwritten by other code.
11449      * @param {String/HTMLElement} el The dom node or id
11450      * @param {String} named (optional) Allows for creation of named reusable flyweights to
11451      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
11452      * @static
11453      * @return {Element} The shared Element object
11454      */
11455     El.fly = function(el, named){
11456         named = named || '_global';
11457         el = Roo.getDom(el);
11458         if(!el){
11459             return null;
11460         }
11461         if(!El._flyweights[named]){
11462             El._flyweights[named] = new El.Flyweight();
11463         }
11464         El._flyweights[named].dom = el;
11465         return El._flyweights[named];
11466     };
11467
11468     /**
11469      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
11470      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
11471      * Shorthand of {@link Roo.Element#get}
11472      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
11473      * @return {Element} The Element object
11474      * @member Roo
11475      * @method get
11476      */
11477     Roo.get = El.get;
11478     /**
11479      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
11480      * the dom node can be overwritten by other code.
11481      * Shorthand of {@link Roo.Element#fly}
11482      * @param {String/HTMLElement} el The dom node or id
11483      * @param {String} named (optional) Allows for creation of named reusable flyweights to
11484      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
11485      * @static
11486      * @return {Element} The shared Element object
11487      * @member Roo
11488      * @method fly
11489      */
11490     Roo.fly = El.fly;
11491
11492     // speedy lookup for elements never to box adjust
11493     var noBoxAdjust = Roo.isStrict ? {
11494         select:1
11495     } : {
11496         input:1, select:1, textarea:1
11497     };
11498     if(Roo.isIE || Roo.isGecko){
11499         noBoxAdjust['button'] = 1;
11500     }
11501
11502
11503     Roo.EventManager.on(window, 'unload', function(){
11504         delete El.cache;
11505         delete El._flyweights;
11506     });
11507 })();
11508
11509
11510
11511
11512 if(Roo.DomQuery){
11513     Roo.Element.selectorFunction = Roo.DomQuery.select;
11514 }
11515
11516 Roo.Element.select = function(selector, unique, root){
11517     var els;
11518     if(typeof selector == "string"){
11519         els = Roo.Element.selectorFunction(selector, root);
11520     }else if(selector.length !== undefined){
11521         els = selector;
11522     }else{
11523         throw "Invalid selector";
11524     }
11525     if(unique === true){
11526         return new Roo.CompositeElement(els);
11527     }else{
11528         return new Roo.CompositeElementLite(els);
11529     }
11530 };
11531 /**
11532  * Selects elements based on the passed CSS selector to enable working on them as 1.
11533  * @param {String/Array} selector The CSS selector or an array of elements
11534  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
11535  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
11536  * @return {CompositeElementLite/CompositeElement}
11537  * @member Roo
11538  * @method select
11539  */
11540 Roo.select = Roo.Element.select;
11541
11542
11543
11544
11545
11546
11547
11548
11549
11550
11551
11552
11553
11554
11555 /*
11556  * Based on:
11557  * Ext JS Library 1.1.1
11558  * Copyright(c) 2006-2007, Ext JS, LLC.
11559  *
11560  * Originally Released Under LGPL - original licence link has changed is not relivant.
11561  *
11562  * Fork - LGPL
11563  * <script type="text/javascript">
11564  */
11565
11566
11567
11568 //Notifies Element that fx methods are available
11569 Roo.enableFx = true;
11570
11571 /**
11572  * @class Roo.Fx
11573  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
11574  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
11575  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
11576  * Element effects to work.</p><br/>
11577  *
11578  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
11579  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
11580  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
11581  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
11582  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
11583  * expected results and should be done with care.</p><br/>
11584  *
11585  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
11586  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
11587 <pre>
11588 Value  Description
11589 -----  -----------------------------
11590 tl     The top left corner
11591 t      The center of the top edge
11592 tr     The top right corner
11593 l      The center of the left edge
11594 r      The center of the right edge
11595 bl     The bottom left corner
11596 b      The center of the bottom edge
11597 br     The bottom right corner
11598 </pre>
11599  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
11600  * below are common options that can be passed to any Fx method.</b>
11601  * @cfg {Function} callback A function called when the effect is finished
11602  * @cfg {Object} scope The scope of the effect function
11603  * @cfg {String} easing A valid Easing value for the effect
11604  * @cfg {String} afterCls A css class to apply after the effect
11605  * @cfg {Number} duration The length of time (in seconds) that the effect should last
11606  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
11607  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
11608  * effects that end with the element being visually hidden, ignored otherwise)
11609  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
11610  * a function which returns such a specification that will be applied to the Element after the effect finishes
11611  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
11612  * @cfg {Boolean} concurrent Whether to allow subsequently-queued effects to run at the same time as the current effect, or to ensure that they run in sequence
11613  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
11614  */
11615 Roo.Fx = {
11616         /**
11617          * Slides the element into view.  An anchor point can be optionally passed to set the point of
11618          * origin for the slide effect.  This function automatically handles wrapping the element with
11619          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
11620          * Usage:
11621          *<pre><code>
11622 // default: slide the element in from the top
11623 el.slideIn();
11624
11625 // custom: slide the element in from the right with a 2-second duration
11626 el.slideIn('r', { duration: 2 });
11627
11628 // common config options shown with default values
11629 el.slideIn('t', {
11630     easing: 'easeOut',
11631     duration: .5
11632 });
11633 </code></pre>
11634          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11635          * @param {Object} options (optional) Object literal with any of the Fx config options
11636          * @return {Roo.Element} The Element
11637          */
11638     slideIn : function(anchor, o){
11639         var el = this.getFxEl();
11640         o = o || {};
11641
11642         el.queueFx(o, function(){
11643
11644             anchor = anchor || "t";
11645
11646             // fix display to visibility
11647             this.fixDisplay();
11648
11649             // restore values after effect
11650             var r = this.getFxRestore();
11651             var b = this.getBox();
11652             // fixed size for slide
11653             this.setSize(b);
11654
11655             // wrap if needed
11656             var wrap = this.fxWrap(r.pos, o, "hidden");
11657
11658             var st = this.dom.style;
11659             st.visibility = "visible";
11660             st.position = "absolute";
11661
11662             // clear out temp styles after slide and unwrap
11663             var after = function(){
11664                 el.fxUnwrap(wrap, r.pos, o);
11665                 st.width = r.width;
11666                 st.height = r.height;
11667                 el.afterFx(o);
11668             };
11669             // time to calc the positions
11670             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
11671
11672             switch(anchor.toLowerCase()){
11673                 case "t":
11674                     wrap.setSize(b.width, 0);
11675                     st.left = st.bottom = "0";
11676                     a = {height: bh};
11677                 break;
11678                 case "l":
11679                     wrap.setSize(0, b.height);
11680                     st.right = st.top = "0";
11681                     a = {width: bw};
11682                 break;
11683                 case "r":
11684                     wrap.setSize(0, b.height);
11685                     wrap.setX(b.right);
11686                     st.left = st.top = "0";
11687                     a = {width: bw, points: pt};
11688                 break;
11689                 case "b":
11690                     wrap.setSize(b.width, 0);
11691                     wrap.setY(b.bottom);
11692                     st.left = st.top = "0";
11693                     a = {height: bh, points: pt};
11694                 break;
11695                 case "tl":
11696                     wrap.setSize(0, 0);
11697                     st.right = st.bottom = "0";
11698                     a = {width: bw, height: bh};
11699                 break;
11700                 case "bl":
11701                     wrap.setSize(0, 0);
11702                     wrap.setY(b.y+b.height);
11703                     st.right = st.top = "0";
11704                     a = {width: bw, height: bh, points: pt};
11705                 break;
11706                 case "br":
11707                     wrap.setSize(0, 0);
11708                     wrap.setXY([b.right, b.bottom]);
11709                     st.left = st.top = "0";
11710                     a = {width: bw, height: bh, points: pt};
11711                 break;
11712                 case "tr":
11713                     wrap.setSize(0, 0);
11714                     wrap.setX(b.x+b.width);
11715                     st.left = st.bottom = "0";
11716                     a = {width: bw, height: bh, points: pt};
11717                 break;
11718             }
11719             this.dom.style.visibility = "visible";
11720             wrap.show();
11721
11722             arguments.callee.anim = wrap.fxanim(a,
11723                 o,
11724                 'motion',
11725                 .5,
11726                 'easeOut', after);
11727         });
11728         return this;
11729     },
11730     
11731         /**
11732          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
11733          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
11734          * 'hidden') but block elements will still take up space in the document.  The element must be removed
11735          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
11736          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
11737          * Usage:
11738          *<pre><code>
11739 // default: slide the element out to the top
11740 el.slideOut();
11741
11742 // custom: slide the element out to the right with a 2-second duration
11743 el.slideOut('r', { duration: 2 });
11744
11745 // common config options shown with default values
11746 el.slideOut('t', {
11747     easing: 'easeOut',
11748     duration: .5,
11749     remove: false,
11750     useDisplay: false
11751 });
11752 </code></pre>
11753          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11754          * @param {Object} options (optional) Object literal with any of the Fx config options
11755          * @return {Roo.Element} The Element
11756          */
11757     slideOut : function(anchor, o){
11758         var el = this.getFxEl();
11759         o = o || {};
11760
11761         el.queueFx(o, function(){
11762
11763             anchor = anchor || "t";
11764
11765             // restore values after effect
11766             var r = this.getFxRestore();
11767             
11768             var b = this.getBox();
11769             // fixed size for slide
11770             this.setSize(b);
11771
11772             // wrap if needed
11773             var wrap = this.fxWrap(r.pos, o, "visible");
11774
11775             var st = this.dom.style;
11776             st.visibility = "visible";
11777             st.position = "absolute";
11778
11779             wrap.setSize(b);
11780
11781             var after = function(){
11782                 if(o.useDisplay){
11783                     el.setDisplayed(false);
11784                 }else{
11785                     el.hide();
11786                 }
11787
11788                 el.fxUnwrap(wrap, r.pos, o);
11789
11790                 st.width = r.width;
11791                 st.height = r.height;
11792
11793                 el.afterFx(o);
11794             };
11795
11796             var a, zero = {to: 0};
11797             switch(anchor.toLowerCase()){
11798                 case "t":
11799                     st.left = st.bottom = "0";
11800                     a = {height: zero};
11801                 break;
11802                 case "l":
11803                     st.right = st.top = "0";
11804                     a = {width: zero};
11805                 break;
11806                 case "r":
11807                     st.left = st.top = "0";
11808                     a = {width: zero, points: {to:[b.right, b.y]}};
11809                 break;
11810                 case "b":
11811                     st.left = st.top = "0";
11812                     a = {height: zero, points: {to:[b.x, b.bottom]}};
11813                 break;
11814                 case "tl":
11815                     st.right = st.bottom = "0";
11816                     a = {width: zero, height: zero};
11817                 break;
11818                 case "bl":
11819                     st.right = st.top = "0";
11820                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
11821                 break;
11822                 case "br":
11823                     st.left = st.top = "0";
11824                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
11825                 break;
11826                 case "tr":
11827                     st.left = st.bottom = "0";
11828                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
11829                 break;
11830             }
11831
11832             arguments.callee.anim = wrap.fxanim(a,
11833                 o,
11834                 'motion',
11835                 .5,
11836                 "easeOut", after);
11837         });
11838         return this;
11839     },
11840
11841         /**
11842          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
11843          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
11844          * The element must be removed from the DOM using the 'remove' config option if desired.
11845          * Usage:
11846          *<pre><code>
11847 // default
11848 el.puff();
11849
11850 // common config options shown with default values
11851 el.puff({
11852     easing: 'easeOut',
11853     duration: .5,
11854     remove: false,
11855     useDisplay: false
11856 });
11857 </code></pre>
11858          * @param {Object} options (optional) Object literal with any of the Fx config options
11859          * @return {Roo.Element} The Element
11860          */
11861     puff : function(o){
11862         var el = this.getFxEl();
11863         o = o || {};
11864
11865         el.queueFx(o, function(){
11866             this.clearOpacity();
11867             this.show();
11868
11869             // restore values after effect
11870             var r = this.getFxRestore();
11871             var st = this.dom.style;
11872
11873             var after = function(){
11874                 if(o.useDisplay){
11875                     el.setDisplayed(false);
11876                 }else{
11877                     el.hide();
11878                 }
11879
11880                 el.clearOpacity();
11881
11882                 el.setPositioning(r.pos);
11883                 st.width = r.width;
11884                 st.height = r.height;
11885                 st.fontSize = '';
11886                 el.afterFx(o);
11887             };
11888
11889             var width = this.getWidth();
11890             var height = this.getHeight();
11891
11892             arguments.callee.anim = this.fxanim({
11893                     width : {to: this.adjustWidth(width * 2)},
11894                     height : {to: this.adjustHeight(height * 2)},
11895                     points : {by: [-(width * .5), -(height * .5)]},
11896                     opacity : {to: 0},
11897                     fontSize: {to:200, unit: "%"}
11898                 },
11899                 o,
11900                 'motion',
11901                 .5,
11902                 "easeOut", after);
11903         });
11904         return this;
11905     },
11906
11907         /**
11908          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
11909          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
11910          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
11911          * Usage:
11912          *<pre><code>
11913 // default
11914 el.switchOff();
11915
11916 // all config options shown with default values
11917 el.switchOff({
11918     easing: 'easeIn',
11919     duration: .3,
11920     remove: false,
11921     useDisplay: false
11922 });
11923 </code></pre>
11924          * @param {Object} options (optional) Object literal with any of the Fx config options
11925          * @return {Roo.Element} The Element
11926          */
11927     switchOff : function(o){
11928         var el = this.getFxEl();
11929         o = o || {};
11930
11931         el.queueFx(o, function(){
11932             this.clearOpacity();
11933             this.clip();
11934
11935             // restore values after effect
11936             var r = this.getFxRestore();
11937             var st = this.dom.style;
11938
11939             var after = function(){
11940                 if(o.useDisplay){
11941                     el.setDisplayed(false);
11942                 }else{
11943                     el.hide();
11944                 }
11945
11946                 el.clearOpacity();
11947                 el.setPositioning(r.pos);
11948                 st.width = r.width;
11949                 st.height = r.height;
11950
11951                 el.afterFx(o);
11952             };
11953
11954             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
11955                 this.clearOpacity();
11956                 (function(){
11957                     this.fxanim({
11958                         height:{to:1},
11959                         points:{by:[0, this.getHeight() * .5]}
11960                     }, o, 'motion', 0.3, 'easeIn', after);
11961                 }).defer(100, this);
11962             });
11963         });
11964         return this;
11965     },
11966
11967     /**
11968      * Highlights the Element by setting a color (applies to the background-color by default, but can be
11969      * changed using the "attr" config option) and then fading back to the original color. If no original
11970      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
11971      * Usage:
11972 <pre><code>
11973 // default: highlight background to yellow
11974 el.highlight();
11975
11976 // custom: highlight foreground text to blue for 2 seconds
11977 el.highlight("0000ff", { attr: 'color', duration: 2 });
11978
11979 // common config options shown with default values
11980 el.highlight("ffff9c", {
11981     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
11982     endColor: (current color) or "ffffff",
11983     easing: 'easeIn',
11984     duration: 1
11985 });
11986 </code></pre>
11987      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
11988      * @param {Object} options (optional) Object literal with any of the Fx config options
11989      * @return {Roo.Element} The Element
11990      */ 
11991     highlight : function(color, o){
11992         var el = this.getFxEl();
11993         o = o || {};
11994
11995         el.queueFx(o, function(){
11996             color = color || "ffff9c";
11997             attr = o.attr || "backgroundColor";
11998
11999             this.clearOpacity();
12000             this.show();
12001
12002             var origColor = this.getColor(attr);
12003             var restoreColor = this.dom.style[attr];
12004             endColor = (o.endColor || origColor) || "ffffff";
12005
12006             var after = function(){
12007                 el.dom.style[attr] = restoreColor;
12008                 el.afterFx(o);
12009             };
12010
12011             var a = {};
12012             a[attr] = {from: color, to: endColor};
12013             arguments.callee.anim = this.fxanim(a,
12014                 o,
12015                 'color',
12016                 1,
12017                 'easeIn', after);
12018         });
12019         return this;
12020     },
12021
12022    /**
12023     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
12024     * Usage:
12025 <pre><code>
12026 // default: a single light blue ripple
12027 el.frame();
12028
12029 // custom: 3 red ripples lasting 3 seconds total
12030 el.frame("ff0000", 3, { duration: 3 });
12031
12032 // common config options shown with default values
12033 el.frame("C3DAF9", 1, {
12034     duration: 1 //duration of entire animation (not each individual ripple)
12035     // Note: Easing is not configurable and will be ignored if included
12036 });
12037 </code></pre>
12038     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
12039     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
12040     * @param {Object} options (optional) Object literal with any of the Fx config options
12041     * @return {Roo.Element} The Element
12042     */
12043     frame : function(color, count, o){
12044         var el = this.getFxEl();
12045         o = o || {};
12046
12047         el.queueFx(o, function(){
12048             color = color || "#C3DAF9";
12049             if(color.length == 6){
12050                 color = "#" + color;
12051             }
12052             count = count || 1;
12053             duration = o.duration || 1;
12054             this.show();
12055
12056             var b = this.getBox();
12057             var animFn = function(){
12058                 var proxy = this.createProxy({
12059
12060                      style:{
12061                         visbility:"hidden",
12062                         position:"absolute",
12063                         "z-index":"35000", // yee haw
12064                         border:"0px solid " + color
12065                      }
12066                   });
12067                 var scale = Roo.isBorderBox ? 2 : 1;
12068                 proxy.animate({
12069                     top:{from:b.y, to:b.y - 20},
12070                     left:{from:b.x, to:b.x - 20},
12071                     borderWidth:{from:0, to:10},
12072                     opacity:{from:1, to:0},
12073                     height:{from:b.height, to:(b.height + (20*scale))},
12074                     width:{from:b.width, to:(b.width + (20*scale))}
12075                 }, duration, function(){
12076                     proxy.remove();
12077                 });
12078                 if(--count > 0){
12079                      animFn.defer((duration/2)*1000, this);
12080                 }else{
12081                     el.afterFx(o);
12082                 }
12083             };
12084             animFn.call(this);
12085         });
12086         return this;
12087     },
12088
12089    /**
12090     * Creates a pause before any subsequent queued effects begin.  If there are
12091     * no effects queued after the pause it will have no effect.
12092     * Usage:
12093 <pre><code>
12094 el.pause(1);
12095 </code></pre>
12096     * @param {Number} seconds The length of time to pause (in seconds)
12097     * @return {Roo.Element} The Element
12098     */
12099     pause : function(seconds){
12100         var el = this.getFxEl();
12101         var o = {};
12102
12103         el.queueFx(o, function(){
12104             setTimeout(function(){
12105                 el.afterFx(o);
12106             }, seconds * 1000);
12107         });
12108         return this;
12109     },
12110
12111    /**
12112     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
12113     * using the "endOpacity" config option.
12114     * Usage:
12115 <pre><code>
12116 // default: fade in from opacity 0 to 100%
12117 el.fadeIn();
12118
12119 // custom: fade in from opacity 0 to 75% over 2 seconds
12120 el.fadeIn({ endOpacity: .75, duration: 2});
12121
12122 // common config options shown with default values
12123 el.fadeIn({
12124     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
12125     easing: 'easeOut',
12126     duration: .5
12127 });
12128 </code></pre>
12129     * @param {Object} options (optional) Object literal with any of the Fx config options
12130     * @return {Roo.Element} The Element
12131     */
12132     fadeIn : function(o){
12133         var el = this.getFxEl();
12134         o = o || {};
12135         el.queueFx(o, function(){
12136             this.setOpacity(0);
12137             this.fixDisplay();
12138             this.dom.style.visibility = 'visible';
12139             var to = o.endOpacity || 1;
12140             arguments.callee.anim = this.fxanim({opacity:{to:to}},
12141                 o, null, .5, "easeOut", function(){
12142                 if(to == 1){
12143                     this.clearOpacity();
12144                 }
12145                 el.afterFx(o);
12146             });
12147         });
12148         return this;
12149     },
12150
12151    /**
12152     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
12153     * using the "endOpacity" config option.
12154     * Usage:
12155 <pre><code>
12156 // default: fade out from the element's current opacity to 0
12157 el.fadeOut();
12158
12159 // custom: fade out from the element's current opacity to 25% over 2 seconds
12160 el.fadeOut({ endOpacity: .25, duration: 2});
12161
12162 // common config options shown with default values
12163 el.fadeOut({
12164     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
12165     easing: 'easeOut',
12166     duration: .5
12167     remove: false,
12168     useDisplay: false
12169 });
12170 </code></pre>
12171     * @param {Object} options (optional) Object literal with any of the Fx config options
12172     * @return {Roo.Element} The Element
12173     */
12174     fadeOut : function(o){
12175         var el = this.getFxEl();
12176         o = o || {};
12177         el.queueFx(o, function(){
12178             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
12179                 o, null, .5, "easeOut", function(){
12180                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
12181                      this.dom.style.display = "none";
12182                 }else{
12183                      this.dom.style.visibility = "hidden";
12184                 }
12185                 this.clearOpacity();
12186                 el.afterFx(o);
12187             });
12188         });
12189         return this;
12190     },
12191
12192    /**
12193     * Animates the transition of an element's dimensions from a starting height/width
12194     * to an ending height/width.
12195     * Usage:
12196 <pre><code>
12197 // change height and width to 100x100 pixels
12198 el.scale(100, 100);
12199
12200 // common config options shown with default values.  The height and width will default to
12201 // the element's existing values if passed as null.
12202 el.scale(
12203     [element's width],
12204     [element's height], {
12205     easing: 'easeOut',
12206     duration: .35
12207 });
12208 </code></pre>
12209     * @param {Number} width  The new width (pass undefined to keep the original width)
12210     * @param {Number} height  The new height (pass undefined to keep the original height)
12211     * @param {Object} options (optional) Object literal with any of the Fx config options
12212     * @return {Roo.Element} The Element
12213     */
12214     scale : function(w, h, o){
12215         this.shift(Roo.apply({}, o, {
12216             width: w,
12217             height: h
12218         }));
12219         return this;
12220     },
12221
12222    /**
12223     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
12224     * Any of these properties not specified in the config object will not be changed.  This effect 
12225     * requires that at least one new dimension, position or opacity setting must be passed in on
12226     * the config object in order for the function to have any effect.
12227     * Usage:
12228 <pre><code>
12229 // slide the element horizontally to x position 200 while changing the height and opacity
12230 el.shift({ x: 200, height: 50, opacity: .8 });
12231
12232 // common config options shown with default values.
12233 el.shift({
12234     width: [element's width],
12235     height: [element's height],
12236     x: [element's x position],
12237     y: [element's y position],
12238     opacity: [element's opacity],
12239     easing: 'easeOut',
12240     duration: .35
12241 });
12242 </code></pre>
12243     * @param {Object} options  Object literal with any of the Fx config options
12244     * @return {Roo.Element} The Element
12245     */
12246     shift : function(o){
12247         var el = this.getFxEl();
12248         o = o || {};
12249         el.queueFx(o, function(){
12250             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
12251             if(w !== undefined){
12252                 a.width = {to: this.adjustWidth(w)};
12253             }
12254             if(h !== undefined){
12255                 a.height = {to: this.adjustHeight(h)};
12256             }
12257             if(x !== undefined || y !== undefined){
12258                 a.points = {to: [
12259                     x !== undefined ? x : this.getX(),
12260                     y !== undefined ? y : this.getY()
12261                 ]};
12262             }
12263             if(op !== undefined){
12264                 a.opacity = {to: op};
12265             }
12266             if(o.xy !== undefined){
12267                 a.points = {to: o.xy};
12268             }
12269             arguments.callee.anim = this.fxanim(a,
12270                 o, 'motion', .35, "easeOut", function(){
12271                 el.afterFx(o);
12272             });
12273         });
12274         return this;
12275     },
12276
12277         /**
12278          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
12279          * ending point of the effect.
12280          * Usage:
12281          *<pre><code>
12282 // default: slide the element downward while fading out
12283 el.ghost();
12284
12285 // custom: slide the element out to the right with a 2-second duration
12286 el.ghost('r', { duration: 2 });
12287
12288 // common config options shown with default values
12289 el.ghost('b', {
12290     easing: 'easeOut',
12291     duration: .5
12292     remove: false,
12293     useDisplay: false
12294 });
12295 </code></pre>
12296          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
12297          * @param {Object} options (optional) Object literal with any of the Fx config options
12298          * @return {Roo.Element} The Element
12299          */
12300     ghost : function(anchor, o){
12301         var el = this.getFxEl();
12302         o = o || {};
12303
12304         el.queueFx(o, function(){
12305             anchor = anchor || "b";
12306
12307             // restore values after effect
12308             var r = this.getFxRestore();
12309             var w = this.getWidth(),
12310                 h = this.getHeight();
12311
12312             var st = this.dom.style;
12313
12314             var after = function(){
12315                 if(o.useDisplay){
12316                     el.setDisplayed(false);
12317                 }else{
12318                     el.hide();
12319                 }
12320
12321                 el.clearOpacity();
12322                 el.setPositioning(r.pos);
12323                 st.width = r.width;
12324                 st.height = r.height;
12325
12326                 el.afterFx(o);
12327             };
12328
12329             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
12330             switch(anchor.toLowerCase()){
12331                 case "t":
12332                     pt.by = [0, -h];
12333                 break;
12334                 case "l":
12335                     pt.by = [-w, 0];
12336                 break;
12337                 case "r":
12338                     pt.by = [w, 0];
12339                 break;
12340                 case "b":
12341                     pt.by = [0, h];
12342                 break;
12343                 case "tl":
12344                     pt.by = [-w, -h];
12345                 break;
12346                 case "bl":
12347                     pt.by = [-w, h];
12348                 break;
12349                 case "br":
12350                     pt.by = [w, h];
12351                 break;
12352                 case "tr":
12353                     pt.by = [w, -h];
12354                 break;
12355             }
12356
12357             arguments.callee.anim = this.fxanim(a,
12358                 o,
12359                 'motion',
12360                 .5,
12361                 "easeOut", after);
12362         });
12363         return this;
12364     },
12365
12366         /**
12367          * Ensures that all effects queued after syncFx is called on the element are
12368          * run concurrently.  This is the opposite of {@link #sequenceFx}.
12369          * @return {Roo.Element} The Element
12370          */
12371     syncFx : function(){
12372         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
12373             block : false,
12374             concurrent : true,
12375             stopFx : false
12376         });
12377         return this;
12378     },
12379
12380         /**
12381          * Ensures that all effects queued after sequenceFx is called on the element are
12382          * run in sequence.  This is the opposite of {@link #syncFx}.
12383          * @return {Roo.Element} The Element
12384          */
12385     sequenceFx : function(){
12386         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
12387             block : false,
12388             concurrent : false,
12389             stopFx : false
12390         });
12391         return this;
12392     },
12393
12394         /* @private */
12395     nextFx : function(){
12396         var ef = this.fxQueue[0];
12397         if(ef){
12398             ef.call(this);
12399         }
12400     },
12401
12402         /**
12403          * Returns true if the element has any effects actively running or queued, else returns false.
12404          * @return {Boolean} True if element has active effects, else false
12405          */
12406     hasActiveFx : function(){
12407         return this.fxQueue && this.fxQueue[0];
12408     },
12409
12410         /**
12411          * Stops any running effects and clears the element's internal effects queue if it contains
12412          * any additional effects that haven't started yet.
12413          * @return {Roo.Element} The Element
12414          */
12415     stopFx : function(){
12416         if(this.hasActiveFx()){
12417             var cur = this.fxQueue[0];
12418             if(cur && cur.anim && cur.anim.isAnimated()){
12419                 this.fxQueue = [cur]; // clear out others
12420                 cur.anim.stop(true);
12421             }
12422         }
12423         return this;
12424     },
12425
12426         /* @private */
12427     beforeFx : function(o){
12428         if(this.hasActiveFx() && !o.concurrent){
12429            if(o.stopFx){
12430                this.stopFx();
12431                return true;
12432            }
12433            return false;
12434         }
12435         return true;
12436     },
12437
12438         /**
12439          * Returns true if the element is currently blocking so that no other effect can be queued
12440          * until this effect is finished, else returns false if blocking is not set.  This is commonly
12441          * used to ensure that an effect initiated by a user action runs to completion prior to the
12442          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
12443          * @return {Boolean} True if blocking, else false
12444          */
12445     hasFxBlock : function(){
12446         var q = this.fxQueue;
12447         return q && q[0] && q[0].block;
12448     },
12449
12450         /* @private */
12451     queueFx : function(o, fn){
12452         if(!this.fxQueue){
12453             this.fxQueue = [];
12454         }
12455         if(!this.hasFxBlock()){
12456             Roo.applyIf(o, this.fxDefaults);
12457             if(!o.concurrent){
12458                 var run = this.beforeFx(o);
12459                 fn.block = o.block;
12460                 this.fxQueue.push(fn);
12461                 if(run){
12462                     this.nextFx();
12463                 }
12464             }else{
12465                 fn.call(this);
12466             }
12467         }
12468         return this;
12469     },
12470
12471         /* @private */
12472     fxWrap : function(pos, o, vis){
12473         var wrap;
12474         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
12475             var wrapXY;
12476             if(o.fixPosition){
12477                 wrapXY = this.getXY();
12478             }
12479             var div = document.createElement("div");
12480             div.style.visibility = vis;
12481             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
12482             wrap.setPositioning(pos);
12483             if(wrap.getStyle("position") == "static"){
12484                 wrap.position("relative");
12485             }
12486             this.clearPositioning('auto');
12487             wrap.clip();
12488             wrap.dom.appendChild(this.dom);
12489             if(wrapXY){
12490                 wrap.setXY(wrapXY);
12491             }
12492         }
12493         return wrap;
12494     },
12495
12496         /* @private */
12497     fxUnwrap : function(wrap, pos, o){
12498         this.clearPositioning();
12499         this.setPositioning(pos);
12500         if(!o.wrap){
12501             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
12502             wrap.remove();
12503         }
12504     },
12505
12506         /* @private */
12507     getFxRestore : function(){
12508         var st = this.dom.style;
12509         return {pos: this.getPositioning(), width: st.width, height : st.height};
12510     },
12511
12512         /* @private */
12513     afterFx : function(o){
12514         if(o.afterStyle){
12515             this.applyStyles(o.afterStyle);
12516         }
12517         if(o.afterCls){
12518             this.addClass(o.afterCls);
12519         }
12520         if(o.remove === true){
12521             this.remove();
12522         }
12523         Roo.callback(o.callback, o.scope, [this]);
12524         if(!o.concurrent){
12525             this.fxQueue.shift();
12526             this.nextFx();
12527         }
12528     },
12529
12530         /* @private */
12531     getFxEl : function(){ // support for composite element fx
12532         return Roo.get(this.dom);
12533     },
12534
12535         /* @private */
12536     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
12537         animType = animType || 'run';
12538         opt = opt || {};
12539         var anim = Roo.lib.Anim[animType](
12540             this.dom, args,
12541             (opt.duration || defaultDur) || .35,
12542             (opt.easing || defaultEase) || 'easeOut',
12543             function(){
12544                 Roo.callback(cb, this);
12545             },
12546             this
12547         );
12548         opt.anim = anim;
12549         return anim;
12550     }
12551 };
12552
12553 // backwords compat
12554 Roo.Fx.resize = Roo.Fx.scale;
12555
12556 //When included, Roo.Fx is automatically applied to Element so that all basic
12557 //effects are available directly via the Element API
12558 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
12559  * Based on:
12560  * Ext JS Library 1.1.1
12561  * Copyright(c) 2006-2007, Ext JS, LLC.
12562  *
12563  * Originally Released Under LGPL - original licence link has changed is not relivant.
12564  *
12565  * Fork - LGPL
12566  * <script type="text/javascript">
12567  */
12568
12569
12570 /**
12571  * @class Roo.CompositeElement
12572  * Standard composite class. Creates a Roo.Element for every element in the collection.
12573  * <br><br>
12574  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12575  * actions will be performed on all the elements in this collection.</b>
12576  * <br><br>
12577  * All methods return <i>this</i> and can be chained.
12578  <pre><code>
12579  var els = Roo.select("#some-el div.some-class", true);
12580  // or select directly from an existing element
12581  var el = Roo.get('some-el');
12582  el.select('div.some-class', true);
12583
12584  els.setWidth(100); // all elements become 100 width
12585  els.hide(true); // all elements fade out and hide
12586  // or
12587  els.setWidth(100).hide(true);
12588  </code></pre>
12589  */
12590 Roo.CompositeElement = function(els){
12591     this.elements = [];
12592     this.addElements(els);
12593 };
12594 Roo.CompositeElement.prototype = {
12595     isComposite: true,
12596     addElements : function(els){
12597         if(!els) {
12598             return this;
12599         }
12600         if(typeof els == "string"){
12601             els = Roo.Element.selectorFunction(els);
12602         }
12603         var yels = this.elements;
12604         var index = yels.length-1;
12605         for(var i = 0, len = els.length; i < len; i++) {
12606                 yels[++index] = Roo.get(els[i]);
12607         }
12608         return this;
12609     },
12610
12611     /**
12612     * Clears this composite and adds the elements returned by the passed selector.
12613     * @param {String/Array} els A string CSS selector, an array of elements or an element
12614     * @return {CompositeElement} this
12615     */
12616     fill : function(els){
12617         this.elements = [];
12618         this.add(els);
12619         return this;
12620     },
12621
12622     /**
12623     * Filters this composite to only elements that match the passed selector.
12624     * @param {String} selector A string CSS selector
12625     * @param {Boolean} inverse return inverse filter (not matches)
12626     * @return {CompositeElement} this
12627     */
12628     filter : function(selector, inverse){
12629         var els = [];
12630         inverse = inverse || false;
12631         this.each(function(el){
12632             var match = inverse ? !el.is(selector) : el.is(selector);
12633             if(match){
12634                 els[els.length] = el.dom;
12635             }
12636         });
12637         this.fill(els);
12638         return this;
12639     },
12640
12641     invoke : function(fn, args){
12642         var els = this.elements;
12643         for(var i = 0, len = els.length; i < len; i++) {
12644                 Roo.Element.prototype[fn].apply(els[i], args);
12645         }
12646         return this;
12647     },
12648     /**
12649     * Adds elements to this composite.
12650     * @param {String/Array} els A string CSS selector, an array of elements or an element
12651     * @return {CompositeElement} this
12652     */
12653     add : function(els){
12654         if(typeof els == "string"){
12655             this.addElements(Roo.Element.selectorFunction(els));
12656         }else if(els.length !== undefined){
12657             this.addElements(els);
12658         }else{
12659             this.addElements([els]);
12660         }
12661         return this;
12662     },
12663     /**
12664     * Calls the passed function passing (el, this, index) for each element in this composite.
12665     * @param {Function} fn The function to call
12666     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12667     * @return {CompositeElement} this
12668     */
12669     each : function(fn, scope){
12670         var els = this.elements;
12671         for(var i = 0, len = els.length; i < len; i++){
12672             if(fn.call(scope || els[i], els[i], this, i) === false) {
12673                 break;
12674             }
12675         }
12676         return this;
12677     },
12678
12679     /**
12680      * Returns the Element object at the specified index
12681      * @param {Number} index
12682      * @return {Roo.Element}
12683      */
12684     item : function(index){
12685         return this.elements[index] || null;
12686     },
12687
12688     /**
12689      * Returns the first Element
12690      * @return {Roo.Element}
12691      */
12692     first : function(){
12693         return this.item(0);
12694     },
12695
12696     /**
12697      * Returns the last Element
12698      * @return {Roo.Element}
12699      */
12700     last : function(){
12701         return this.item(this.elements.length-1);
12702     },
12703
12704     /**
12705      * Returns the number of elements in this composite
12706      * @return Number
12707      */
12708     getCount : function(){
12709         return this.elements.length;
12710     },
12711
12712     /**
12713      * Returns true if this composite contains the passed element
12714      * @return Boolean
12715      */
12716     contains : function(el){
12717         return this.indexOf(el) !== -1;
12718     },
12719
12720     /**
12721      * Returns true if this composite contains the passed element
12722      * @return Boolean
12723      */
12724     indexOf : function(el){
12725         return this.elements.indexOf(Roo.get(el));
12726     },
12727
12728
12729     /**
12730     * Removes the specified element(s).
12731     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
12732     * or an array of any of those.
12733     * @param {Boolean} removeDom (optional) True to also remove the element from the document
12734     * @return {CompositeElement} this
12735     */
12736     removeElement : function(el, removeDom){
12737         if(el instanceof Array){
12738             for(var i = 0, len = el.length; i < len; i++){
12739                 this.removeElement(el[i]);
12740             }
12741             return this;
12742         }
12743         var index = typeof el == 'number' ? el : this.indexOf(el);
12744         if(index !== -1){
12745             if(removeDom){
12746                 var d = this.elements[index];
12747                 if(d.dom){
12748                     d.remove();
12749                 }else{
12750                     d.parentNode.removeChild(d);
12751                 }
12752             }
12753             this.elements.splice(index, 1);
12754         }
12755         return this;
12756     },
12757
12758     /**
12759     * Replaces the specified element with the passed element.
12760     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
12761     * to replace.
12762     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
12763     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
12764     * @return {CompositeElement} this
12765     */
12766     replaceElement : function(el, replacement, domReplace){
12767         var index = typeof el == 'number' ? el : this.indexOf(el);
12768         if(index !== -1){
12769             if(domReplace){
12770                 this.elements[index].replaceWith(replacement);
12771             }else{
12772                 this.elements.splice(index, 1, Roo.get(replacement))
12773             }
12774         }
12775         return this;
12776     },
12777
12778     /**
12779      * Removes all elements.
12780      */
12781     clear : function(){
12782         this.elements = [];
12783     }
12784 };
12785 (function(){
12786     Roo.CompositeElement.createCall = function(proto, fnName){
12787         if(!proto[fnName]){
12788             proto[fnName] = function(){
12789                 return this.invoke(fnName, arguments);
12790             };
12791         }
12792     };
12793     for(var fnName in Roo.Element.prototype){
12794         if(typeof Roo.Element.prototype[fnName] == "function"){
12795             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
12796         }
12797     };
12798 })();
12799 /*
12800  * Based on:
12801  * Ext JS Library 1.1.1
12802  * Copyright(c) 2006-2007, Ext JS, LLC.
12803  *
12804  * Originally Released Under LGPL - original licence link has changed is not relivant.
12805  *
12806  * Fork - LGPL
12807  * <script type="text/javascript">
12808  */
12809
12810 /**
12811  * @class Roo.CompositeElementLite
12812  * @extends Roo.CompositeElement
12813  * Flyweight composite class. Reuses the same Roo.Element for element operations.
12814  <pre><code>
12815  var els = Roo.select("#some-el div.some-class");
12816  // or select directly from an existing element
12817  var el = Roo.get('some-el');
12818  el.select('div.some-class');
12819
12820  els.setWidth(100); // all elements become 100 width
12821  els.hide(true); // all elements fade out and hide
12822  // or
12823  els.setWidth(100).hide(true);
12824  </code></pre><br><br>
12825  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12826  * actions will be performed on all the elements in this collection.</b>
12827  */
12828 Roo.CompositeElementLite = function(els){
12829     Roo.CompositeElementLite.superclass.constructor.call(this, els);
12830     this.el = new Roo.Element.Flyweight();
12831 };
12832 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
12833     addElements : function(els){
12834         if(els){
12835             if(els instanceof Array){
12836                 this.elements = this.elements.concat(els);
12837             }else{
12838                 var yels = this.elements;
12839                 var index = yels.length-1;
12840                 for(var i = 0, len = els.length; i < len; i++) {
12841                     yels[++index] = els[i];
12842                 }
12843             }
12844         }
12845         return this;
12846     },
12847     invoke : function(fn, args){
12848         var els = this.elements;
12849         var el = this.el;
12850         for(var i = 0, len = els.length; i < len; i++) {
12851             el.dom = els[i];
12852                 Roo.Element.prototype[fn].apply(el, args);
12853         }
12854         return this;
12855     },
12856     /**
12857      * Returns a flyweight Element of the dom element object at the specified index
12858      * @param {Number} index
12859      * @return {Roo.Element}
12860      */
12861     item : function(index){
12862         if(!this.elements[index]){
12863             return null;
12864         }
12865         this.el.dom = this.elements[index];
12866         return this.el;
12867     },
12868
12869     // fixes scope with flyweight
12870     addListener : function(eventName, handler, scope, opt){
12871         var els = this.elements;
12872         for(var i = 0, len = els.length; i < len; i++) {
12873             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
12874         }
12875         return this;
12876     },
12877
12878     /**
12879     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
12880     * passed is the flyweight (shared) Roo.Element instance, so if you require a
12881     * a reference to the dom node, use el.dom.</b>
12882     * @param {Function} fn The function to call
12883     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12884     * @return {CompositeElement} this
12885     */
12886     each : function(fn, scope){
12887         var els = this.elements;
12888         var el = this.el;
12889         for(var i = 0, len = els.length; i < len; i++){
12890             el.dom = els[i];
12891                 if(fn.call(scope || el, el, this, i) === false){
12892                 break;
12893             }
12894         }
12895         return this;
12896     },
12897
12898     indexOf : function(el){
12899         return this.elements.indexOf(Roo.getDom(el));
12900     },
12901
12902     replaceElement : function(el, replacement, domReplace){
12903         var index = typeof el == 'number' ? el : this.indexOf(el);
12904         if(index !== -1){
12905             replacement = Roo.getDom(replacement);
12906             if(domReplace){
12907                 var d = this.elements[index];
12908                 d.parentNode.insertBefore(replacement, d);
12909                 d.parentNode.removeChild(d);
12910             }
12911             this.elements.splice(index, 1, replacement);
12912         }
12913         return this;
12914     }
12915 });
12916 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
12917
12918 /*
12919  * Based on:
12920  * Ext JS Library 1.1.1
12921  * Copyright(c) 2006-2007, Ext JS, LLC.
12922  *
12923  * Originally Released Under LGPL - original licence link has changed is not relivant.
12924  *
12925  * Fork - LGPL
12926  * <script type="text/javascript">
12927  */
12928
12929  
12930
12931 /**
12932  * @class Roo.data.Connection
12933  * @extends Roo.util.Observable
12934  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
12935  * either to a configured URL, or to a URL specified at request time. 
12936  * 
12937  * Requests made by this class are asynchronous, and will return immediately. No data from
12938  * the server will be available to the statement immediately following the {@link #request} call.
12939  * To process returned data, use a callback in the request options object, or an event listener.
12940  * 
12941  * Note: If you are doing a file upload, you will not get a normal response object sent back to
12942  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
12943  * The response object is created using the innerHTML of the IFRAME's document as the responseText
12944  * property and, if present, the IFRAME's XML document as the responseXML property.
12945  * 
12946  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
12947  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
12948  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
12949  * standard DOM methods.
12950  * @constructor
12951  * @param {Object} config a configuration object.
12952  */
12953 Roo.data.Connection = function(config){
12954     Roo.apply(this, config);
12955     this.addEvents({
12956         /**
12957          * @event beforerequest
12958          * Fires before a network request is made to retrieve a data object.
12959          * @param {Connection} conn This Connection object.
12960          * @param {Object} options The options config object passed to the {@link #request} method.
12961          */
12962         "beforerequest" : true,
12963         /**
12964          * @event requestcomplete
12965          * Fires if the request was successfully completed.
12966          * @param {Connection} conn This Connection object.
12967          * @param {Object} response The XHR object containing the response data.
12968          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12969          * @param {Object} options The options config object passed to the {@link #request} method.
12970          */
12971         "requestcomplete" : true,
12972         /**
12973          * @event requestexception
12974          * Fires if an error HTTP status was returned from the server.
12975          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
12976          * @param {Connection} conn This Connection object.
12977          * @param {Object} response The XHR object containing the response data.
12978          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12979          * @param {Object} options The options config object passed to the {@link #request} method.
12980          */
12981         "requestexception" : true
12982     });
12983     Roo.data.Connection.superclass.constructor.call(this);
12984 };
12985
12986 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
12987     /**
12988      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12989      */
12990     /**
12991      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12992      * extra parameters to each request made by this object. (defaults to undefined)
12993      */
12994     /**
12995      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12996      *  to each request made by this object. (defaults to undefined)
12997      */
12998     /**
12999      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
13000      */
13001     /**
13002      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
13003      */
13004     timeout : 30000,
13005     /**
13006      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
13007      * @type Boolean
13008      */
13009     autoAbort:false,
13010
13011     /**
13012      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
13013      * @type Boolean
13014      */
13015     disableCaching: true,
13016
13017     /**
13018      * Sends an HTTP request to a remote server.
13019      * @param {Object} options An object which may contain the following properties:<ul>
13020      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
13021      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
13022      * request, a url encoded string or a function to call to get either.</li>
13023      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
13024      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
13025      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
13026      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
13027      * <li>options {Object} The parameter to the request call.</li>
13028      * <li>success {Boolean} True if the request succeeded.</li>
13029      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
13030      * </ul></li>
13031      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
13032      * The callback is passed the following parameters:<ul>
13033      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
13034      * <li>options {Object} The parameter to the request call.</li>
13035      * </ul></li>
13036      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
13037      * The callback is passed the following parameters:<ul>
13038      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
13039      * <li>options {Object} The parameter to the request call.</li>
13040      * </ul></li>
13041      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
13042      * for the callback function. Defaults to the browser window.</li>
13043      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
13044      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
13045      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
13046      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
13047      * params for the post data. Any params will be appended to the URL.</li>
13048      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
13049      * </ul>
13050      * @return {Number} transactionId
13051      */
13052     request : function(o){
13053         if(this.fireEvent("beforerequest", this, o) !== false){
13054             var p = o.params;
13055
13056             if(typeof p == "function"){
13057                 p = p.call(o.scope||window, o);
13058             }
13059             if(typeof p == "object"){
13060                 p = Roo.urlEncode(o.params);
13061             }
13062             if(this.extraParams){
13063                 var extras = Roo.urlEncode(this.extraParams);
13064                 p = p ? (p + '&' + extras) : extras;
13065             }
13066
13067             var url = o.url || this.url;
13068             if(typeof url == 'function'){
13069                 url = url.call(o.scope||window, o);
13070             }
13071
13072             if(o.form){
13073                 var form = Roo.getDom(o.form);
13074                 url = url || form.action;
13075
13076                 var enctype = form.getAttribute("enctype");
13077                 
13078                 if (o.formData) {
13079                     return this.doFormDataUpload(o, url);
13080                 }
13081                 
13082                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
13083                     return this.doFormUpload(o, p, url);
13084                 }
13085                 var f = Roo.lib.Ajax.serializeForm(form);
13086                 p = p ? (p + '&' + f) : f;
13087             }
13088             
13089             if (!o.form && o.formData) {
13090                 o.formData = o.formData === true ? new FormData() : o.formData;
13091                 for (var k in o.params) {
13092                     o.formData.append(k,o.params[k]);
13093                 }
13094                     
13095                 return this.doFormDataUpload(o, url);
13096             }
13097             
13098
13099             var hs = o.headers;
13100             if(this.defaultHeaders){
13101                 hs = Roo.apply(hs || {}, this.defaultHeaders);
13102                 if(!o.headers){
13103                     o.headers = hs;
13104                 }
13105             }
13106
13107             var cb = {
13108                 success: this.handleResponse,
13109                 failure: this.handleFailure,
13110                 scope: this,
13111                 argument: {options: o},
13112                 timeout : o.timeout || this.timeout
13113             };
13114
13115             var method = o.method||this.method||(p ? "POST" : "GET");
13116
13117             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
13118                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
13119             }
13120
13121             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
13122                 if(o.autoAbort){
13123                     this.abort();
13124                 }
13125             }else if(this.autoAbort !== false){
13126                 this.abort();
13127             }
13128
13129             if((method == 'GET' && p) || o.xmlData){
13130                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
13131                 p = '';
13132             }
13133             Roo.lib.Ajax.useDefaultHeader = typeof(o.headers) == 'undefined' || typeof(o.headers['Content-Type']) == 'undefined';
13134             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
13135             Roo.lib.Ajax.useDefaultHeader == true;
13136             return this.transId;
13137         }else{
13138             Roo.callback(o.callback, o.scope, [o, null, null]);
13139             return null;
13140         }
13141     },
13142
13143     /**
13144      * Determine whether this object has a request outstanding.
13145      * @param {Number} transactionId (Optional) defaults to the last transaction
13146      * @return {Boolean} True if there is an outstanding request.
13147      */
13148     isLoading : function(transId){
13149         if(transId){
13150             return Roo.lib.Ajax.isCallInProgress(transId);
13151         }else{
13152             return this.transId ? true : false;
13153         }
13154     },
13155
13156     /**
13157      * Aborts any outstanding request.
13158      * @param {Number} transactionId (Optional) defaults to the last transaction
13159      */
13160     abort : function(transId){
13161         if(transId || this.isLoading()){
13162             Roo.lib.Ajax.abort(transId || this.transId);
13163         }
13164     },
13165
13166     // private
13167     handleResponse : function(response){
13168         this.transId = false;
13169         var options = response.argument.options;
13170         response.argument = options ? options.argument : null;
13171         this.fireEvent("requestcomplete", this, response, options);
13172         Roo.callback(options.success, options.scope, [response, options]);
13173         Roo.callback(options.callback, options.scope, [options, true, response]);
13174     },
13175
13176     // private
13177     handleFailure : function(response, e){
13178         this.transId = false;
13179         var options = response.argument.options;
13180         response.argument = options ? options.argument : null;
13181         this.fireEvent("requestexception", this, response, options, e);
13182         Roo.callback(options.failure, options.scope, [response, options]);
13183         Roo.callback(options.callback, options.scope, [options, false, response]);
13184     },
13185
13186     // private
13187     doFormUpload : function(o, ps, url){
13188         var id = Roo.id();
13189         var frame = document.createElement('iframe');
13190         frame.id = id;
13191         frame.name = id;
13192         frame.className = 'x-hidden';
13193         if(Roo.isIE){
13194             frame.src = Roo.SSL_SECURE_URL;
13195         }
13196         document.body.appendChild(frame);
13197
13198         if(Roo.isIE){
13199            document.frames[id].name = id;
13200         }
13201
13202         var form = Roo.getDom(o.form);
13203         form.target = id;
13204         form.method = 'POST';
13205         form.enctype = form.encoding = 'multipart/form-data';
13206         if(url){
13207             form.action = url;
13208         }
13209
13210         var hiddens, hd;
13211         if(ps){ // add dynamic params
13212             hiddens = [];
13213             ps = Roo.urlDecode(ps, false);
13214             for(var k in ps){
13215                 if(ps.hasOwnProperty(k)){
13216                     hd = document.createElement('input');
13217                     hd.type = 'hidden';
13218                     hd.name = k;
13219                     hd.value = ps[k];
13220                     form.appendChild(hd);
13221                     hiddens.push(hd);
13222                 }
13223             }
13224         }
13225
13226         function cb(){
13227             var r = {  // bogus response object
13228                 responseText : '',
13229                 responseXML : null
13230             };
13231
13232             r.argument = o ? o.argument : null;
13233
13234             try { //
13235                 var doc;
13236                 if(Roo.isIE){
13237                     doc = frame.contentWindow.document;
13238                 }else {
13239                     doc = (frame.contentDocument || window.frames[id].document);
13240                 }
13241                 if(doc && doc.body){
13242                     r.responseText = doc.body.innerHTML;
13243                 }
13244                 if(doc && doc.XMLDocument){
13245                     r.responseXML = doc.XMLDocument;
13246                 }else {
13247                     r.responseXML = doc;
13248                 }
13249             }
13250             catch(e) {
13251                 // ignore
13252             }
13253
13254             Roo.EventManager.removeListener(frame, 'load', cb, this);
13255
13256             this.fireEvent("requestcomplete", this, r, o);
13257             Roo.callback(o.success, o.scope, [r, o]);
13258             Roo.callback(o.callback, o.scope, [o, true, r]);
13259
13260             setTimeout(function(){document.body.removeChild(frame);}, 100);
13261         }
13262
13263         Roo.EventManager.on(frame, 'load', cb, this);
13264         form.submit();
13265
13266         if(hiddens){ // remove dynamic params
13267             for(var i = 0, len = hiddens.length; i < len; i++){
13268                 form.removeChild(hiddens[i]);
13269             }
13270         }
13271     },
13272     // this is a 'formdata version???'
13273     
13274     
13275     doFormDataUpload : function(o,  url)
13276     {
13277         var formData;
13278         if (o.form) {
13279             var form =  Roo.getDom(o.form);
13280             form.enctype = form.encoding = 'multipart/form-data';
13281             formData = o.formData === true ? new FormData(form) : o.formData;
13282         } else {
13283             formData = o.formData === true ? new FormData() : o.formData;
13284         }
13285         
13286       
13287         var cb = {
13288             success: this.handleResponse,
13289             failure: this.handleFailure,
13290             scope: this,
13291             argument: {options: o},
13292             timeout : o.timeout || this.timeout
13293         };
13294  
13295         if(typeof o.autoAbort == 'boolean'){ // options gets top priority
13296             if(o.autoAbort){
13297                 this.abort();
13298             }
13299         }else if(this.autoAbort !== false){
13300             this.abort();
13301         }
13302
13303         //Roo.lib.Ajax.defaultPostHeader = null;
13304         Roo.lib.Ajax.useDefaultHeader = false;
13305         this.transId = Roo.lib.Ajax.request( "POST", url, cb,  formData, o);
13306         Roo.lib.Ajax.useDefaultHeader = true;
13307  
13308          
13309     }
13310     
13311 });
13312 /*
13313  * Based on:
13314  * Ext JS Library 1.1.1
13315  * Copyright(c) 2006-2007, Ext JS, LLC.
13316  *
13317  * Originally Released Under LGPL - original licence link has changed is not relivant.
13318  *
13319  * Fork - LGPL
13320  * <script type="text/javascript">
13321  */
13322  
13323 /**
13324  * Global Ajax request class.
13325  * 
13326  * @class Roo.Ajax
13327  * @extends Roo.data.Connection
13328  * @static
13329  * 
13330  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
13331  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
13332  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
13333  * @cfg {String} method (Optional)  The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
13334  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
13335  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
13336  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
13337  */
13338 Roo.Ajax = new Roo.data.Connection({
13339     // fix up the docs
13340     /**
13341      * @scope Roo.Ajax
13342      * @type {Boolear} 
13343      */
13344     autoAbort : false,
13345
13346     /**
13347      * Serialize the passed form into a url encoded string
13348      * @scope Roo.Ajax
13349      * @param {String/HTMLElement} form
13350      * @return {String}
13351      */
13352     serializeForm : function(form){
13353         return Roo.lib.Ajax.serializeForm(form);
13354     }
13355 });/*
13356  * Based on:
13357  * Ext JS Library 1.1.1
13358  * Copyright(c) 2006-2007, Ext JS, LLC.
13359  *
13360  * Originally Released Under LGPL - original licence link has changed is not relivant.
13361  *
13362  * Fork - LGPL
13363  * <script type="text/javascript">
13364  */
13365
13366  
13367 /**
13368  * @class Roo.UpdateManager
13369  * @extends Roo.util.Observable
13370  * Provides AJAX-style update for Element object.<br><br>
13371  * Usage:<br>
13372  * <pre><code>
13373  * // Get it from a Roo.Element object
13374  * var el = Roo.get("foo");
13375  * var mgr = el.getUpdateManager();
13376  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
13377  * ...
13378  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
13379  * <br>
13380  * // or directly (returns the same UpdateManager instance)
13381  * var mgr = new Roo.UpdateManager("myElementId");
13382  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
13383  * mgr.on("update", myFcnNeedsToKnow);
13384  * <br>
13385    // short handed call directly from the element object
13386    Roo.get("foo").load({
13387         url: "bar.php",
13388         scripts:true,
13389         params: "for=bar",
13390         text: "Loading Foo..."
13391    });
13392  * </code></pre>
13393  * @constructor
13394  * Create new UpdateManager directly.
13395  * @param {String/HTMLElement/Roo.Element} el The element to update
13396  * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already has an UpdateManager and if it does it returns the same instance. This will skip that check (useful for extending this class).
13397  */
13398 Roo.UpdateManager = function(el, forceNew){
13399     el = Roo.get(el);
13400     if(!forceNew && el.updateManager){
13401         return el.updateManager;
13402     }
13403     /**
13404      * The Element object
13405      * @type Roo.Element
13406      */
13407     this.el = el;
13408     /**
13409      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
13410      * @type String
13411      */
13412     this.defaultUrl = null;
13413
13414     this.addEvents({
13415         /**
13416          * @event beforeupdate
13417          * Fired before an update is made, return false from your handler and the update is cancelled.
13418          * @param {Roo.Element} el
13419          * @param {String/Object/Function} url
13420          * @param {String/Object} params
13421          */
13422         "beforeupdate": true,
13423         /**
13424          * @event update
13425          * Fired after successful update is made.
13426          * @param {Roo.Element} el
13427          * @param {Object} oResponseObject The response Object
13428          */
13429         "update": true,
13430         /**
13431          * @event failure
13432          * Fired on update failure.
13433          * @param {Roo.Element} el
13434          * @param {Object} oResponseObject The response Object
13435          */
13436         "failure": true
13437     });
13438     var d = Roo.UpdateManager.defaults;
13439     /**
13440      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
13441      * @type String
13442      */
13443     this.sslBlankUrl = d.sslBlankUrl;
13444     /**
13445      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
13446      * @type Boolean
13447      */
13448     this.disableCaching = d.disableCaching;
13449     /**
13450      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
13451      * @type String
13452      */
13453     this.indicatorText = d.indicatorText;
13454     /**
13455      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
13456      * @type String
13457      */
13458     this.showLoadIndicator = d.showLoadIndicator;
13459     /**
13460      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
13461      * @type Number
13462      */
13463     this.timeout = d.timeout;
13464
13465     /**
13466      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
13467      * @type Boolean
13468      */
13469     this.loadScripts = d.loadScripts;
13470
13471     /**
13472      * Transaction object of current executing transaction
13473      */
13474     this.transaction = null;
13475
13476     /**
13477      * @private
13478      */
13479     this.autoRefreshProcId = null;
13480     /**
13481      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
13482      * @type Function
13483      */
13484     this.refreshDelegate = this.refresh.createDelegate(this);
13485     /**
13486      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
13487      * @type Function
13488      */
13489     this.updateDelegate = this.update.createDelegate(this);
13490     /**
13491      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
13492      * @type Function
13493      */
13494     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
13495     /**
13496      * @private
13497      */
13498     this.successDelegate = this.processSuccess.createDelegate(this);
13499     /**
13500      * @private
13501      */
13502     this.failureDelegate = this.processFailure.createDelegate(this);
13503
13504     if(!this.renderer){
13505      /**
13506       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
13507       */
13508     this.renderer = new Roo.UpdateManager.BasicRenderer();
13509     }
13510     
13511     Roo.UpdateManager.superclass.constructor.call(this);
13512 };
13513
13514 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
13515     /**
13516      * Get the Element this UpdateManager is bound to
13517      * @return {Roo.Element} The element
13518      */
13519     getEl : function(){
13520         return this.el;
13521     },
13522     /**
13523      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
13524      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
13525 <pre><code>
13526 um.update({<br/>
13527     url: "your-url.php",<br/>
13528     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
13529     callback: yourFunction,<br/>
13530     scope: yourObject, //(optional scope)  <br/>
13531     discardUrl: false, <br/>
13532     nocache: false,<br/>
13533     text: "Loading...",<br/>
13534     timeout: 30,<br/>
13535     scripts: false<br/>
13536 });
13537 </code></pre>
13538      * The only required property is url. The optional properties nocache, text and scripts
13539      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
13540      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
13541      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
13542      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
13543      */
13544     update : function(url, params, callback, discardUrl){
13545         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
13546             var method = this.method,
13547                 cfg;
13548             if(typeof url == "object"){ // must be config object
13549                 cfg = url;
13550                 url = cfg.url;
13551                 params = params || cfg.params;
13552                 callback = callback || cfg.callback;
13553                 discardUrl = discardUrl || cfg.discardUrl;
13554                 if(callback && cfg.scope){
13555                     callback = callback.createDelegate(cfg.scope);
13556                 }
13557                 if(typeof cfg.method != "undefined"){method = cfg.method;};
13558                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
13559                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
13560                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
13561                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
13562             }
13563             this.showLoading();
13564             if(!discardUrl){
13565                 this.defaultUrl = url;
13566             }
13567             if(typeof url == "function"){
13568                 url = url.call(this);
13569             }
13570
13571             method = method || (params ? "POST" : "GET");
13572             if(method == "GET"){
13573                 url = this.prepareUrl(url);
13574             }
13575
13576             var o = Roo.apply(cfg ||{}, {
13577                 url : url,
13578                 params: params,
13579                 success: this.successDelegate,
13580                 failure: this.failureDelegate,
13581                 callback: undefined,
13582                 timeout: (this.timeout*1000),
13583                 argument: {"url": url, "form": null, "callback": callback, "params": params}
13584             });
13585             Roo.log("updated manager called with timeout of " + o.timeout);
13586             this.transaction = Roo.Ajax.request(o);
13587         }
13588     },
13589
13590     /**
13591      * Performs an async form post, updating this element with the response. If the form has the attribute enctype="multipart/form-data", it assumes it's a file upload.
13592      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
13593      * @param {String/HTMLElement} form The form Id or form element
13594      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
13595      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
13596      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
13597      */
13598     formUpdate : function(form, url, reset, callback){
13599         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
13600             if(typeof url == "function"){
13601                 url = url.call(this);
13602             }
13603             form = Roo.getDom(form);
13604             this.transaction = Roo.Ajax.request({
13605                 form: form,
13606                 url:url,
13607                 success: this.successDelegate,
13608                 failure: this.failureDelegate,
13609                 timeout: (this.timeout*1000),
13610                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
13611             });
13612             this.showLoading.defer(1, this);
13613         }
13614     },
13615
13616     /**
13617      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
13618      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13619      */
13620     refresh : function(callback){
13621         if(this.defaultUrl == null){
13622             return;
13623         }
13624         this.update(this.defaultUrl, null, callback, true);
13625     },
13626
13627     /**
13628      * Set this element to auto refresh.
13629      * @param {Number} interval How often to update (in seconds).
13630      * @param {String/Function} url (optional) The url for this request or a function to call to get the url (Defaults to the last used url)
13631      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
13632      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13633      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
13634      */
13635     startAutoRefresh : function(interval, url, params, callback, refreshNow){
13636         if(refreshNow){
13637             this.update(url || this.defaultUrl, params, callback, true);
13638         }
13639         if(this.autoRefreshProcId){
13640             clearInterval(this.autoRefreshProcId);
13641         }
13642         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
13643     },
13644
13645     /**
13646      * Stop auto refresh on this element.
13647      */
13648      stopAutoRefresh : function(){
13649         if(this.autoRefreshProcId){
13650             clearInterval(this.autoRefreshProcId);
13651             delete this.autoRefreshProcId;
13652         }
13653     },
13654
13655     isAutoRefreshing : function(){
13656        return this.autoRefreshProcId ? true : false;
13657     },
13658     /**
13659      * Called to update the element to "Loading" state. Override to perform custom action.
13660      */
13661     showLoading : function(){
13662         if(this.showLoadIndicator){
13663             this.el.update(this.indicatorText);
13664         }
13665     },
13666
13667     /**
13668      * Adds unique parameter to query string if disableCaching = true
13669      * @private
13670      */
13671     prepareUrl : function(url){
13672         if(this.disableCaching){
13673             var append = "_dc=" + (new Date().getTime());
13674             if(url.indexOf("?") !== -1){
13675                 url += "&" + append;
13676             }else{
13677                 url += "?" + append;
13678             }
13679         }
13680         return url;
13681     },
13682
13683     /**
13684      * @private
13685      */
13686     processSuccess : function(response){
13687         this.transaction = null;
13688         if(response.argument.form && response.argument.reset){
13689             try{ // put in try/catch since some older FF releases had problems with this
13690                 response.argument.form.reset();
13691             }catch(e){}
13692         }
13693         if(this.loadScripts){
13694             this.renderer.render(this.el, response, this,
13695                 this.updateComplete.createDelegate(this, [response]));
13696         }else{
13697             this.renderer.render(this.el, response, this);
13698             this.updateComplete(response);
13699         }
13700     },
13701
13702     updateComplete : function(response){
13703         this.fireEvent("update", this.el, response);
13704         if(typeof response.argument.callback == "function"){
13705             response.argument.callback(this.el, true, response);
13706         }
13707     },
13708
13709     /**
13710      * @private
13711      */
13712     processFailure : function(response){
13713         this.transaction = null;
13714         this.fireEvent("failure", this.el, response);
13715         if(typeof response.argument.callback == "function"){
13716             response.argument.callback(this.el, false, response);
13717         }
13718     },
13719
13720     /**
13721      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
13722      * @param {Object} renderer The object implementing the render() method
13723      */
13724     setRenderer : function(renderer){
13725         this.renderer = renderer;
13726     },
13727
13728     getRenderer : function(){
13729        return this.renderer;
13730     },
13731
13732     /**
13733      * Set the defaultUrl used for updates
13734      * @param {String/Function} defaultUrl The url or a function to call to get the url
13735      */
13736     setDefaultUrl : function(defaultUrl){
13737         this.defaultUrl = defaultUrl;
13738     },
13739
13740     /**
13741      * Aborts the executing transaction
13742      */
13743     abort : function(){
13744         if(this.transaction){
13745             Roo.Ajax.abort(this.transaction);
13746         }
13747     },
13748
13749     /**
13750      * Returns true if an update is in progress
13751      * @return {Boolean}
13752      */
13753     isUpdating : function(){
13754         if(this.transaction){
13755             return Roo.Ajax.isLoading(this.transaction);
13756         }
13757         return false;
13758     }
13759 });
13760
13761 /**
13762  * @class Roo.UpdateManager.defaults
13763  * @static (not really - but it helps the doc tool)
13764  * The defaults collection enables customizing the default properties of UpdateManager
13765  */
13766    Roo.UpdateManager.defaults = {
13767        /**
13768          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
13769          * @type Number
13770          */
13771          timeout : 30,
13772
13773          /**
13774          * True to process scripts by default (Defaults to false).
13775          * @type Boolean
13776          */
13777         loadScripts : false,
13778
13779         /**
13780         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
13781         * @type String
13782         */
13783         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
13784         /**
13785          * Whether to append unique parameter on get request to disable caching (Defaults to false).
13786          * @type Boolean
13787          */
13788         disableCaching : false,
13789         /**
13790          * Whether to show indicatorText when loading (Defaults to true).
13791          * @type Boolean
13792          */
13793         showLoadIndicator : true,
13794         /**
13795          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
13796          * @type String
13797          */
13798         indicatorText : '<div class="loading-indicator">Loading...</div>'
13799    };
13800
13801 /**
13802  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
13803  *Usage:
13804  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
13805  * @param {String/HTMLElement/Roo.Element} el The element to update
13806  * @param {String} url The url
13807  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
13808  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
13809  * @static
13810  * @deprecated
13811  * @member Roo.UpdateManager
13812  */
13813 Roo.UpdateManager.updateElement = function(el, url, params, options){
13814     var um = Roo.get(el, true).getUpdateManager();
13815     Roo.apply(um, options);
13816     um.update(url, params, options ? options.callback : null);
13817 };
13818 // alias for backwards compat
13819 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
13820 /**
13821  * @class Roo.UpdateManager.BasicRenderer
13822  * Default Content renderer. Updates the elements innerHTML with the responseText.
13823  */
13824 Roo.UpdateManager.BasicRenderer = function(){};
13825
13826 Roo.UpdateManager.BasicRenderer.prototype = {
13827     /**
13828      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
13829      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
13830      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
13831      * @param {Roo.Element} el The element being rendered
13832      * @param {Object} response The YUI Connect response object
13833      * @param {UpdateManager} updateManager The calling update manager
13834      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
13835      */
13836      render : function(el, response, updateManager, callback){
13837         el.update(response.responseText, updateManager.loadScripts, callback);
13838     }
13839 };
13840 /*
13841  * Based on:
13842  * Roo JS
13843  * (c)) Alan Knowles
13844  * Licence : LGPL
13845  */
13846
13847
13848 /**
13849  * @class Roo.DomTemplate
13850  * @extends Roo.Template
13851  * An effort at a dom based template engine..
13852  *
13853  * Similar to XTemplate, except it uses dom parsing to create the template..
13854  *
13855  * Supported features:
13856  *
13857  *  Tags:
13858
13859 <pre><code>
13860       {a_variable} - output encoded.
13861       {a_variable.format:("Y-m-d")} - call a method on the variable
13862       {a_variable:raw} - unencoded output
13863       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
13864       {a_variable:this.method_on_template(...)} - call a method on the template object.
13865  
13866 </code></pre>
13867  *  The tpl tag:
13868 <pre><code>
13869         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
13870         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
13871         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
13872         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
13873   
13874 </code></pre>
13875  *      
13876  */
13877 Roo.DomTemplate = function()
13878 {
13879      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
13880      if (this.html) {
13881         this.compile();
13882      }
13883 };
13884
13885
13886 Roo.extend(Roo.DomTemplate, Roo.Template, {
13887     /**
13888      * id counter for sub templates.
13889      */
13890     id : 0,
13891     /**
13892      * flag to indicate if dom parser is inside a pre,
13893      * it will strip whitespace if not.
13894      */
13895     inPre : false,
13896     
13897     /**
13898      * The various sub templates
13899      */
13900     tpls : false,
13901     
13902     
13903     
13904     /**
13905      *
13906      * basic tag replacing syntax
13907      * WORD:WORD()
13908      *
13909      * // you can fake an object call by doing this
13910      *  x.t:(test,tesT) 
13911      * 
13912      */
13913     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
13914     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
13915     
13916     iterChild : function (node, method) {
13917         
13918         var oldPre = this.inPre;
13919         if (node.tagName == 'PRE') {
13920             this.inPre = true;
13921         }
13922         for( var i = 0; i < node.childNodes.length; i++) {
13923             method.call(this, node.childNodes[i]);
13924         }
13925         this.inPre = oldPre;
13926     },
13927     
13928     
13929     
13930     /**
13931      * compile the template
13932      *
13933      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
13934      *
13935      */
13936     compile: function()
13937     {
13938         var s = this.html;
13939         
13940         // covert the html into DOM...
13941         var doc = false;
13942         var div =false;
13943         try {
13944             doc = document.implementation.createHTMLDocument("");
13945             doc.documentElement.innerHTML =   this.html  ;
13946             div = doc.documentElement;
13947         } catch (e) {
13948             // old IE... - nasty -- it causes all sorts of issues.. with
13949             // images getting pulled from server..
13950             div = document.createElement('div');
13951             div.innerHTML = this.html;
13952         }
13953         //doc.documentElement.innerHTML = htmlBody
13954          
13955         
13956         
13957         this.tpls = [];
13958         var _t = this;
13959         this.iterChild(div, function(n) {_t.compileNode(n, true); });
13960         
13961         var tpls = this.tpls;
13962         
13963         // create a top level template from the snippet..
13964         
13965         //Roo.log(div.innerHTML);
13966         
13967         var tpl = {
13968             uid : 'master',
13969             id : this.id++,
13970             attr : false,
13971             value : false,
13972             body : div.innerHTML,
13973             
13974             forCall : false,
13975             execCall : false,
13976             dom : div,
13977             isTop : true
13978             
13979         };
13980         tpls.unshift(tpl);
13981         
13982         
13983         // compile them...
13984         this.tpls = [];
13985         Roo.each(tpls, function(tp){
13986             this.compileTpl(tp);
13987             this.tpls[tp.id] = tp;
13988         }, this);
13989         
13990         this.master = tpls[0];
13991         return this;
13992         
13993         
13994     },
13995     
13996     compileNode : function(node, istop) {
13997         // test for
13998         //Roo.log(node);
13999         
14000         
14001         // skip anything not a tag..
14002         if (node.nodeType != 1) {
14003             if (node.nodeType == 3 && !this.inPre) {
14004                 // reduce white space..
14005                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
14006                 
14007             }
14008             return;
14009         }
14010         
14011         var tpl = {
14012             uid : false,
14013             id : false,
14014             attr : false,
14015             value : false,
14016             body : '',
14017             
14018             forCall : false,
14019             execCall : false,
14020             dom : false,
14021             isTop : istop
14022             
14023             
14024         };
14025         
14026         
14027         switch(true) {
14028             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
14029             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
14030             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
14031             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
14032             // no default..
14033         }
14034         
14035         
14036         if (!tpl.attr) {
14037             // just itterate children..
14038             this.iterChild(node,this.compileNode);
14039             return;
14040         }
14041         tpl.uid = this.id++;
14042         tpl.value = node.getAttribute('roo-' +  tpl.attr);
14043         node.removeAttribute('roo-'+ tpl.attr);
14044         if (tpl.attr != 'name') {
14045             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
14046             node.parentNode.replaceChild(placeholder,  node);
14047         } else {
14048             
14049             var placeholder =  document.createElement('span');
14050             placeholder.className = 'roo-tpl-' + tpl.value;
14051             node.parentNode.replaceChild(placeholder,  node);
14052         }
14053         
14054         // parent now sees '{domtplXXXX}
14055         this.iterChild(node,this.compileNode);
14056         
14057         // we should now have node body...
14058         var div = document.createElement('div');
14059         div.appendChild(node);
14060         tpl.dom = node;
14061         // this has the unfortunate side effect of converting tagged attributes
14062         // eg. href="{...}" into %7C...%7D
14063         // this has been fixed by searching for those combo's although it's a bit hacky..
14064         
14065         
14066         tpl.body = div.innerHTML;
14067         
14068         
14069          
14070         tpl.id = tpl.uid;
14071         switch(tpl.attr) {
14072             case 'for' :
14073                 switch (tpl.value) {
14074                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
14075                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
14076                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
14077                 }
14078                 break;
14079             
14080             case 'exec':
14081                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
14082                 break;
14083             
14084             case 'if':     
14085                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
14086                 break;
14087             
14088             case 'name':
14089                 tpl.id  = tpl.value; // replace non characters???
14090                 break;
14091             
14092         }
14093         
14094         
14095         this.tpls.push(tpl);
14096         
14097         
14098         
14099     },
14100     
14101     
14102     
14103     
14104     /**
14105      * Compile a segment of the template into a 'sub-template'
14106      *
14107      * 
14108      * 
14109      *
14110      */
14111     compileTpl : function(tpl)
14112     {
14113         var fm = Roo.util.Format;
14114         var useF = this.disableFormats !== true;
14115         
14116         var sep = Roo.isGecko ? "+\n" : ",\n";
14117         
14118         var undef = function(str) {
14119             Roo.debug && Roo.log("Property not found :"  + str);
14120             return '';
14121         };
14122           
14123         //Roo.log(tpl.body);
14124         
14125         
14126         
14127         var fn = function(m, lbrace, name, format, args)
14128         {
14129             //Roo.log("ARGS");
14130             //Roo.log(arguments);
14131             args = args ? args.replace(/\\'/g,"'") : args;
14132             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
14133             if (typeof(format) == 'undefined') {
14134                 format =  'htmlEncode'; 
14135             }
14136             if (format == 'raw' ) {
14137                 format = false;
14138             }
14139             
14140             if(name.substr(0, 6) == 'domtpl'){
14141                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
14142             }
14143             
14144             // build an array of options to determine if value is undefined..
14145             
14146             // basically get 'xxxx.yyyy' then do
14147             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
14148             //    (function () { Roo.log("Property not found"); return ''; })() :
14149             //    ......
14150             
14151             var udef_ar = [];
14152             var lookfor = '';
14153             Roo.each(name.split('.'), function(st) {
14154                 lookfor += (lookfor.length ? '.': '') + st;
14155                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
14156             });
14157             
14158             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
14159             
14160             
14161             if(format && useF){
14162                 
14163                 args = args ? ',' + args : "";
14164                  
14165                 if(format.substr(0, 5) != "this."){
14166                     format = "fm." + format + '(';
14167                 }else{
14168                     format = 'this.call("'+ format.substr(5) + '", ';
14169                     args = ", values";
14170                 }
14171                 
14172                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
14173             }
14174              
14175             if (args && args.length) {
14176                 // called with xxyx.yuu:(test,test)
14177                 // change to ()
14178                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
14179             }
14180             // raw.. - :raw modifier..
14181             return "'"+ sep + udef_st  + name + ")"+sep+"'";
14182             
14183         };
14184         var body;
14185         // branched to use + in gecko and [].join() in others
14186         if(Roo.isGecko){
14187             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
14188                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
14189                     "';};};";
14190         }else{
14191             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
14192             body.push(tpl.body.replace(/(\r\n|\n)/g,
14193                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
14194             body.push("'].join('');};};");
14195             body = body.join('');
14196         }
14197         
14198         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
14199        
14200         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
14201         eval(body);
14202         
14203         return this;
14204     },
14205      
14206     /**
14207      * same as applyTemplate, except it's done to one of the subTemplates
14208      * when using named templates, you can do:
14209      *
14210      * var str = pl.applySubTemplate('your-name', values);
14211      *
14212      * 
14213      * @param {Number} id of the template
14214      * @param {Object} values to apply to template
14215      * @param {Object} parent (normaly the instance of this object)
14216      */
14217     applySubTemplate : function(id, values, parent)
14218     {
14219         
14220         
14221         var t = this.tpls[id];
14222         
14223         
14224         try { 
14225             if(t.ifCall && !t.ifCall.call(this, values, parent)){
14226                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
14227                 return '';
14228             }
14229         } catch(e) {
14230             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
14231             Roo.log(values);
14232           
14233             return '';
14234         }
14235         try { 
14236             
14237             if(t.execCall && t.execCall.call(this, values, parent)){
14238                 return '';
14239             }
14240         } catch(e) {
14241             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
14242             Roo.log(values);
14243             return '';
14244         }
14245         
14246         try {
14247             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
14248             parent = t.target ? values : parent;
14249             if(t.forCall && vs instanceof Array){
14250                 var buf = [];
14251                 for(var i = 0, len = vs.length; i < len; i++){
14252                     try {
14253                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
14254                     } catch (e) {
14255                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
14256                         Roo.log(e.body);
14257                         //Roo.log(t.compiled);
14258                         Roo.log(vs[i]);
14259                     }   
14260                 }
14261                 return buf.join('');
14262             }
14263         } catch (e) {
14264             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
14265             Roo.log(values);
14266             return '';
14267         }
14268         try {
14269             return t.compiled.call(this, vs, parent);
14270         } catch (e) {
14271             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
14272             Roo.log(e.body);
14273             //Roo.log(t.compiled);
14274             Roo.log(values);
14275             return '';
14276         }
14277     },
14278
14279    
14280
14281     applyTemplate : function(values){
14282         return this.master.compiled.call(this, values, {});
14283         //var s = this.subs;
14284     },
14285
14286     apply : function(){
14287         return this.applyTemplate.apply(this, arguments);
14288     }
14289
14290  });
14291
14292 Roo.DomTemplate.from = function(el){
14293     el = Roo.getDom(el);
14294     return new Roo.Domtemplate(el.value || el.innerHTML);
14295 };/*
14296  * Based on:
14297  * Ext JS Library 1.1.1
14298  * Copyright(c) 2006-2007, Ext JS, LLC.
14299  *
14300  * Originally Released Under LGPL - original licence link has changed is not relivant.
14301  *
14302  * Fork - LGPL
14303  * <script type="text/javascript">
14304  */
14305
14306 /**
14307  * @class Roo.util.DelayedTask
14308  * Provides a convenient method of performing setTimeout where a new
14309  * timeout cancels the old timeout. An example would be performing validation on a keypress.
14310  * You can use this class to buffer
14311  * the keypress events for a certain number of milliseconds, and perform only if they stop
14312  * for that amount of time.
14313  * @constructor The parameters to this constructor serve as defaults and are not required.
14314  * @param {Function} fn (optional) The default function to timeout
14315  * @param {Object} scope (optional) The default scope of that timeout
14316  * @param {Array} args (optional) The default Array of arguments
14317  */
14318 Roo.util.DelayedTask = function(fn, scope, args){
14319     var id = null, d, t;
14320
14321     var call = function(){
14322         var now = new Date().getTime();
14323         if(now - t >= d){
14324             clearInterval(id);
14325             id = null;
14326             fn.apply(scope, args || []);
14327         }
14328     };
14329     /**
14330      * Cancels any pending timeout and queues a new one
14331      * @param {Number} delay The milliseconds to delay
14332      * @param {Function} newFn (optional) Overrides function passed to constructor
14333      * @param {Object} newScope (optional) Overrides scope passed to constructor
14334      * @param {Array} newArgs (optional) Overrides args passed to constructor
14335      */
14336     this.delay = function(delay, newFn, newScope, newArgs){
14337         if(id && delay != d){
14338             this.cancel();
14339         }
14340         d = delay;
14341         t = new Date().getTime();
14342         fn = newFn || fn;
14343         scope = newScope || scope;
14344         args = newArgs || args;
14345         if(!id){
14346             id = setInterval(call, d);
14347         }
14348     };
14349
14350     /**
14351      * Cancel the last queued timeout
14352      */
14353     this.cancel = function(){
14354         if(id){
14355             clearInterval(id);
14356             id = null;
14357         }
14358     };
14359 };/*
14360  * Based on:
14361  * Ext JS Library 1.1.1
14362  * Copyright(c) 2006-2007, Ext JS, LLC.
14363  *
14364  * Originally Released Under LGPL - original licence link has changed is not relivant.
14365  *
14366  * Fork - LGPL
14367  * <script type="text/javascript">
14368  */
14369 /**
14370  * @class Roo.util.TaskRunner
14371  * Manage background tasks - not sure why this is better that setInterval?
14372  * @static
14373  *
14374  */
14375  
14376 Roo.util.TaskRunner = function(interval){
14377     interval = interval || 10;
14378     var tasks = [], removeQueue = [];
14379     var id = 0;
14380     var running = false;
14381
14382     var stopThread = function(){
14383         running = false;
14384         clearInterval(id);
14385         id = 0;
14386     };
14387
14388     var startThread = function(){
14389         if(!running){
14390             running = true;
14391             id = setInterval(runTasks, interval);
14392         }
14393     };
14394
14395     var removeTask = function(task){
14396         removeQueue.push(task);
14397         if(task.onStop){
14398             task.onStop();
14399         }
14400     };
14401
14402     var runTasks = function(){
14403         if(removeQueue.length > 0){
14404             for(var i = 0, len = removeQueue.length; i < len; i++){
14405                 tasks.remove(removeQueue[i]);
14406             }
14407             removeQueue = [];
14408             if(tasks.length < 1){
14409                 stopThread();
14410                 return;
14411             }
14412         }
14413         var now = new Date().getTime();
14414         for(var i = 0, len = tasks.length; i < len; ++i){
14415             var t = tasks[i];
14416             var itime = now - t.taskRunTime;
14417             if(t.interval <= itime){
14418                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
14419                 t.taskRunTime = now;
14420                 if(rt === false || t.taskRunCount === t.repeat){
14421                     removeTask(t);
14422                     return;
14423                 }
14424             }
14425             if(t.duration && t.duration <= (now - t.taskStartTime)){
14426                 removeTask(t);
14427             }
14428         }
14429     };
14430
14431     /**
14432      * Queues a new task.
14433      * @param {Object} task
14434      *
14435      * Task property : interval = how frequent to run.
14436      * Task object should implement
14437      * function run()
14438      * Task object may implement
14439      * function onStop()
14440      */
14441     this.start = function(task){
14442         tasks.push(task);
14443         task.taskStartTime = new Date().getTime();
14444         task.taskRunTime = 0;
14445         task.taskRunCount = 0;
14446         startThread();
14447         return task;
14448     };
14449     /**
14450      * Stop  new task.
14451      * @param {Object} task
14452      */
14453     this.stop = function(task){
14454         removeTask(task);
14455         return task;
14456     };
14457     /**
14458      * Stop all Tasks
14459      */
14460     this.stopAll = function(){
14461         stopThread();
14462         for(var i = 0, len = tasks.length; i < len; i++){
14463             if(tasks[i].onStop){
14464                 tasks[i].onStop();
14465             }
14466         }
14467         tasks = [];
14468         removeQueue = [];
14469     };
14470 };
14471
14472 Roo.TaskMgr = new Roo.util.TaskRunner();/*
14473  * Based on:
14474  * Ext JS Library 1.1.1
14475  * Copyright(c) 2006-2007, Ext JS, LLC.
14476  *
14477  * Originally Released Under LGPL - original licence link has changed is not relivant.
14478  *
14479  * Fork - LGPL
14480  * <script type="text/javascript">
14481  */
14482
14483  
14484 /**
14485  * @class Roo.util.MixedCollection
14486  * @extends Roo.util.Observable
14487  * A Collection class that maintains both numeric indexes and keys and exposes events.
14488  * @constructor
14489  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
14490  * collection (defaults to false)
14491  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
14492  * and return the key value for that item.  This is used when available to look up the key on items that
14493  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
14494  * equivalent to providing an implementation for the {@link #getKey} method.
14495  */
14496 Roo.util.MixedCollection = function(allowFunctions, keyFn){
14497     this.items = [];
14498     this.map = {};
14499     this.keys = [];
14500     this.length = 0;
14501     this.addEvents({
14502         /**
14503          * @event clear
14504          * Fires when the collection is cleared.
14505          */
14506         "clear" : true,
14507         /**
14508          * @event add
14509          * Fires when an item is added to the collection.
14510          * @param {Number} index The index at which the item was added.
14511          * @param {Object} o The item added.
14512          * @param {String} key The key associated with the added item.
14513          */
14514         "add" : true,
14515         /**
14516          * @event replace
14517          * Fires when an item is replaced in the collection.
14518          * @param {String} key he key associated with the new added.
14519          * @param {Object} old The item being replaced.
14520          * @param {Object} new The new item.
14521          */
14522         "replace" : true,
14523         /**
14524          * @event remove
14525          * Fires when an item is removed from the collection.
14526          * @param {Object} o The item being removed.
14527          * @param {String} key (optional) The key associated with the removed item.
14528          */
14529         "remove" : true,
14530         "sort" : true
14531     });
14532     this.allowFunctions = allowFunctions === true;
14533     if(keyFn){
14534         this.getKey = keyFn;
14535     }
14536     Roo.util.MixedCollection.superclass.constructor.call(this);
14537 };
14538
14539 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
14540     allowFunctions : false,
14541     
14542 /**
14543  * Adds an item to the collection.
14544  * @param {String} key The key to associate with the item
14545  * @param {Object} o The item to add.
14546  * @return {Object} The item added.
14547  */
14548     add : function(key, o){
14549         if(arguments.length == 1){
14550             o = arguments[0];
14551             key = this.getKey(o);
14552         }
14553         if(typeof key == "undefined" || key === null){
14554             this.length++;
14555             this.items.push(o);
14556             this.keys.push(null);
14557         }else{
14558             var old = this.map[key];
14559             if(old){
14560                 return this.replace(key, o);
14561             }
14562             this.length++;
14563             this.items.push(o);
14564             this.map[key] = o;
14565             this.keys.push(key);
14566         }
14567         this.fireEvent("add", this.length-1, o, key);
14568         return o;
14569     },
14570        
14571 /**
14572   * MixedCollection has a generic way to fetch keys if you implement getKey.
14573 <pre><code>
14574 // normal way
14575 var mc = new Roo.util.MixedCollection();
14576 mc.add(someEl.dom.id, someEl);
14577 mc.add(otherEl.dom.id, otherEl);
14578 //and so on
14579
14580 // using getKey
14581 var mc = new Roo.util.MixedCollection();
14582 mc.getKey = function(el){
14583    return el.dom.id;
14584 };
14585 mc.add(someEl);
14586 mc.add(otherEl);
14587
14588 // or via the constructor
14589 var mc = new Roo.util.MixedCollection(false, function(el){
14590    return el.dom.id;
14591 });
14592 mc.add(someEl);
14593 mc.add(otherEl);
14594 </code></pre>
14595  * @param o {Object} The item for which to find the key.
14596  * @return {Object} The key for the passed item.
14597  */
14598     getKey : function(o){
14599          return o.id; 
14600     },
14601    
14602 /**
14603  * Replaces an item in the collection.
14604  * @param {String} key The key associated with the item to replace, or the item to replace.
14605  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
14606  * @return {Object}  The new item.
14607  */
14608     replace : function(key, o){
14609         if(arguments.length == 1){
14610             o = arguments[0];
14611             key = this.getKey(o);
14612         }
14613         var old = this.item(key);
14614         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
14615              return this.add(key, o);
14616         }
14617         var index = this.indexOfKey(key);
14618         this.items[index] = o;
14619         this.map[key] = o;
14620         this.fireEvent("replace", key, old, o);
14621         return o;
14622     },
14623    
14624 /**
14625  * Adds all elements of an Array or an Object to the collection.
14626  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
14627  * an Array of values, each of which are added to the collection.
14628  */
14629     addAll : function(objs){
14630         if(arguments.length > 1 || objs instanceof Array){
14631             var args = arguments.length > 1 ? arguments : objs;
14632             for(var i = 0, len = args.length; i < len; i++){
14633                 this.add(args[i]);
14634             }
14635         }else{
14636             for(var key in objs){
14637                 if(this.allowFunctions || typeof objs[key] != "function"){
14638                     this.add(key, objs[key]);
14639                 }
14640             }
14641         }
14642     },
14643    
14644 /**
14645  * Executes the specified function once for every item in the collection, passing each
14646  * item as the first and only parameter. returning false from the function will stop the iteration.
14647  * @param {Function} fn The function to execute for each item.
14648  * @param {Object} scope (optional) The scope in which to execute the function.
14649  */
14650     each : function(fn, scope){
14651         var items = [].concat(this.items); // each safe for removal
14652         for(var i = 0, len = items.length; i < len; i++){
14653             if(fn.call(scope || items[i], items[i], i, len) === false){
14654                 break;
14655             }
14656         }
14657     },
14658    
14659 /**
14660  * Executes the specified function once for every key in the collection, passing each
14661  * key, and its associated item as the first two parameters.
14662  * @param {Function} fn The function to execute for each item.
14663  * @param {Object} scope (optional) The scope in which to execute the function.
14664  */
14665     eachKey : function(fn, scope){
14666         for(var i = 0, len = this.keys.length; i < len; i++){
14667             fn.call(scope || window, this.keys[i], this.items[i], i, len);
14668         }
14669     },
14670    
14671 /**
14672  * Returns the first item in the collection which elicits a true return value from the
14673  * passed selection function.
14674  * @param {Function} fn The selection function to execute for each item.
14675  * @param {Object} scope (optional) The scope in which to execute the function.
14676  * @return {Object} The first item in the collection which returned true from the selection function.
14677  */
14678     find : function(fn, scope){
14679         for(var i = 0, len = this.items.length; i < len; i++){
14680             if(fn.call(scope || window, this.items[i], this.keys[i])){
14681                 return this.items[i];
14682             }
14683         }
14684         return null;
14685     },
14686    
14687 /**
14688  * Inserts an item at the specified index in the collection.
14689  * @param {Number} index The index to insert the item at.
14690  * @param {String} key The key to associate with the new item, or the item itself.
14691  * @param {Object} o  (optional) If the second parameter was a key, the new item.
14692  * @return {Object} The item inserted.
14693  */
14694     insert : function(index, key, o){
14695         if(arguments.length == 2){
14696             o = arguments[1];
14697             key = this.getKey(o);
14698         }
14699         if(index >= this.length){
14700             return this.add(key, o);
14701         }
14702         this.length++;
14703         this.items.splice(index, 0, o);
14704         if(typeof key != "undefined" && key != null){
14705             this.map[key] = o;
14706         }
14707         this.keys.splice(index, 0, key);
14708         this.fireEvent("add", index, o, key);
14709         return o;
14710     },
14711    
14712 /**
14713  * Removed an item from the collection.
14714  * @param {Object} o The item to remove.
14715  * @return {Object} The item removed.
14716  */
14717     remove : function(o){
14718         return this.removeAt(this.indexOf(o));
14719     },
14720    
14721 /**
14722  * Remove an item from a specified index in the collection.
14723  * @param {Number} index The index within the collection of the item to remove.
14724  */
14725     removeAt : function(index){
14726         if(index < this.length && index >= 0){
14727             this.length--;
14728             var o = this.items[index];
14729             this.items.splice(index, 1);
14730             var key = this.keys[index];
14731             if(typeof key != "undefined"){
14732                 delete this.map[key];
14733             }
14734             this.keys.splice(index, 1);
14735             this.fireEvent("remove", o, key);
14736         }
14737     },
14738    
14739 /**
14740  * Removed an item associated with the passed key fom the collection.
14741  * @param {String} key The key of the item to remove.
14742  */
14743     removeKey : function(key){
14744         return this.removeAt(this.indexOfKey(key));
14745     },
14746    
14747 /**
14748  * Returns the number of items in the collection.
14749  * @return {Number} the number of items in the collection.
14750  */
14751     getCount : function(){
14752         return this.length; 
14753     },
14754    
14755 /**
14756  * Returns index within the collection of the passed Object.
14757  * @param {Object} o The item to find the index of.
14758  * @return {Number} index of the item.
14759  */
14760     indexOf : function(o){
14761         if(!this.items.indexOf){
14762             for(var i = 0, len = this.items.length; i < len; i++){
14763                 if(this.items[i] == o) {
14764                     return i;
14765                 }
14766             }
14767             return -1;
14768         }else{
14769             return this.items.indexOf(o);
14770         }
14771     },
14772    
14773 /**
14774  * Returns index within the collection of the passed key.
14775  * @param {String} key The key to find the index of.
14776  * @return {Number} index of the key.
14777  */
14778     indexOfKey : function(key){
14779         if(!this.keys.indexOf){
14780             for(var i = 0, len = this.keys.length; i < len; i++){
14781                 if(this.keys[i] == key) {
14782                     return i;
14783                 }
14784             }
14785             return -1;
14786         }else{
14787             return this.keys.indexOf(key);
14788         }
14789     },
14790    
14791 /**
14792  * Returns the item associated with the passed key OR index. Key has priority over index.
14793  * @param {String/Number} key The key or index of the item.
14794  * @return {Object} The item associated with the passed key.
14795  */
14796     item : function(key){
14797         if (key === 'length') {
14798             return null;
14799         }
14800         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
14801         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
14802     },
14803     
14804 /**
14805  * Returns the item at the specified index.
14806  * @param {Number} index The index of the item.
14807  * @return {Object}
14808  */
14809     itemAt : function(index){
14810         return this.items[index];
14811     },
14812     
14813 /**
14814  * Returns the item associated with the passed key.
14815  * @param {String/Number} key The key of the item.
14816  * @return {Object} The item associated with the passed key.
14817  */
14818     key : function(key){
14819         return this.map[key];
14820     },
14821    
14822 /**
14823  * Returns true if the collection contains the passed Object as an item.
14824  * @param {Object} o  The Object to look for in the collection.
14825  * @return {Boolean} True if the collection contains the Object as an item.
14826  */
14827     contains : function(o){
14828         return this.indexOf(o) != -1;
14829     },
14830    
14831 /**
14832  * Returns true if the collection contains the passed Object as a key.
14833  * @param {String} key The key to look for in the collection.
14834  * @return {Boolean} True if the collection contains the Object as a key.
14835  */
14836     containsKey : function(key){
14837         return typeof this.map[key] != "undefined";
14838     },
14839    
14840 /**
14841  * Removes all items from the collection.
14842  */
14843     clear : function(){
14844         this.length = 0;
14845         this.items = [];
14846         this.keys = [];
14847         this.map = {};
14848         this.fireEvent("clear");
14849     },
14850    
14851 /**
14852  * Returns the first item in the collection.
14853  * @return {Object} the first item in the collection..
14854  */
14855     first : function(){
14856         return this.items[0]; 
14857     },
14858    
14859 /**
14860  * Returns the last item in the collection.
14861  * @return {Object} the last item in the collection..
14862  */
14863     last : function(){
14864         return this.items[this.length-1];   
14865     },
14866     
14867     _sort : function(property, dir, fn){
14868         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
14869         fn = fn || function(a, b){
14870             return a-b;
14871         };
14872         var c = [], k = this.keys, items = this.items;
14873         for(var i = 0, len = items.length; i < len; i++){
14874             c[c.length] = {key: k[i], value: items[i], index: i};
14875         }
14876         c.sort(function(a, b){
14877             var v = fn(a[property], b[property]) * dsc;
14878             if(v == 0){
14879                 v = (a.index < b.index ? -1 : 1);
14880             }
14881             return v;
14882         });
14883         for(var i = 0, len = c.length; i < len; i++){
14884             items[i] = c[i].value;
14885             k[i] = c[i].key;
14886         }
14887         this.fireEvent("sort", this);
14888     },
14889     
14890     /**
14891      * Sorts this collection with the passed comparison function
14892      * @param {String} direction (optional) "ASC" or "DESC"
14893      * @param {Function} fn (optional) comparison function
14894      */
14895     sort : function(dir, fn){
14896         this._sort("value", dir, fn);
14897     },
14898     
14899     /**
14900      * Sorts this collection by keys
14901      * @param {String} direction (optional) "ASC" or "DESC"
14902      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
14903      */
14904     keySort : function(dir, fn){
14905         this._sort("key", dir, fn || function(a, b){
14906             return String(a).toUpperCase()-String(b).toUpperCase();
14907         });
14908     },
14909     
14910     /**
14911      * Returns a range of items in this collection
14912      * @param {Number} startIndex (optional) defaults to 0
14913      * @param {Number} endIndex (optional) default to the last item
14914      * @return {Array} An array of items
14915      */
14916     getRange : function(start, end){
14917         var items = this.items;
14918         if(items.length < 1){
14919             return [];
14920         }
14921         start = start || 0;
14922         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
14923         var r = [];
14924         if(start <= end){
14925             for(var i = start; i <= end; i++) {
14926                     r[r.length] = items[i];
14927             }
14928         }else{
14929             for(var i = start; i >= end; i--) {
14930                     r[r.length] = items[i];
14931             }
14932         }
14933         return r;
14934     },
14935         
14936     /**
14937      * Filter the <i>objects</i> in this collection by a specific property. 
14938      * Returns a new collection that has been filtered.
14939      * @param {String} property A property on your objects
14940      * @param {String/RegExp} value Either string that the property values 
14941      * should start with or a RegExp to test against the property
14942      * @return {MixedCollection} The new filtered collection
14943      */
14944     filter : function(property, value){
14945         if(!value.exec){ // not a regex
14946             value = String(value);
14947             if(value.length == 0){
14948                 return this.clone();
14949             }
14950             value = new RegExp("^" + Roo.escapeRe(value), "i");
14951         }
14952         return this.filterBy(function(o){
14953             return o && value.test(o[property]);
14954         });
14955         },
14956     
14957     /**
14958      * Filter by a function. * Returns a new collection that has been filtered.
14959      * The passed function will be called with each 
14960      * object in the collection. If the function returns true, the value is included 
14961      * otherwise it is filtered.
14962      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
14963      * @param {Object} scope (optional) The scope of the function (defaults to this) 
14964      * @return {MixedCollection} The new filtered collection
14965      */
14966     filterBy : function(fn, scope){
14967         var r = new Roo.util.MixedCollection();
14968         r.getKey = this.getKey;
14969         var k = this.keys, it = this.items;
14970         for(var i = 0, len = it.length; i < len; i++){
14971             if(fn.call(scope||this, it[i], k[i])){
14972                                 r.add(k[i], it[i]);
14973                         }
14974         }
14975         return r;
14976     },
14977     
14978     /**
14979      * Creates a duplicate of this collection
14980      * @return {MixedCollection}
14981      */
14982     clone : function(){
14983         var r = new Roo.util.MixedCollection();
14984         var k = this.keys, it = this.items;
14985         for(var i = 0, len = it.length; i < len; i++){
14986             r.add(k[i], it[i]);
14987         }
14988         r.getKey = this.getKey;
14989         return r;
14990     }
14991 });
14992 /**
14993  * Returns the item associated with the passed key or index.
14994  * @method
14995  * @param {String/Number} key The key or index of the item.
14996  * @return {Object} The item associated with the passed key.
14997  */
14998 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
14999  * Based on:
15000  * Ext JS Library 1.1.1
15001  * Copyright(c) 2006-2007, Ext JS, LLC.
15002  *
15003  * Originally Released Under LGPL - original licence link has changed is not relivant.
15004  *
15005  * Fork - LGPL
15006  * <script type="text/javascript">
15007  */
15008 /**
15009  * @class Roo.util.JSON
15010  * Modified version of Douglas Crockford"s json.js that doesn"t
15011  * mess with the Object prototype 
15012  * http://www.json.org/js.html
15013  * @static
15014  */
15015 Roo.util.JSON = new (function(){
15016     var useHasOwn = {}.hasOwnProperty ? true : false;
15017     
15018     // crashes Safari in some instances
15019     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
15020     
15021     var pad = function(n) {
15022         return n < 10 ? "0" + n : n;
15023     };
15024     
15025     var m = {
15026         "\b": '\\b',
15027         "\t": '\\t',
15028         "\n": '\\n',
15029         "\f": '\\f',
15030         "\r": '\\r',
15031         '"' : '\\"',
15032         "\\": '\\\\'
15033     };
15034
15035     var encodeString = function(s){
15036         if (/["\\\x00-\x1f]/.test(s)) {
15037             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
15038                 var c = m[b];
15039                 if(c){
15040                     return c;
15041                 }
15042                 c = b.charCodeAt();
15043                 return "\\u00" +
15044                     Math.floor(c / 16).toString(16) +
15045                     (c % 16).toString(16);
15046             }) + '"';
15047         }
15048         return '"' + s + '"';
15049     };
15050     
15051     var encodeArray = function(o){
15052         var a = ["["], b, i, l = o.length, v;
15053             for (i = 0; i < l; i += 1) {
15054                 v = o[i];
15055                 switch (typeof v) {
15056                     case "undefined":
15057                     case "function":
15058                     case "unknown":
15059                         break;
15060                     default:
15061                         if (b) {
15062                             a.push(',');
15063                         }
15064                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
15065                         b = true;
15066                 }
15067             }
15068             a.push("]");
15069             return a.join("");
15070     };
15071     
15072     var encodeDate = function(o){
15073         return '"' + o.getFullYear() + "-" +
15074                 pad(o.getMonth() + 1) + "-" +
15075                 pad(o.getDate()) + "T" +
15076                 pad(o.getHours()) + ":" +
15077                 pad(o.getMinutes()) + ":" +
15078                 pad(o.getSeconds()) + '"';
15079     };
15080     
15081     /**
15082      * Encodes an Object, Array or other value
15083      * @param {Mixed} o The variable to encode
15084      * @return {String} The JSON string
15085      */
15086     this.encode = function(o)
15087     {
15088         // should this be extended to fully wrap stringify..
15089         
15090         if(typeof o == "undefined" || o === null){
15091             return "null";
15092         }else if(o instanceof Array){
15093             return encodeArray(o);
15094         }else if(o instanceof Date){
15095             return encodeDate(o);
15096         }else if(typeof o == "string"){
15097             return encodeString(o);
15098         }else if(typeof o == "number"){
15099             return isFinite(o) ? String(o) : "null";
15100         }else if(typeof o == "boolean"){
15101             return String(o);
15102         }else {
15103             var a = ["{"], b, i, v;
15104             for (i in o) {
15105                 if(!useHasOwn || o.hasOwnProperty(i)) {
15106                     v = o[i];
15107                     switch (typeof v) {
15108                     case "undefined":
15109                     case "function":
15110                     case "unknown":
15111                         break;
15112                     default:
15113                         if(b){
15114                             a.push(',');
15115                         }
15116                         a.push(this.encode(i), ":",
15117                                 v === null ? "null" : this.encode(v));
15118                         b = true;
15119                     }
15120                 }
15121             }
15122             a.push("}");
15123             return a.join("");
15124         }
15125     };
15126     
15127     /**
15128      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
15129      * @param {String} json The JSON string
15130      * @return {Object} The resulting object
15131      */
15132     this.decode = function(json){
15133         
15134         return  /** eval:var:json */ eval("(" + json + ')');
15135     };
15136 })();
15137 /** 
15138  * Shorthand for {@link Roo.util.JSON#encode}
15139  * @member Roo encode 
15140  * @method */
15141 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
15142 /** 
15143  * Shorthand for {@link Roo.util.JSON#decode}
15144  * @member Roo decode 
15145  * @method */
15146 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
15147 /*
15148  * Based on:
15149  * Ext JS Library 1.1.1
15150  * Copyright(c) 2006-2007, Ext JS, LLC.
15151  *
15152  * Originally Released Under LGPL - original licence link has changed is not relivant.
15153  *
15154  * Fork - LGPL
15155  * <script type="text/javascript">
15156  */
15157  
15158 /**
15159  * @class Roo.util.Format
15160  * Reusable data formatting functions
15161  * @static
15162  */
15163 Roo.util.Format = function(){
15164     var trimRe = /^\s+|\s+$/g;
15165     return {
15166         /**
15167          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
15168          * @param {String} value The string to truncate
15169          * @param {Number} length The maximum length to allow before truncating
15170          * @return {String} The converted text
15171          */
15172         ellipsis : function(value, len){
15173             if(value && value.length > len){
15174                 return value.substr(0, len-3)+"...";
15175             }
15176             return value;
15177         },
15178
15179         /**
15180          * Checks a reference and converts it to empty string if it is undefined
15181          * @param {Mixed} value Reference to check
15182          * @return {Mixed} Empty string if converted, otherwise the original value
15183          */
15184         undef : function(value){
15185             return typeof value != "undefined" ? value : "";
15186         },
15187
15188         /**
15189          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
15190          * @param {String} value The string to encode
15191          * @return {String} The encoded text
15192          */
15193         htmlEncode : function(value){
15194             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
15195         },
15196
15197         /**
15198          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
15199          * @param {String} value The string to decode
15200          * @return {String} The decoded text
15201          */
15202         htmlDecode : function(value){
15203             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
15204         },
15205
15206         /**
15207          * Trims any whitespace from either side of a string
15208          * @param {String} value The text to trim
15209          * @return {String} The trimmed text
15210          */
15211         trim : function(value){
15212             return String(value).replace(trimRe, "");
15213         },
15214
15215         /**
15216          * Returns a substring from within an original string
15217          * @param {String} value The original text
15218          * @param {Number} start The start index of the substring
15219          * @param {Number} length The length of the substring
15220          * @return {String} The substring
15221          */
15222         substr : function(value, start, length){
15223             return String(value).substr(start, length);
15224         },
15225
15226         /**
15227          * Converts a string to all lower case letters
15228          * @param {String} value The text to convert
15229          * @return {String} The converted text
15230          */
15231         lowercase : function(value){
15232             return String(value).toLowerCase();
15233         },
15234
15235         /**
15236          * Converts a string to all upper case letters
15237          * @param {String} value The text to convert
15238          * @return {String} The converted text
15239          */
15240         uppercase : function(value){
15241             return String(value).toUpperCase();
15242         },
15243
15244         /**
15245          * Converts the first character only of a string to upper case
15246          * @param {String} value The text to convert
15247          * @return {String} The converted text
15248          */
15249         capitalize : function(value){
15250             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
15251         },
15252
15253         // private
15254         call : function(value, fn){
15255             if(arguments.length > 2){
15256                 var args = Array.prototype.slice.call(arguments, 2);
15257                 args.unshift(value);
15258                  
15259                 return /** eval:var:value */  eval(fn).apply(window, args);
15260             }else{
15261                 /** eval:var:value */
15262                 return /** eval:var:value */ eval(fn).call(window, value);
15263             }
15264         },
15265
15266        
15267         /**
15268          * safer version of Math.toFixed..??/
15269          * @param {Number/String} value The numeric value to format
15270          * @param {Number/String} value Decimal places 
15271          * @return {String} The formatted currency string
15272          */
15273         toFixed : function(v, n)
15274         {
15275             // why not use to fixed - precision is buggered???
15276             if (!n) {
15277                 return Math.round(v-0);
15278             }
15279             var fact = Math.pow(10,n+1);
15280             v = (Math.round((v-0)*fact))/fact;
15281             var z = (''+fact).substring(2);
15282             if (v == Math.floor(v)) {
15283                 return Math.floor(v) + '.' + z;
15284             }
15285             
15286             // now just padd decimals..
15287             var ps = String(v).split('.');
15288             var fd = (ps[1] + z);
15289             var r = fd.substring(0,n); 
15290             var rm = fd.substring(n); 
15291             if (rm < 5) {
15292                 return ps[0] + '.' + r;
15293             }
15294             r*=1; // turn it into a number;
15295             r++;
15296             if (String(r).length != n) {
15297                 ps[0]*=1;
15298                 ps[0]++;
15299                 r = String(r).substring(1); // chop the end off.
15300             }
15301             
15302             return ps[0] + '.' + r;
15303              
15304         },
15305         
15306         /**
15307          * Format a number as US currency
15308          * @param {Number/String} value The numeric value to format
15309          * @return {String} The formatted currency string
15310          */
15311         usMoney : function(v){
15312             return '$' + Roo.util.Format.number(v);
15313         },
15314         
15315         /**
15316          * Format a number
15317          * eventually this should probably emulate php's number_format
15318          * @param {Number/String} value The numeric value to format
15319          * @param {Number} decimals number of decimal places
15320          * @param {String} delimiter for thousands (default comma)
15321          * @return {String} The formatted currency string
15322          */
15323         number : function(v, decimals, thousandsDelimiter)
15324         {
15325             // multiply and round.
15326             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
15327             thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
15328             
15329             var mul = Math.pow(10, decimals);
15330             var zero = String(mul).substring(1);
15331             v = (Math.round((v-0)*mul))/mul;
15332             
15333             // if it's '0' number.. then
15334             
15335             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
15336             v = String(v);
15337             var ps = v.split('.');
15338             var whole = ps[0];
15339             
15340             var r = /(\d+)(\d{3})/;
15341             // add comma's
15342             
15343             if(thousandsDelimiter.length != 0) {
15344                 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
15345             } 
15346             
15347             var sub = ps[1] ?
15348                     // has decimals..
15349                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
15350                     // does not have decimals
15351                     (decimals ? ('.' + zero) : '');
15352             
15353             
15354             return whole + sub ;
15355         },
15356         
15357         /**
15358          * Parse a value into a formatted date using the specified format pattern.
15359          * @param {Mixed} value The value to format
15360          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
15361          * @return {String} The formatted date string
15362          */
15363         date : function(v, format){
15364             if(!v){
15365                 return "";
15366             }
15367             if(!(v instanceof Date)){
15368                 v = new Date(Date.parse(v));
15369             }
15370             return v.dateFormat(format || Roo.util.Format.defaults.date);
15371         },
15372
15373         /**
15374          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
15375          * @param {String} format Any valid date format string
15376          * @return {Function} The date formatting function
15377          */
15378         dateRenderer : function(format){
15379             return function(v){
15380                 return Roo.util.Format.date(v, format);  
15381             };
15382         },
15383
15384         // private
15385         stripTagsRE : /<\/?[^>]+>/gi,
15386         
15387         /**
15388          * Strips all HTML tags
15389          * @param {Mixed} value The text from which to strip tags
15390          * @return {String} The stripped text
15391          */
15392         stripTags : function(v){
15393             return !v ? v : String(v).replace(this.stripTagsRE, "");
15394         },
15395         
15396         /**
15397          * Size in Mb,Gb etc.
15398          * @param {Number} value The number to be formated
15399          * @param {number} decimals how many decimal places
15400          * @return {String} the formated string
15401          */
15402         size : function(value, decimals)
15403         {
15404             var sizes = ['b', 'k', 'M', 'G', 'T'];
15405             if (value == 0) {
15406                 return 0;
15407             }
15408             var i = parseInt(Math.floor(Math.log(value) / Math.log(1024)));
15409             return Roo.util.Format.number(value/ Math.pow(1024, i) ,decimals)   + sizes[i];
15410         }
15411         
15412         
15413         
15414     };
15415 }();
15416 Roo.util.Format.defaults = {
15417     date : 'd/M/Y'
15418 };/*
15419  * Based on:
15420  * Ext JS Library 1.1.1
15421  * Copyright(c) 2006-2007, Ext JS, LLC.
15422  *
15423  * Originally Released Under LGPL - original licence link has changed is not relivant.
15424  *
15425  * Fork - LGPL
15426  * <script type="text/javascript">
15427  */
15428
15429
15430  
15431
15432 /**
15433  * @class Roo.MasterTemplate
15434  * @extends Roo.Template
15435  * Provides a template that can have child templates. The syntax is:
15436 <pre><code>
15437 var t = new Roo.MasterTemplate(
15438         '&lt;select name="{name}"&gt;',
15439                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
15440         '&lt;/select&gt;'
15441 );
15442 t.add('options', {value: 'foo', text: 'bar'});
15443 // or you can add multiple child elements in one shot
15444 t.addAll('options', [
15445     {value: 'foo', text: 'bar'},
15446     {value: 'foo2', text: 'bar2'},
15447     {value: 'foo3', text: 'bar3'}
15448 ]);
15449 // then append, applying the master template values
15450 t.append('my-form', {name: 'my-select'});
15451 </code></pre>
15452 * A name attribute for the child template is not required if you have only one child
15453 * template or you want to refer to them by index.
15454  */
15455 Roo.MasterTemplate = function(){
15456     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
15457     this.originalHtml = this.html;
15458     var st = {};
15459     var m, re = this.subTemplateRe;
15460     re.lastIndex = 0;
15461     var subIndex = 0;
15462     while(m = re.exec(this.html)){
15463         var name = m[1], content = m[2];
15464         st[subIndex] = {
15465             name: name,
15466             index: subIndex,
15467             buffer: [],
15468             tpl : new Roo.Template(content)
15469         };
15470         if(name){
15471             st[name] = st[subIndex];
15472         }
15473         st[subIndex].tpl.compile();
15474         st[subIndex].tpl.call = this.call.createDelegate(this);
15475         subIndex++;
15476     }
15477     this.subCount = subIndex;
15478     this.subs = st;
15479 };
15480 Roo.extend(Roo.MasterTemplate, Roo.Template, {
15481     /**
15482     * The regular expression used to match sub templates
15483     * @type RegExp
15484     * @property
15485     */
15486     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
15487
15488     /**
15489      * Applies the passed values to a child template.
15490      * @param {String/Number} name (optional) The name or index of the child template
15491      * @param {Array/Object} values The values to be applied to the template
15492      * @return {MasterTemplate} this
15493      */
15494      add : function(name, values){
15495         if(arguments.length == 1){
15496             values = arguments[0];
15497             name = 0;
15498         }
15499         var s = this.subs[name];
15500         s.buffer[s.buffer.length] = s.tpl.apply(values);
15501         return this;
15502     },
15503
15504     /**
15505      * Applies all the passed values to a child template.
15506      * @param {String/Number} name (optional) The name or index of the child template
15507      * @param {Array} values The values to be applied to the template, this should be an array of objects.
15508      * @param {Boolean} reset (optional) True to reset the template first
15509      * @return {MasterTemplate} this
15510      */
15511     fill : function(name, values, reset){
15512         var a = arguments;
15513         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
15514             values = a[0];
15515             name = 0;
15516             reset = a[1];
15517         }
15518         if(reset){
15519             this.reset();
15520         }
15521         for(var i = 0, len = values.length; i < len; i++){
15522             this.add(name, values[i]);
15523         }
15524         return this;
15525     },
15526
15527     /**
15528      * Resets the template for reuse
15529      * @return {MasterTemplate} this
15530      */
15531      reset : function(){
15532         var s = this.subs;
15533         for(var i = 0; i < this.subCount; i++){
15534             s[i].buffer = [];
15535         }
15536         return this;
15537     },
15538
15539     applyTemplate : function(values){
15540         var s = this.subs;
15541         var replaceIndex = -1;
15542         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
15543             return s[++replaceIndex].buffer.join("");
15544         });
15545         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
15546     },
15547
15548     apply : function(){
15549         return this.applyTemplate.apply(this, arguments);
15550     },
15551
15552     compile : function(){return this;}
15553 });
15554
15555 /**
15556  * Alias for fill().
15557  * @method
15558  */
15559 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
15560  /**
15561  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
15562  * var tpl = Roo.MasterTemplate.from('element-id');
15563  * @param {String/HTMLElement} el
15564  * @param {Object} config
15565  * @static
15566  */
15567 Roo.MasterTemplate.from = function(el, config){
15568     el = Roo.getDom(el);
15569     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
15570 };/*
15571  * Based on:
15572  * Ext JS Library 1.1.1
15573  * Copyright(c) 2006-2007, Ext JS, LLC.
15574  *
15575  * Originally Released Under LGPL - original licence link has changed is not relivant.
15576  *
15577  * Fork - LGPL
15578  * <script type="text/javascript">
15579  */
15580
15581  
15582 /**
15583  * @class Roo.util.CSS
15584  * Utility class for manipulating CSS rules
15585  * @static
15586
15587  */
15588 Roo.util.CSS = function(){
15589         var rules = null;
15590         var doc = document;
15591
15592     var camelRe = /(-[a-z])/gi;
15593     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
15594
15595    return {
15596    /**
15597     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
15598     * tag and appended to the HEAD of the document.
15599     * @param {String|Object} cssText The text containing the css rules
15600     * @param {String} id An id to add to the stylesheet for later removal
15601     * @return {StyleSheet}
15602     */
15603     createStyleSheet : function(cssText, id){
15604         var ss;
15605         var head = doc.getElementsByTagName("head")[0];
15606         var nrules = doc.createElement("style");
15607         nrules.setAttribute("type", "text/css");
15608         if(id){
15609             nrules.setAttribute("id", id);
15610         }
15611         if (typeof(cssText) != 'string') {
15612             // support object maps..
15613             // not sure if this a good idea.. 
15614             // perhaps it should be merged with the general css handling
15615             // and handle js style props.
15616             var cssTextNew = [];
15617             for(var n in cssText) {
15618                 var citems = [];
15619                 for(var k in cssText[n]) {
15620                     citems.push( k + ' : ' +cssText[n][k] + ';' );
15621                 }
15622                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
15623                 
15624             }
15625             cssText = cssTextNew.join("\n");
15626             
15627         }
15628        
15629        
15630        if(Roo.isIE){
15631            head.appendChild(nrules);
15632            ss = nrules.styleSheet;
15633            ss.cssText = cssText;
15634        }else{
15635            try{
15636                 nrules.appendChild(doc.createTextNode(cssText));
15637            }catch(e){
15638                nrules.cssText = cssText; 
15639            }
15640            head.appendChild(nrules);
15641            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
15642        }
15643        this.cacheStyleSheet(ss);
15644        return ss;
15645    },
15646
15647    /**
15648     * Removes a style or link tag by id
15649     * @param {String} id The id of the tag
15650     */
15651    removeStyleSheet : function(id){
15652        var existing = doc.getElementById(id);
15653        if(existing){
15654            existing.parentNode.removeChild(existing);
15655        }
15656    },
15657
15658    /**
15659     * Dynamically swaps an existing stylesheet reference for a new one
15660     * @param {String} id The id of an existing link tag to remove
15661     * @param {String} url The href of the new stylesheet to include
15662     */
15663    swapStyleSheet : function(id, url){
15664        this.removeStyleSheet(id);
15665        var ss = doc.createElement("link");
15666        ss.setAttribute("rel", "stylesheet");
15667        ss.setAttribute("type", "text/css");
15668        ss.setAttribute("id", id);
15669        ss.setAttribute("href", url);
15670        doc.getElementsByTagName("head")[0].appendChild(ss);
15671    },
15672    
15673    /**
15674     * Refresh the rule cache if you have dynamically added stylesheets
15675     * @return {Object} An object (hash) of rules indexed by selector
15676     */
15677    refreshCache : function(){
15678        return this.getRules(true);
15679    },
15680
15681    // private
15682    cacheStyleSheet : function(stylesheet){
15683        if(!rules){
15684            rules = {};
15685        }
15686        try{// try catch for cross domain access issue
15687            var ssRules = stylesheet.cssRules || stylesheet.rules;
15688            for(var j = ssRules.length-1; j >= 0; --j){
15689                rules[ssRules[j].selectorText] = ssRules[j];
15690            }
15691        }catch(e){}
15692    },
15693    
15694    /**
15695     * Gets all css rules for the document
15696     * @param {Boolean} refreshCache true to refresh the internal cache
15697     * @return {Object} An object (hash) of rules indexed by selector
15698     */
15699    getRules : function(refreshCache){
15700                 if(rules == null || refreshCache){
15701                         rules = {};
15702                         var ds = doc.styleSheets;
15703                         for(var i =0, len = ds.length; i < len; i++){
15704                             try{
15705                         this.cacheStyleSheet(ds[i]);
15706                     }catch(e){} 
15707                 }
15708                 }
15709                 return rules;
15710         },
15711         
15712         /**
15713     * Gets an an individual CSS rule by selector(s)
15714     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
15715     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
15716     * @return {CSSRule} The CSS rule or null if one is not found
15717     */
15718    getRule : function(selector, refreshCache){
15719                 var rs = this.getRules(refreshCache);
15720                 if(!(selector instanceof Array)){
15721                     return rs[selector];
15722                 }
15723                 for(var i = 0; i < selector.length; i++){
15724                         if(rs[selector[i]]){
15725                                 return rs[selector[i]];
15726                         }
15727                 }
15728                 return null;
15729         },
15730         
15731         
15732         /**
15733     * Updates a rule property
15734     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
15735     * @param {String} property The css property
15736     * @param {String} value The new value for the property
15737     * @return {Boolean} true If a rule was found and updated
15738     */
15739    updateRule : function(selector, property, value){
15740                 if(!(selector instanceof Array)){
15741                         var rule = this.getRule(selector);
15742                         if(rule){
15743                                 rule.style[property.replace(camelRe, camelFn)] = value;
15744                                 return true;
15745                         }
15746                 }else{
15747                         for(var i = 0; i < selector.length; i++){
15748                                 if(this.updateRule(selector[i], property, value)){
15749                                         return true;
15750                                 }
15751                         }
15752                 }
15753                 return false;
15754         }
15755    };   
15756 }();/*
15757  * Based on:
15758  * Ext JS Library 1.1.1
15759  * Copyright(c) 2006-2007, Ext JS, LLC.
15760  *
15761  * Originally Released Under LGPL - original licence link has changed is not relivant.
15762  *
15763  * Fork - LGPL
15764  * <script type="text/javascript">
15765  */
15766
15767  
15768
15769 /**
15770  * @class Roo.util.ClickRepeater
15771  * @extends Roo.util.Observable
15772  * 
15773  * A wrapper class which can be applied to any element. Fires a "click" event while the
15774  * mouse is pressed. The interval between firings may be specified in the config but
15775  * defaults to 10 milliseconds.
15776  * 
15777  * Optionally, a CSS class may be applied to the element during the time it is pressed.
15778  * 
15779  * @cfg {String/HTMLElement/Element} el The element to act as a button.
15780  * @cfg {Number} delay The initial delay before the repeating event begins firing.
15781  * Similar to an autorepeat key delay.
15782  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
15783  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
15784  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
15785  *           "interval" and "delay" are ignored. "immediate" is honored.
15786  * @cfg {Boolean} preventDefault True to prevent the default click event
15787  * @cfg {Boolean} stopDefault True to stop the default click event
15788  * 
15789  * @history
15790  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
15791  *     2007-02-02 jvs Renamed to ClickRepeater
15792  *   2007-02-03 jvs Modifications for FF Mac and Safari 
15793  *
15794  *  @constructor
15795  * @param {String/HTMLElement/Element} el The element to listen on
15796  * @param {Object} config
15797  **/
15798 Roo.util.ClickRepeater = function(el, config)
15799 {
15800     this.el = Roo.get(el);
15801     this.el.unselectable();
15802
15803     Roo.apply(this, config);
15804
15805     this.addEvents({
15806     /**
15807      * @event mousedown
15808      * Fires when the mouse button is depressed.
15809      * @param {Roo.util.ClickRepeater} this
15810      */
15811         "mousedown" : true,
15812     /**
15813      * @event click
15814      * Fires on a specified interval during the time the element is pressed.
15815      * @param {Roo.util.ClickRepeater} this
15816      */
15817         "click" : true,
15818     /**
15819      * @event mouseup
15820      * Fires when the mouse key is released.
15821      * @param {Roo.util.ClickRepeater} this
15822      */
15823         "mouseup" : true
15824     });
15825
15826     this.el.on("mousedown", this.handleMouseDown, this);
15827     if(this.preventDefault || this.stopDefault){
15828         this.el.on("click", function(e){
15829             if(this.preventDefault){
15830                 e.preventDefault();
15831             }
15832             if(this.stopDefault){
15833                 e.stopEvent();
15834             }
15835         }, this);
15836     }
15837
15838     // allow inline handler
15839     if(this.handler){
15840         this.on("click", this.handler,  this.scope || this);
15841     }
15842
15843     Roo.util.ClickRepeater.superclass.constructor.call(this);
15844 };
15845
15846 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
15847     interval : 20,
15848     delay: 250,
15849     preventDefault : true,
15850     stopDefault : false,
15851     timer : 0,
15852
15853     // private
15854     handleMouseDown : function(){
15855         clearTimeout(this.timer);
15856         this.el.blur();
15857         if(this.pressClass){
15858             this.el.addClass(this.pressClass);
15859         }
15860         this.mousedownTime = new Date();
15861
15862         Roo.get(document).on("mouseup", this.handleMouseUp, this);
15863         this.el.on("mouseout", this.handleMouseOut, this);
15864
15865         this.fireEvent("mousedown", this);
15866         this.fireEvent("click", this);
15867         
15868         this.timer = this.click.defer(this.delay || this.interval, this);
15869     },
15870
15871     // private
15872     click : function(){
15873         this.fireEvent("click", this);
15874         this.timer = this.click.defer(this.getInterval(), this);
15875     },
15876
15877     // private
15878     getInterval: function(){
15879         if(!this.accelerate){
15880             return this.interval;
15881         }
15882         var pressTime = this.mousedownTime.getElapsed();
15883         if(pressTime < 500){
15884             return 400;
15885         }else if(pressTime < 1700){
15886             return 320;
15887         }else if(pressTime < 2600){
15888             return 250;
15889         }else if(pressTime < 3500){
15890             return 180;
15891         }else if(pressTime < 4400){
15892             return 140;
15893         }else if(pressTime < 5300){
15894             return 80;
15895         }else if(pressTime < 6200){
15896             return 50;
15897         }else{
15898             return 10;
15899         }
15900     },
15901
15902     // private
15903     handleMouseOut : function(){
15904         clearTimeout(this.timer);
15905         if(this.pressClass){
15906             this.el.removeClass(this.pressClass);
15907         }
15908         this.el.on("mouseover", this.handleMouseReturn, this);
15909     },
15910
15911     // private
15912     handleMouseReturn : function(){
15913         this.el.un("mouseover", this.handleMouseReturn);
15914         if(this.pressClass){
15915             this.el.addClass(this.pressClass);
15916         }
15917         this.click();
15918     },
15919
15920     // private
15921     handleMouseUp : function(){
15922         clearTimeout(this.timer);
15923         this.el.un("mouseover", this.handleMouseReturn);
15924         this.el.un("mouseout", this.handleMouseOut);
15925         Roo.get(document).un("mouseup", this.handleMouseUp);
15926         this.el.removeClass(this.pressClass);
15927         this.fireEvent("mouseup", this);
15928     }
15929 });/**
15930  * @class Roo.util.Clipboard
15931  * @static
15932  * 
15933  * Clipboard UTILS
15934  * 
15935  **/
15936 Roo.util.Clipboard = {
15937     /**
15938      * Writes a string to the clipboard - using the Clipboard API if https, otherwise using text area.
15939      * @param {String} text to copy to clipboard
15940      */
15941     write : function(text) {
15942         // navigator clipboard api needs a secure context (https)
15943         if (navigator.clipboard && window.isSecureContext) {
15944             // navigator clipboard api method'
15945             navigator.clipboard.writeText(text);
15946             return ;
15947         } 
15948         // text area method
15949         var ta = document.createElement("textarea");
15950         ta.value = text;
15951         // make the textarea out of viewport
15952         ta.style.position = "fixed";
15953         ta.style.left = "-999999px";
15954         ta.style.top = "-999999px";
15955         document.body.appendChild(ta);
15956         ta.focus();
15957         ta.select();
15958         document.execCommand('copy');
15959         (function() {
15960             ta.remove();
15961         }).defer(100);
15962         
15963     }
15964         
15965 }
15966     /*
15967  * Based on:
15968  * Ext JS Library 1.1.1
15969  * Copyright(c) 2006-2007, Ext JS, LLC.
15970  *
15971  * Originally Released Under LGPL - original licence link has changed is not relivant.
15972  *
15973  * Fork - LGPL
15974  * <script type="text/javascript">
15975  */
15976
15977  
15978 /**
15979  * @class Roo.KeyNav
15980  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
15981  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
15982  * way to implement custom navigation schemes for any UI component.</p>
15983  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
15984  * pageUp, pageDown, del, home, end.  Usage:</p>
15985  <pre><code>
15986 var nav = new Roo.KeyNav("my-element", {
15987     "left" : function(e){
15988         this.moveLeft(e.ctrlKey);
15989     },
15990     "right" : function(e){
15991         this.moveRight(e.ctrlKey);
15992     },
15993     "enter" : function(e){
15994         this.save();
15995     },
15996     scope : this
15997 });
15998 </code></pre>
15999  * @constructor
16000  * @param {String/HTMLElement/Roo.Element} el The element to bind to
16001  * @param {Object} config The config
16002  */
16003 Roo.KeyNav = function(el, config){
16004     this.el = Roo.get(el);
16005     Roo.apply(this, config);
16006     if(!this.disabled){
16007         this.disabled = true;
16008         this.enable();
16009     }
16010 };
16011
16012 Roo.KeyNav.prototype = {
16013     /**
16014      * @cfg {Boolean} disabled
16015      * True to disable this KeyNav instance (defaults to false)
16016      */
16017     disabled : false,
16018     /**
16019      * @cfg {String} defaultEventAction
16020      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
16021      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
16022      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
16023      */
16024     defaultEventAction: "stopEvent",
16025     /**
16026      * @cfg {Boolean} forceKeyDown
16027      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
16028      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
16029      * handle keydown instead of keypress.
16030      */
16031     forceKeyDown : false,
16032
16033     // private
16034     prepareEvent : function(e){
16035         var k = e.getKey();
16036         var h = this.keyToHandler[k];
16037         //if(h && this[h]){
16038         //    e.stopPropagation();
16039         //}
16040         if(Roo.isSafari && h && k >= 37 && k <= 40){
16041             e.stopEvent();
16042         }
16043     },
16044
16045     // private
16046     relay : function(e){
16047         var k = e.getKey();
16048         var h = this.keyToHandler[k];
16049         if(h && this[h]){
16050             if(this.doRelay(e, this[h], h) !== true){
16051                 e[this.defaultEventAction]();
16052             }
16053         }
16054     },
16055
16056     // private
16057     doRelay : function(e, h, hname){
16058         return h.call(this.scope || this, e);
16059     },
16060
16061     // possible handlers
16062     enter : false,
16063     left : false,
16064     right : false,
16065     up : false,
16066     down : false,
16067     tab : false,
16068     esc : false,
16069     pageUp : false,
16070     pageDown : false,
16071     del : false,
16072     home : false,
16073     end : false,
16074
16075     // quick lookup hash
16076     keyToHandler : {
16077         37 : "left",
16078         39 : "right",
16079         38 : "up",
16080         40 : "down",
16081         33 : "pageUp",
16082         34 : "pageDown",
16083         46 : "del",
16084         36 : "home",
16085         35 : "end",
16086         13 : "enter",
16087         27 : "esc",
16088         9  : "tab"
16089     },
16090
16091         /**
16092          * Enable this KeyNav
16093          */
16094         enable: function(){
16095                 if(this.disabled){
16096             // ie won't do special keys on keypress, no one else will repeat keys with keydown
16097             // the EventObject will normalize Safari automatically
16098             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
16099                 this.el.on("keydown", this.relay,  this);
16100             }else{
16101                 this.el.on("keydown", this.prepareEvent,  this);
16102                 this.el.on("keypress", this.relay,  this);
16103             }
16104                     this.disabled = false;
16105                 }
16106         },
16107
16108         /**
16109          * Disable this KeyNav
16110          */
16111         disable: function(){
16112                 if(!this.disabled){
16113                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
16114                 this.el.un("keydown", this.relay);
16115             }else{
16116                 this.el.un("keydown", this.prepareEvent);
16117                 this.el.un("keypress", this.relay);
16118             }
16119                     this.disabled = true;
16120                 }
16121         }
16122 };/*
16123  * Based on:
16124  * Ext JS Library 1.1.1
16125  * Copyright(c) 2006-2007, Ext JS, LLC.
16126  *
16127  * Originally Released Under LGPL - original licence link has changed is not relivant.
16128  *
16129  * Fork - LGPL
16130  * <script type="text/javascript">
16131  */
16132
16133  
16134 /**
16135  * @class Roo.KeyMap
16136  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
16137  * The constructor accepts the same config object as defined by {@link #addBinding}.
16138  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
16139  * combination it will call the function with this signature (if the match is a multi-key
16140  * combination the callback will still be called only once): (String key, Roo.EventObject e)
16141  * A KeyMap can also handle a string representation of keys.<br />
16142  * Usage:
16143  <pre><code>
16144 // map one key by key code
16145 var map = new Roo.KeyMap("my-element", {
16146     key: 13, // or Roo.EventObject.ENTER
16147     fn: myHandler,
16148     scope: myObject
16149 });
16150
16151 // map multiple keys to one action by string
16152 var map = new Roo.KeyMap("my-element", {
16153     key: "a\r\n\t",
16154     fn: myHandler,
16155     scope: myObject
16156 });
16157
16158 // map multiple keys to multiple actions by strings and array of codes
16159 var map = new Roo.KeyMap("my-element", [
16160     {
16161         key: [10,13],
16162         fn: function(){ alert("Return was pressed"); }
16163     }, {
16164         key: "abc",
16165         fn: function(){ alert('a, b or c was pressed'); }
16166     }, {
16167         key: "\t",
16168         ctrl:true,
16169         shift:true,
16170         fn: function(){ alert('Control + shift + tab was pressed.'); }
16171     }
16172 ]);
16173 </code></pre>
16174  * <b>Note: A KeyMap starts enabled</b>
16175  * @constructor
16176  * @param {String/HTMLElement/Roo.Element} el The element to bind to
16177  * @param {Object} config The config (see {@link #addBinding})
16178  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
16179  */
16180 Roo.KeyMap = function(el, config, eventName){
16181     this.el  = Roo.get(el);
16182     this.eventName = eventName || "keydown";
16183     this.bindings = [];
16184     if(config){
16185         this.addBinding(config);
16186     }
16187     this.enable();
16188 };
16189
16190 Roo.KeyMap.prototype = {
16191     /**
16192      * True to stop the event from bubbling and prevent the default browser action if the
16193      * key was handled by the KeyMap (defaults to false)
16194      * @type Boolean
16195      */
16196     stopEvent : false,
16197
16198     /**
16199      * Add a new binding to this KeyMap. The following config object properties are supported:
16200      * <pre>
16201 Property    Type             Description
16202 ----------  ---------------  ----------------------------------------------------------------------
16203 key         String/Array     A single keycode or an array of keycodes to handle
16204 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
16205 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
16206 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
16207 fn          Function         The function to call when KeyMap finds the expected key combination
16208 scope       Object           The scope of the callback function
16209 </pre>
16210      *
16211      * Usage:
16212      * <pre><code>
16213 // Create a KeyMap
16214 var map = new Roo.KeyMap(document, {
16215     key: Roo.EventObject.ENTER,
16216     fn: handleKey,
16217     scope: this
16218 });
16219
16220 //Add a new binding to the existing KeyMap later
16221 map.addBinding({
16222     key: 'abc',
16223     shift: true,
16224     fn: handleKey,
16225     scope: this
16226 });
16227 </code></pre>
16228      * @param {Object/Array} config A single KeyMap config or an array of configs
16229      */
16230         addBinding : function(config){
16231         if(config instanceof Array){
16232             for(var i = 0, len = config.length; i < len; i++){
16233                 this.addBinding(config[i]);
16234             }
16235             return;
16236         }
16237         var keyCode = config.key,
16238             shift = config.shift, 
16239             ctrl = config.ctrl, 
16240             alt = config.alt,
16241             fn = config.fn,
16242             scope = config.scope;
16243         if(typeof keyCode == "string"){
16244             var ks = [];
16245             var keyString = keyCode.toUpperCase();
16246             for(var j = 0, len = keyString.length; j < len; j++){
16247                 ks.push(keyString.charCodeAt(j));
16248             }
16249             keyCode = ks;
16250         }
16251         var keyArray = keyCode instanceof Array;
16252         var handler = function(e){
16253             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
16254                 var k = e.getKey();
16255                 if(keyArray){
16256                     for(var i = 0, len = keyCode.length; i < len; i++){
16257                         if(keyCode[i] == k){
16258                           if(this.stopEvent){
16259                               e.stopEvent();
16260                           }
16261                           fn.call(scope || window, k, e);
16262                           return;
16263                         }
16264                     }
16265                 }else{
16266                     if(k == keyCode){
16267                         if(this.stopEvent){
16268                            e.stopEvent();
16269                         }
16270                         fn.call(scope || window, k, e);
16271                     }
16272                 }
16273             }
16274         };
16275         this.bindings.push(handler);  
16276         },
16277
16278     /**
16279      * Shorthand for adding a single key listener
16280      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
16281      * following options:
16282      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
16283      * @param {Function} fn The function to call
16284      * @param {Object} scope (optional) The scope of the function
16285      */
16286     on : function(key, fn, scope){
16287         var keyCode, shift, ctrl, alt;
16288         if(typeof key == "object" && !(key instanceof Array)){
16289             keyCode = key.key;
16290             shift = key.shift;
16291             ctrl = key.ctrl;
16292             alt = key.alt;
16293         }else{
16294             keyCode = key;
16295         }
16296         this.addBinding({
16297             key: keyCode,
16298             shift: shift,
16299             ctrl: ctrl,
16300             alt: alt,
16301             fn: fn,
16302             scope: scope
16303         })
16304     },
16305
16306     // private
16307     handleKeyDown : function(e){
16308             if(this.enabled){ //just in case
16309             var b = this.bindings;
16310             for(var i = 0, len = b.length; i < len; i++){
16311                 b[i].call(this, e);
16312             }
16313             }
16314         },
16315         
16316         /**
16317          * Returns true if this KeyMap is enabled
16318          * @return {Boolean} 
16319          */
16320         isEnabled : function(){
16321             return this.enabled;  
16322         },
16323         
16324         /**
16325          * Enables this KeyMap
16326          */
16327         enable: function(){
16328                 if(!this.enabled){
16329                     this.el.on(this.eventName, this.handleKeyDown, this);
16330                     this.enabled = true;
16331                 }
16332         },
16333
16334         /**
16335          * Disable this KeyMap
16336          */
16337         disable: function(){
16338                 if(this.enabled){
16339                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
16340                     this.enabled = false;
16341                 }
16342         }
16343 };/*
16344  * Based on:
16345  * Ext JS Library 1.1.1
16346  * Copyright(c) 2006-2007, Ext JS, LLC.
16347  *
16348  * Originally Released Under LGPL - original licence link has changed is not relivant.
16349  *
16350  * Fork - LGPL
16351  * <script type="text/javascript">
16352  */
16353
16354  
16355 /**
16356  * @class Roo.util.TextMetrics
16357  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
16358  * wide, in pixels, a given block of text will be.
16359  * @static
16360  */
16361 Roo.util.TextMetrics = function(){
16362     var shared;
16363     return {
16364         /**
16365          * Measures the size of the specified text
16366          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
16367          * that can affect the size of the rendered text
16368          * @param {String} text The text to measure
16369          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
16370          * in order to accurately measure the text height
16371          * @return {Object} An object containing the text's size {width: (width), height: (height)}
16372          */
16373         measure : function(el, text, fixedWidth){
16374             if(!shared){
16375                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
16376             }
16377             shared.bind(el);
16378             shared.setFixedWidth(fixedWidth || 'auto');
16379             return shared.getSize(text);
16380         },
16381
16382         /**
16383          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
16384          * the overhead of multiple calls to initialize the style properties on each measurement.
16385          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
16386          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
16387          * in order to accurately measure the text height
16388          * @return {Roo.util.TextMetrics.Instance} instance The new instance
16389          */
16390         createInstance : function(el, fixedWidth){
16391             return Roo.util.TextMetrics.Instance(el, fixedWidth);
16392         }
16393     };
16394 }();
16395
16396 /**
16397  * @class Roo.util.TextMetrics.Instance
16398  * Instance of  TextMetrics Calcuation
16399  * @constructor
16400  * Create a new TextMetrics Instance
16401  * @param {Object} bindto
16402  * @param {Boolean} fixedWidth
16403  */
16404
16405 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth)
16406 {
16407     var ml = new Roo.Element(document.createElement('div'));
16408     document.body.appendChild(ml.dom);
16409     ml.position('absolute');
16410     ml.setLeftTop(-1000, -1000);
16411     ml.hide();
16412
16413     if(fixedWidth){
16414         ml.setWidth(fixedWidth);
16415     }
16416      
16417     var instance = {
16418         /**
16419          * Returns the size of the specified text based on the internal element's style and width properties
16420          * @param {String} text The text to measure
16421          * @return {Object} An object containing the text's size {width: (width), height: (height)}
16422          */
16423         getSize : function(text){
16424             ml.update(text);
16425             var s = ml.getSize();
16426             ml.update('');
16427             return s;
16428         },
16429
16430         /**
16431          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
16432          * that can affect the size of the rendered text
16433          * @param {String/HTMLElement} el The element, dom node or id
16434          */
16435         bind : function(el){
16436             ml.setStyle(
16437                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
16438             );
16439         },
16440
16441         /**
16442          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
16443          * to set a fixed width in order to accurately measure the text height.
16444          * @param {Number} width The width to set on the element
16445          */
16446         setFixedWidth : function(width){
16447             ml.setWidth(width);
16448         },
16449
16450         /**
16451          * Returns the measured width of the specified text
16452          * @param {String} text The text to measure
16453          * @return {Number} width The width in pixels
16454          */
16455         getWidth : function(text){
16456             ml.dom.style.width = 'auto';
16457             return this.getSize(text).width;
16458         },
16459
16460         /**
16461          * Returns the measured height of the specified text.  For multiline text, be sure to call
16462          * {@link #setFixedWidth} if necessary.
16463          * @param {String} text The text to measure
16464          * @return {Number} height The height in pixels
16465          */
16466         getHeight : function(text){
16467             return this.getSize(text).height;
16468         }
16469     };
16470
16471     instance.bind(bindTo);
16472
16473     return instance;
16474 };
16475
16476 // backwards compat
16477 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
16478  * Based on:
16479  * Ext JS Library 1.1.1
16480  * Copyright(c) 2006-2007, Ext JS, LLC.
16481  *
16482  * Originally Released Under LGPL - original licence link has changed is not relivant.
16483  *
16484  * Fork - LGPL
16485  * <script type="text/javascript">
16486  */
16487
16488 /**
16489  * @class Roo.state.Provider
16490  * Abstract base class for state provider implementations. This class provides methods
16491  * for encoding and decoding <b>typed</b> variables including dates and defines the 
16492  * Provider interface.
16493  */
16494 Roo.state.Provider = function(){
16495     /**
16496      * @event statechange
16497      * Fires when a state change occurs.
16498      * @param {Provider} this This state provider
16499      * @param {String} key The state key which was changed
16500      * @param {String} value The encoded value for the state
16501      */
16502     this.addEvents({
16503         "statechange": true
16504     });
16505     this.state = {};
16506     Roo.state.Provider.superclass.constructor.call(this);
16507 };
16508 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
16509     /**
16510      * Returns the current value for a key
16511      * @param {String} name The key name
16512      * @param {Mixed} defaultValue A default value to return if the key's value is not found
16513      * @return {Mixed} The state data
16514      */
16515     get : function(name, defaultValue){
16516         return typeof this.state[name] == "undefined" ?
16517             defaultValue : this.state[name];
16518     },
16519     
16520     /**
16521      * Clears a value from the state
16522      * @param {String} name The key name
16523      */
16524     clear : function(name){
16525         delete this.state[name];
16526         this.fireEvent("statechange", this, name, null);
16527     },
16528     
16529     /**
16530      * Sets the value for a key
16531      * @param {String} name The key name
16532      * @param {Mixed} value The value to set
16533      */
16534     set : function(name, value){
16535         this.state[name] = value;
16536         this.fireEvent("statechange", this, name, value);
16537     },
16538     
16539     /**
16540      * Decodes a string previously encoded with {@link #encodeValue}.
16541      * @param {String} value The value to decode
16542      * @return {Mixed} The decoded value
16543      */
16544     decodeValue : function(cookie){
16545         var re = /^(a|n|d|b|s|o)\:(.*)$/;
16546         var matches = re.exec(unescape(cookie));
16547         if(!matches || !matches[1]) {
16548             return; // non state cookie
16549         }
16550         var type = matches[1];
16551         var v = matches[2];
16552         switch(type){
16553             case "n":
16554                 return parseFloat(v);
16555             case "d":
16556                 return new Date(Date.parse(v));
16557             case "b":
16558                 return (v == "1");
16559             case "a":
16560                 var all = [];
16561                 var values = v.split("^");
16562                 for(var i = 0, len = values.length; i < len; i++){
16563                     all.push(this.decodeValue(values[i]));
16564                 }
16565                 return all;
16566            case "o":
16567                 var all = {};
16568                 var values = v.split("^");
16569                 for(var i = 0, len = values.length; i < len; i++){
16570                     var kv = values[i].split("=");
16571                     all[kv[0]] = this.decodeValue(kv[1]);
16572                 }
16573                 return all;
16574            default:
16575                 return v;
16576         }
16577     },
16578     
16579     /**
16580      * Encodes a value including type information.  Decode with {@link #decodeValue}.
16581      * @param {Mixed} value The value to encode
16582      * @return {String} The encoded value
16583      */
16584     encodeValue : function(v){
16585         var enc;
16586         if(typeof v == "number"){
16587             enc = "n:" + v;
16588         }else if(typeof v == "boolean"){
16589             enc = "b:" + (v ? "1" : "0");
16590         }else if(v instanceof Date){
16591             enc = "d:" + v.toGMTString();
16592         }else if(v instanceof Array){
16593             var flat = "";
16594             for(var i = 0, len = v.length; i < len; i++){
16595                 flat += this.encodeValue(v[i]);
16596                 if(i != len-1) {
16597                     flat += "^";
16598                 }
16599             }
16600             enc = "a:" + flat;
16601         }else if(typeof v == "object"){
16602             var flat = "";
16603             for(var key in v){
16604                 if(typeof v[key] != "function"){
16605                     flat += key + "=" + this.encodeValue(v[key]) + "^";
16606                 }
16607             }
16608             enc = "o:" + flat.substring(0, flat.length-1);
16609         }else{
16610             enc = "s:" + v;
16611         }
16612         return escape(enc);        
16613     }
16614 });
16615
16616 /*
16617  * Based on:
16618  * Ext JS Library 1.1.1
16619  * Copyright(c) 2006-2007, Ext JS, LLC.
16620  *
16621  * Originally Released Under LGPL - original licence link has changed is not relivant.
16622  *
16623  * Fork - LGPL
16624  * <script type="text/javascript">
16625  */
16626 /**
16627  * @class Roo.state.Manager
16628  * This is the global state manager. By default all components that are "state aware" check this class
16629  * for state information if you don't pass them a custom state provider. In order for this class
16630  * to be useful, it must be initialized with a provider when your application initializes.
16631  <pre><code>
16632 // in your initialization function
16633 init : function(){
16634    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
16635    ...
16636    // supposed you have a {@link Roo.BorderLayout}
16637    var layout = new Roo.BorderLayout(...);
16638    layout.restoreState();
16639    // or a {Roo.BasicDialog}
16640    var dialog = new Roo.BasicDialog(...);
16641    dialog.restoreState();
16642  </code></pre>
16643  * @static
16644  */
16645 Roo.state.Manager = function(){
16646     var provider = new Roo.state.Provider();
16647     
16648     return {
16649         /**
16650          * Configures the default state provider for your application
16651          * @param {Provider} stateProvider The state provider to set
16652          */
16653         setProvider : function(stateProvider){
16654             provider = stateProvider;
16655         },
16656         
16657         /**
16658          * Returns the current value for a key
16659          * @param {String} name The key name
16660          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
16661          * @return {Mixed} The state data
16662          */
16663         get : function(key, defaultValue){
16664             return provider.get(key, defaultValue);
16665         },
16666         
16667         /**
16668          * Sets the value for a key
16669          * @param {String} name The key name
16670          * @param {Mixed} value The state data
16671          */
16672          set : function(key, value){
16673             provider.set(key, value);
16674         },
16675         
16676         /**
16677          * Clears a value from the state
16678          * @param {String} name The key name
16679          */
16680         clear : function(key){
16681             provider.clear(key);
16682         },
16683         
16684         /**
16685          * Gets the currently configured state provider
16686          * @return {Provider} The state provider
16687          */
16688         getProvider : function(){
16689             return provider;
16690         }
16691     };
16692 }();
16693 /*
16694  * Based on:
16695  * Ext JS Library 1.1.1
16696  * Copyright(c) 2006-2007, Ext JS, LLC.
16697  *
16698  * Originally Released Under LGPL - original licence link has changed is not relivant.
16699  *
16700  * Fork - LGPL
16701  * <script type="text/javascript">
16702  */
16703 /**
16704  * @class Roo.state.CookieProvider
16705  * @extends Roo.state.Provider
16706  * The default Provider implementation which saves state via cookies.
16707  * <br />Usage:
16708  <pre><code>
16709    var cp = new Roo.state.CookieProvider({
16710        path: "/cgi-bin/",
16711        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
16712        domain: "roojs.com"
16713    })
16714    Roo.state.Manager.setProvider(cp);
16715  </code></pre>
16716  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
16717  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
16718  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
16719  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
16720  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
16721  * domain the page is running on including the 'www' like 'www.roojs.com')
16722  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
16723  * @constructor
16724  * Create a new CookieProvider
16725  * @param {Object} config The configuration object
16726  */
16727 Roo.state.CookieProvider = function(config){
16728     Roo.state.CookieProvider.superclass.constructor.call(this);
16729     this.path = "/";
16730     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
16731     this.domain = null;
16732     this.secure = false;
16733     Roo.apply(this, config);
16734     this.state = this.readCookies();
16735 };
16736
16737 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
16738     // private
16739     set : function(name, value){
16740         if(typeof value == "undefined" || value === null){
16741             this.clear(name);
16742             return;
16743         }
16744         this.setCookie(name, value);
16745         Roo.state.CookieProvider.superclass.set.call(this, name, value);
16746     },
16747
16748     // private
16749     clear : function(name){
16750         this.clearCookie(name);
16751         Roo.state.CookieProvider.superclass.clear.call(this, name);
16752     },
16753
16754     // private
16755     readCookies : function(){
16756         var cookies = {};
16757         var c = document.cookie + ";";
16758         var re = /\s?(.*?)=(.*?);/g;
16759         var matches;
16760         while((matches = re.exec(c)) != null){
16761             var name = matches[1];
16762             var value = matches[2];
16763             if(name && name.substring(0,3) == "ys-"){
16764                 cookies[name.substr(3)] = this.decodeValue(value);
16765             }
16766         }
16767         return cookies;
16768     },
16769
16770     // private
16771     setCookie : function(name, value){
16772         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
16773            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
16774            ((this.path == null) ? "" : ("; path=" + this.path)) +
16775            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16776            ((this.secure == true) ? "; secure" : "");
16777     },
16778
16779     // private
16780     clearCookie : function(name){
16781         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
16782            ((this.path == null) ? "" : ("; path=" + this.path)) +
16783            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16784            ((this.secure == true) ? "; secure" : "");
16785     }
16786 });/*
16787  * Based on:
16788  * Ext JS Library 1.1.1
16789  * Copyright(c) 2006-2007, Ext JS, LLC.
16790  *
16791  * Originally Released Under LGPL - original licence link has changed is not relivant.
16792  *
16793  * Fork - LGPL
16794  * <script type="text/javascript">
16795  */
16796  
16797
16798 /**
16799  * @class Roo.ComponentMgr
16800  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
16801  * @static
16802  */
16803 Roo.ComponentMgr = function(){
16804     var all = new Roo.util.MixedCollection();
16805
16806     return {
16807         /**
16808          * Registers a component.
16809          * @param {Roo.Component} c The component
16810          */
16811         register : function(c){
16812             all.add(c);
16813         },
16814
16815         /**
16816          * Unregisters a component.
16817          * @param {Roo.Component} c The component
16818          */
16819         unregister : function(c){
16820             all.remove(c);
16821         },
16822
16823         /**
16824          * Returns a component by id
16825          * @param {String} id The component id
16826          */
16827         get : function(id){
16828             return all.get(id);
16829         },
16830
16831         /**
16832          * Registers a function that will be called when a specified component is added to ComponentMgr
16833          * @param {String} id The component id
16834          * @param {Funtction} fn The callback function
16835          * @param {Object} scope The scope of the callback
16836          */
16837         onAvailable : function(id, fn, scope){
16838             all.on("add", function(index, o){
16839                 if(o.id == id){
16840                     fn.call(scope || o, o);
16841                     all.un("add", fn, scope);
16842                 }
16843             });
16844         }
16845     };
16846 }();/*
16847  * Based on:
16848  * Ext JS Library 1.1.1
16849  * Copyright(c) 2006-2007, Ext JS, LLC.
16850  *
16851  * Originally Released Under LGPL - original licence link has changed is not relivant.
16852  *
16853  * Fork - LGPL
16854  * <script type="text/javascript">
16855  */
16856  
16857 /**
16858  * @class Roo.Component
16859  * @extends Roo.util.Observable
16860  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
16861  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
16862  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
16863  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
16864  * All visual components (widgets) that require rendering into a layout should subclass Component.
16865  * @constructor
16866  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
16867  * element and its id used as the component id.  If a string is passed, it is assumed to be the id of an existing element
16868  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
16869  */
16870 Roo.Component = function(config){
16871     config = config || {};
16872     if(config.tagName || config.dom || typeof config == "string"){ // element object
16873         config = {el: config, id: config.id || config};
16874     }
16875     this.initialConfig = config;
16876
16877     Roo.apply(this, config);
16878     this.addEvents({
16879         /**
16880          * @event disable
16881          * Fires after the component is disabled.
16882              * @param {Roo.Component} this
16883              */
16884         disable : true,
16885         /**
16886          * @event enable
16887          * Fires after the component is enabled.
16888              * @param {Roo.Component} this
16889              */
16890         enable : true,
16891         /**
16892          * @event beforeshow
16893          * Fires before the component is shown.  Return false to stop the show.
16894              * @param {Roo.Component} this
16895              */
16896         beforeshow : true,
16897         /**
16898          * @event show
16899          * Fires after the component is shown.
16900              * @param {Roo.Component} this
16901              */
16902         show : true,
16903         /**
16904          * @event beforehide
16905          * Fires before the component is hidden. Return false to stop the hide.
16906              * @param {Roo.Component} this
16907              */
16908         beforehide : true,
16909         /**
16910          * @event hide
16911          * Fires after the component is hidden.
16912              * @param {Roo.Component} this
16913              */
16914         hide : true,
16915         /**
16916          * @event beforerender
16917          * Fires before the component is rendered. Return false to stop the render.
16918              * @param {Roo.Component} this
16919              */
16920         beforerender : true,
16921         /**
16922          * @event render
16923          * Fires after the component is rendered.
16924              * @param {Roo.Component} this
16925              */
16926         render : true,
16927         /**
16928          * @event beforedestroy
16929          * Fires before the component is destroyed. Return false to stop the destroy.
16930              * @param {Roo.Component} this
16931              */
16932         beforedestroy : true,
16933         /**
16934          * @event destroy
16935          * Fires after the component is destroyed.
16936              * @param {Roo.Component} this
16937              */
16938         destroy : true
16939     });
16940     if(!this.id){
16941         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
16942     }
16943     Roo.ComponentMgr.register(this);
16944     Roo.Component.superclass.constructor.call(this);
16945     this.initComponent();
16946     if(this.renderTo){ // not supported by all components yet. use at your own risk!
16947         this.render(this.renderTo);
16948         delete this.renderTo;
16949     }
16950 };
16951
16952 /** @private */
16953 Roo.Component.AUTO_ID = 1000;
16954
16955 Roo.extend(Roo.Component, Roo.util.Observable, {
16956     /**
16957      * @scope Roo.Component.prototype
16958      * @type {Boolean}
16959      * true if this component is hidden. Read-only.
16960      */
16961     hidden : false,
16962     /**
16963      * @type {Boolean}
16964      * true if this component is disabled. Read-only.
16965      */
16966     disabled : false,
16967     /**
16968      * @type {Boolean}
16969      * true if this component has been rendered. Read-only.
16970      */
16971     rendered : false,
16972     
16973     /** @cfg {String} disableClass
16974      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
16975      */
16976     disabledClass : "x-item-disabled",
16977         /** @cfg {Boolean} allowDomMove
16978          * Whether the component can move the Dom node when rendering (defaults to true).
16979          */
16980     allowDomMove : true,
16981     /** @cfg {String} hideMode (display|visibility)
16982      * How this component should hidden. Supported values are
16983      * "visibility" (css visibility), "offsets" (negative offset position) and
16984      * "display" (css display) - defaults to "display".
16985      */
16986     hideMode: 'display',
16987
16988     /** @private */
16989     ctype : "Roo.Component",
16990
16991     /**
16992      * @cfg {String} actionMode 
16993      * which property holds the element that used for  hide() / show() / disable() / enable()
16994      * default is 'el' for forms you probably want to set this to fieldEl 
16995      */
16996     actionMode : "el",
16997
16998          /**
16999      * @cfg {String} style
17000      * css styles to add to component
17001      * eg. text-align:right;
17002      */
17003     style : false,
17004         
17005     /** @private */
17006     getActionEl : function(){
17007         return this[this.actionMode];
17008     },
17009
17010     initComponent : Roo.emptyFn,
17011     /**
17012      * If this is a lazy rendering component, render it to its container element.
17013      * @param {String/HTMLElement/Element} container (optional) The element this component should be rendered into. If it is being applied to existing markup, this should be left off.
17014      */
17015     render : function(container, position){
17016         
17017         if(this.rendered){
17018             return this;
17019         }
17020         
17021         if(this.fireEvent("beforerender", this) === false){
17022             return false;
17023         }
17024         
17025         if(!container && this.el){
17026             this.el = Roo.get(this.el);
17027             container = this.el.dom.parentNode;
17028             this.allowDomMove = false;
17029         }
17030         this.container = Roo.get(container);
17031         this.rendered = true;
17032         if(position !== undefined){
17033             if(typeof position == 'number'){
17034                 position = this.container.dom.childNodes[position];
17035             }else{
17036                 position = Roo.getDom(position);
17037             }
17038         }
17039         this.onRender(this.container, position || null);
17040         if(this.cls){
17041             this.el.addClass(this.cls);
17042             delete this.cls;
17043         }
17044         if(this.style){
17045             this.el.applyStyles(this.style);
17046             delete this.style;
17047         }
17048         this.fireEvent("render", this);
17049         this.afterRender(this.container);
17050         if(this.hidden){
17051             this.hide();
17052         }
17053         if(this.disabled){
17054             this.disable();
17055         }
17056
17057         return this;
17058         
17059     },
17060
17061     /** @private */
17062     // default function is not really useful
17063     onRender : function(ct, position){
17064         if(this.el){
17065             this.el = Roo.get(this.el);
17066             if(this.allowDomMove !== false){
17067                 ct.dom.insertBefore(this.el.dom, position);
17068             }
17069         }
17070     },
17071
17072     /** @private */
17073     getAutoCreate : function(){
17074         var cfg = typeof this.autoCreate == "object" ?
17075                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
17076         if(this.id && !cfg.id){
17077             cfg.id = this.id;
17078         }
17079         return cfg;
17080     },
17081
17082     /** @private */
17083     afterRender : Roo.emptyFn,
17084
17085     /**
17086      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
17087      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
17088      */
17089     destroy : function(){
17090         if(this.fireEvent("beforedestroy", this) !== false){
17091             this.purgeListeners();
17092             this.beforeDestroy();
17093             if(this.rendered){
17094                 this.el.removeAllListeners();
17095                 this.el.remove();
17096                 if(this.actionMode == "container"){
17097                     this.container.remove();
17098                 }
17099             }
17100             this.onDestroy();
17101             Roo.ComponentMgr.unregister(this);
17102             this.fireEvent("destroy", this);
17103         }
17104     },
17105
17106         /** @private */
17107     beforeDestroy : function(){
17108
17109     },
17110
17111         /** @private */
17112         onDestroy : function(){
17113
17114     },
17115
17116     /**
17117      * Returns the underlying {@link Roo.Element}.
17118      * @return {Roo.Element} The element
17119      */
17120     getEl : function(){
17121         return this.el;
17122     },
17123
17124     /**
17125      * Returns the id of this component.
17126      * @return {String}
17127      */
17128     getId : function(){
17129         return this.id;
17130     },
17131
17132     /**
17133      * Try to focus this component.
17134      * @param {Boolean} selectText True to also select the text in this component (if applicable)
17135      * @return {Roo.Component} this
17136      */
17137     focus : function(selectText){
17138         if(this.rendered){
17139             this.el.focus();
17140             if(selectText === true){
17141                 this.el.dom.select();
17142             }
17143         }
17144         return this;
17145     },
17146
17147     /** @private */
17148     blur : function(){
17149         if(this.rendered){
17150             this.el.blur();
17151         }
17152         return this;
17153     },
17154
17155     /**
17156      * Disable this component.
17157      * @return {Roo.Component} this
17158      */
17159     disable : function(){
17160         if(this.rendered){
17161             this.onDisable();
17162         }
17163         this.disabled = true;
17164         this.fireEvent("disable", this);
17165         return this;
17166     },
17167
17168         // private
17169     onDisable : function(){
17170         this.getActionEl().addClass(this.disabledClass);
17171         this.el.dom.disabled = true;
17172     },
17173
17174     /**
17175      * Enable this component.
17176      * @return {Roo.Component} this
17177      */
17178     enable : function(){
17179         if(this.rendered){
17180             this.onEnable();
17181         }
17182         this.disabled = false;
17183         this.fireEvent("enable", this);
17184         return this;
17185     },
17186
17187         // private
17188     onEnable : function(){
17189         this.getActionEl().removeClass(this.disabledClass);
17190         this.el.dom.disabled = false;
17191     },
17192
17193     /**
17194      * Convenience function for setting disabled/enabled by boolean.
17195      * @param {Boolean} disabled
17196      */
17197     setDisabled : function(disabled){
17198         this[disabled ? "disable" : "enable"]();
17199     },
17200
17201     /**
17202      * Show this component.
17203      * @return {Roo.Component} this
17204      */
17205     show: function(){
17206         if(this.fireEvent("beforeshow", this) !== false){
17207             this.hidden = false;
17208             if(this.rendered){
17209                 this.onShow();
17210             }
17211             this.fireEvent("show", this);
17212         }
17213         return this;
17214     },
17215
17216     // private
17217     onShow : function(){
17218         var ae = this.getActionEl();
17219         if(this.hideMode == 'visibility'){
17220             ae.dom.style.visibility = "visible";
17221         }else if(this.hideMode == 'offsets'){
17222             ae.removeClass('x-hidden');
17223         }else{
17224             ae.dom.style.display = "";
17225         }
17226     },
17227
17228     /**
17229      * Hide this component.
17230      * @return {Roo.Component} this
17231      */
17232     hide: function(){
17233         if(this.fireEvent("beforehide", this) !== false){
17234             this.hidden = true;
17235             if(this.rendered){
17236                 this.onHide();
17237             }
17238             this.fireEvent("hide", this);
17239         }
17240         return this;
17241     },
17242
17243     // private
17244     onHide : function(){
17245         var ae = this.getActionEl();
17246         if(this.hideMode == 'visibility'){
17247             ae.dom.style.visibility = "hidden";
17248         }else if(this.hideMode == 'offsets'){
17249             ae.addClass('x-hidden');
17250         }else{
17251             ae.dom.style.display = "none";
17252         }
17253     },
17254
17255     /**
17256      * Convenience function to hide or show this component by boolean.
17257      * @param {Boolean} visible True to show, false to hide
17258      * @return {Roo.Component} this
17259      */
17260     setVisible: function(visible){
17261         if(visible) {
17262             this.show();
17263         }else{
17264             this.hide();
17265         }
17266         return this;
17267     },
17268
17269     /**
17270      * Returns true if this component is visible.
17271      */
17272     isVisible : function(){
17273         return this.getActionEl().isVisible();
17274     },
17275
17276     cloneConfig : function(overrides){
17277         overrides = overrides || {};
17278         var id = overrides.id || Roo.id();
17279         var cfg = Roo.applyIf(overrides, this.initialConfig);
17280         cfg.id = id; // prevent dup id
17281         return new this.constructor(cfg);
17282     }
17283 });/*
17284  * Based on:
17285  * Ext JS Library 1.1.1
17286  * Copyright(c) 2006-2007, Ext JS, LLC.
17287  *
17288  * Originally Released Under LGPL - original licence link has changed is not relivant.
17289  *
17290  * Fork - LGPL
17291  * <script type="text/javascript">
17292  */
17293
17294 /**
17295  * @class Roo.BoxComponent
17296  * @extends Roo.Component
17297  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
17298  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
17299  * container classes should subclass BoxComponent so that they will work consistently when nested within other Roo
17300  * layout containers.
17301  * @constructor
17302  * @param {Roo.Element/String/Object} config The configuration options.
17303  */
17304 Roo.BoxComponent = function(config){
17305     Roo.Component.call(this, config);
17306     this.addEvents({
17307         /**
17308          * @event resize
17309          * Fires after the component is resized.
17310              * @param {Roo.Component} this
17311              * @param {Number} adjWidth The box-adjusted width that was set
17312              * @param {Number} adjHeight The box-adjusted height that was set
17313              * @param {Number} rawWidth The width that was originally specified
17314              * @param {Number} rawHeight The height that was originally specified
17315              */
17316         resize : true,
17317         /**
17318          * @event move
17319          * Fires after the component is moved.
17320              * @param {Roo.Component} this
17321              * @param {Number} x The new x position
17322              * @param {Number} y The new y position
17323              */
17324         move : true
17325     });
17326 };
17327
17328 Roo.extend(Roo.BoxComponent, Roo.Component, {
17329     // private, set in afterRender to signify that the component has been rendered
17330     boxReady : false,
17331     // private, used to defer height settings to subclasses
17332     deferHeight: false,
17333     /** @cfg {Number} width
17334      * width (optional) size of component
17335      */
17336      /** @cfg {Number} height
17337      * height (optional) size of component
17338      */
17339      
17340     /**
17341      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
17342      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
17343      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
17344      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
17345      * @return {Roo.BoxComponent} this
17346      */
17347     setSize : function(w, h){
17348         // support for standard size objects
17349         if(typeof w == 'object'){
17350             h = w.height;
17351             w = w.width;
17352         }
17353         // not rendered
17354         if(!this.boxReady){
17355             this.width = w;
17356             this.height = h;
17357             return this;
17358         }
17359
17360         // prevent recalcs when not needed
17361         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
17362             return this;
17363         }
17364         this.lastSize = {width: w, height: h};
17365
17366         var adj = this.adjustSize(w, h);
17367         var aw = adj.width, ah = adj.height;
17368         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
17369             var rz = this.getResizeEl();
17370             if(!this.deferHeight && aw !== undefined && ah !== undefined){
17371                 rz.setSize(aw, ah);
17372             }else if(!this.deferHeight && ah !== undefined){
17373                 rz.setHeight(ah);
17374             }else if(aw !== undefined){
17375                 rz.setWidth(aw);
17376             }
17377             this.onResize(aw, ah, w, h);
17378             this.fireEvent('resize', this, aw, ah, w, h);
17379         }
17380         return this;
17381     },
17382
17383     /**
17384      * Gets the current size of the component's underlying element.
17385      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
17386      */
17387     getSize : function(){
17388         return this.el.getSize();
17389     },
17390
17391     /**
17392      * Gets the current XY position of the component's underlying element.
17393      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
17394      * @return {Array} The XY position of the element (e.g., [100, 200])
17395      */
17396     getPosition : function(local){
17397         if(local === true){
17398             return [this.el.getLeft(true), this.el.getTop(true)];
17399         }
17400         return this.xy || this.el.getXY();
17401     },
17402
17403     /**
17404      * Gets the current box measurements of the component's underlying element.
17405      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
17406      * @returns {Object} box An object in the format {x, y, width, height}
17407      */
17408     getBox : function(local){
17409         var s = this.el.getSize();
17410         if(local){
17411             s.x = this.el.getLeft(true);
17412             s.y = this.el.getTop(true);
17413         }else{
17414             var xy = this.xy || this.el.getXY();
17415             s.x = xy[0];
17416             s.y = xy[1];
17417         }
17418         return s;
17419     },
17420
17421     /**
17422      * Sets the current box measurements of the component's underlying element.
17423      * @param {Object} box An object in the format {x, y, width, height}
17424      * @returns {Roo.BoxComponent} this
17425      */
17426     updateBox : function(box){
17427         this.setSize(box.width, box.height);
17428         this.setPagePosition(box.x, box.y);
17429         return this;
17430     },
17431
17432     // protected
17433     getResizeEl : function(){
17434         return this.resizeEl || this.el;
17435     },
17436
17437     // protected
17438     getPositionEl : function(){
17439         return this.positionEl || this.el;
17440     },
17441
17442     /**
17443      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
17444      * This method fires the move event.
17445      * @param {Number} left The new left
17446      * @param {Number} top The new top
17447      * @returns {Roo.BoxComponent} this
17448      */
17449     setPosition : function(x, y){
17450         this.x = x;
17451         this.y = y;
17452         if(!this.boxReady){
17453             return this;
17454         }
17455         var adj = this.adjustPosition(x, y);
17456         var ax = adj.x, ay = adj.y;
17457
17458         var el = this.getPositionEl();
17459         if(ax !== undefined || ay !== undefined){
17460             if(ax !== undefined && ay !== undefined){
17461                 el.setLeftTop(ax, ay);
17462             }else if(ax !== undefined){
17463                 el.setLeft(ax);
17464             }else if(ay !== undefined){
17465                 el.setTop(ay);
17466             }
17467             this.onPosition(ax, ay);
17468             this.fireEvent('move', this, ax, ay);
17469         }
17470         return this;
17471     },
17472
17473     /**
17474      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
17475      * This method fires the move event.
17476      * @param {Number} x The new x position
17477      * @param {Number} y The new y position
17478      * @returns {Roo.BoxComponent} this
17479      */
17480     setPagePosition : function(x, y){
17481         this.pageX = x;
17482         this.pageY = y;
17483         if(!this.boxReady){
17484             return;
17485         }
17486         if(x === undefined || y === undefined){ // cannot translate undefined points
17487             return;
17488         }
17489         var p = this.el.translatePoints(x, y);
17490         this.setPosition(p.left, p.top);
17491         return this;
17492     },
17493
17494     // private
17495     onRender : function(ct, position){
17496         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
17497         if(this.resizeEl){
17498             this.resizeEl = Roo.get(this.resizeEl);
17499         }
17500         if(this.positionEl){
17501             this.positionEl = Roo.get(this.positionEl);
17502         }
17503     },
17504
17505     // private
17506     afterRender : function(){
17507         Roo.BoxComponent.superclass.afterRender.call(this);
17508         this.boxReady = true;
17509         this.setSize(this.width, this.height);
17510         if(this.x || this.y){
17511             this.setPosition(this.x, this.y);
17512         }
17513         if(this.pageX || this.pageY){
17514             this.setPagePosition(this.pageX, this.pageY);
17515         }
17516     },
17517
17518     /**
17519      * Force the component's size to recalculate based on the underlying element's current height and width.
17520      * @returns {Roo.BoxComponent} this
17521      */
17522     syncSize : function(){
17523         delete this.lastSize;
17524         this.setSize(this.el.getWidth(), this.el.getHeight());
17525         return this;
17526     },
17527
17528     /**
17529      * Called after the component is resized, this method is empty by default but can be implemented by any
17530      * subclass that needs to perform custom logic after a resize occurs.
17531      * @param {Number} adjWidth The box-adjusted width that was set
17532      * @param {Number} adjHeight The box-adjusted height that was set
17533      * @param {Number} rawWidth The width that was originally specified
17534      * @param {Number} rawHeight The height that was originally specified
17535      */
17536     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
17537
17538     },
17539
17540     /**
17541      * Called after the component is moved, this method is empty by default but can be implemented by any
17542      * subclass that needs to perform custom logic after a move occurs.
17543      * @param {Number} x The new x position
17544      * @param {Number} y The new y position
17545      */
17546     onPosition : function(x, y){
17547
17548     },
17549
17550     // private
17551     adjustSize : function(w, h){
17552         if(this.autoWidth){
17553             w = 'auto';
17554         }
17555         if(this.autoHeight){
17556             h = 'auto';
17557         }
17558         return {width : w, height: h};
17559     },
17560
17561     // private
17562     adjustPosition : function(x, y){
17563         return {x : x, y: y};
17564     }
17565 });/*
17566  * Based on:
17567  * Ext JS Library 1.1.1
17568  * Copyright(c) 2006-2007, Ext JS, LLC.
17569  *
17570  * Originally Released Under LGPL - original licence link has changed is not relivant.
17571  *
17572  * Fork - LGPL
17573  * <script type="text/javascript">
17574  */
17575  (function(){ 
17576 /**
17577  * @class Roo.Layer
17578  * @extends Roo.Element
17579  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
17580  * automatic maintaining of shadow/shim positions.
17581  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
17582  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
17583  * you can pass a string with a CSS class name. False turns off the shadow.
17584  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
17585  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
17586  * @cfg {String} cls CSS class to add to the element
17587  * @cfg {Number} zindex Starting z-index (defaults to 11000)
17588  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
17589  * @constructor
17590  * @param {Object} config An object with config options.
17591  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
17592  */
17593
17594 Roo.Layer = function(config, existingEl){
17595     config = config || {};
17596     var dh = Roo.DomHelper;
17597     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
17598     if(existingEl){
17599         this.dom = Roo.getDom(existingEl);
17600     }
17601     if(!this.dom){
17602         var o = config.dh || {tag: "div", cls: "x-layer"};
17603         this.dom = dh.append(pel, o);
17604     }
17605     if(config.cls){
17606         this.addClass(config.cls);
17607     }
17608     this.constrain = config.constrain !== false;
17609     this.visibilityMode = Roo.Element.VISIBILITY;
17610     if(config.id){
17611         this.id = this.dom.id = config.id;
17612     }else{
17613         this.id = Roo.id(this.dom);
17614     }
17615     this.zindex = config.zindex || this.getZIndex();
17616     this.position("absolute", this.zindex);
17617     if(config.shadow){
17618         this.shadowOffset = config.shadowOffset || 4;
17619         this.shadow = new Roo.Shadow({
17620             offset : this.shadowOffset,
17621             mode : config.shadow
17622         });
17623     }else{
17624         this.shadowOffset = 0;
17625     }
17626     this.useShim = config.shim !== false && Roo.useShims;
17627     this.useDisplay = config.useDisplay;
17628     this.hide();
17629 };
17630
17631 var supr = Roo.Element.prototype;
17632
17633 // shims are shared among layer to keep from having 100 iframes
17634 var shims = [];
17635
17636 Roo.extend(Roo.Layer, Roo.Element, {
17637
17638     getZIndex : function(){
17639         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
17640     },
17641
17642     getShim : function(){
17643         if(!this.useShim){
17644             return null;
17645         }
17646         if(this.shim){
17647             return this.shim;
17648         }
17649         var shim = shims.shift();
17650         if(!shim){
17651             shim = this.createShim();
17652             shim.enableDisplayMode('block');
17653             shim.dom.style.display = 'none';
17654             shim.dom.style.visibility = 'visible';
17655         }
17656         var pn = this.dom.parentNode;
17657         if(shim.dom.parentNode != pn){
17658             pn.insertBefore(shim.dom, this.dom);
17659         }
17660         shim.setStyle('z-index', this.getZIndex()-2);
17661         this.shim = shim;
17662         return shim;
17663     },
17664
17665     hideShim : function(){
17666         if(this.shim){
17667             this.shim.setDisplayed(false);
17668             shims.push(this.shim);
17669             delete this.shim;
17670         }
17671     },
17672
17673     disableShadow : function(){
17674         if(this.shadow){
17675             this.shadowDisabled = true;
17676             this.shadow.hide();
17677             this.lastShadowOffset = this.shadowOffset;
17678             this.shadowOffset = 0;
17679         }
17680     },
17681
17682     enableShadow : function(show){
17683         if(this.shadow){
17684             this.shadowDisabled = false;
17685             this.shadowOffset = this.lastShadowOffset;
17686             delete this.lastShadowOffset;
17687             if(show){
17688                 this.sync(true);
17689             }
17690         }
17691     },
17692
17693     // private
17694     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
17695     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
17696     sync : function(doShow){
17697         var sw = this.shadow;
17698         if(!this.updating && this.isVisible() && (sw || this.useShim)){
17699             var sh = this.getShim();
17700
17701             var w = this.getWidth(),
17702                 h = this.getHeight();
17703
17704             var l = this.getLeft(true),
17705                 t = this.getTop(true);
17706
17707             if(sw && !this.shadowDisabled){
17708                 if(doShow && !sw.isVisible()){
17709                     sw.show(this);
17710                 }else{
17711                     sw.realign(l, t, w, h);
17712                 }
17713                 if(sh){
17714                     if(doShow){
17715                        sh.show();
17716                     }
17717                     // fit the shim behind the shadow, so it is shimmed too
17718                     var a = sw.adjusts, s = sh.dom.style;
17719                     s.left = (Math.min(l, l+a.l))+"px";
17720                     s.top = (Math.min(t, t+a.t))+"px";
17721                     s.width = (w+a.w)+"px";
17722                     s.height = (h+a.h)+"px";
17723                 }
17724             }else if(sh){
17725                 if(doShow){
17726                    sh.show();
17727                 }
17728                 sh.setSize(w, h);
17729                 sh.setLeftTop(l, t);
17730             }
17731             
17732         }
17733     },
17734
17735     // private
17736     destroy : function(){
17737         this.hideShim();
17738         if(this.shadow){
17739             this.shadow.hide();
17740         }
17741         this.removeAllListeners();
17742         var pn = this.dom.parentNode;
17743         if(pn){
17744             pn.removeChild(this.dom);
17745         }
17746         Roo.Element.uncache(this.id);
17747     },
17748
17749     remove : function(){
17750         this.destroy();
17751     },
17752
17753     // private
17754     beginUpdate : function(){
17755         this.updating = true;
17756     },
17757
17758     // private
17759     endUpdate : function(){
17760         this.updating = false;
17761         this.sync(true);
17762     },
17763
17764     // private
17765     hideUnders : function(negOffset){
17766         if(this.shadow){
17767             this.shadow.hide();
17768         }
17769         this.hideShim();
17770     },
17771
17772     // private
17773     constrainXY : function(){
17774         if(this.constrain){
17775             var vw = Roo.lib.Dom.getViewWidth(),
17776                 vh = Roo.lib.Dom.getViewHeight();
17777             var s = Roo.get(document).getScroll();
17778
17779             var xy = this.getXY();
17780             var x = xy[0], y = xy[1];   
17781             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
17782             // only move it if it needs it
17783             var moved = false;
17784             // first validate right/bottom
17785             if((x + w) > vw+s.left){
17786                 x = vw - w - this.shadowOffset;
17787                 moved = true;
17788             }
17789             if((y + h) > vh+s.top){
17790                 y = vh - h - this.shadowOffset;
17791                 moved = true;
17792             }
17793             // then make sure top/left isn't negative
17794             if(x < s.left){
17795                 x = s.left;
17796                 moved = true;
17797             }
17798             if(y < s.top){
17799                 y = s.top;
17800                 moved = true;
17801             }
17802             if(moved){
17803                 if(this.avoidY){
17804                     var ay = this.avoidY;
17805                     if(y <= ay && (y+h) >= ay){
17806                         y = ay-h-5;   
17807                     }
17808                 }
17809                 xy = [x, y];
17810                 this.storeXY(xy);
17811                 supr.setXY.call(this, xy);
17812                 this.sync();
17813             }
17814         }
17815     },
17816
17817     isVisible : function(){
17818         return this.visible;    
17819     },
17820
17821     // private
17822     showAction : function(){
17823         this.visible = true; // track visibility to prevent getStyle calls
17824         if(this.useDisplay === true){
17825             this.setDisplayed("");
17826         }else if(this.lastXY){
17827             supr.setXY.call(this, this.lastXY);
17828         }else if(this.lastLT){
17829             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
17830         }
17831     },
17832
17833     // private
17834     hideAction : function(){
17835         this.visible = false;
17836         if(this.useDisplay === true){
17837             this.setDisplayed(false);
17838         }else{
17839             this.setLeftTop(-10000,-10000);
17840         }
17841     },
17842
17843     // overridden Element method
17844     setVisible : function(v, a, d, c, e){
17845         if(v){
17846             this.showAction();
17847         }
17848         if(a && v){
17849             var cb = function(){
17850                 this.sync(true);
17851                 if(c){
17852                     c();
17853                 }
17854             }.createDelegate(this);
17855             supr.setVisible.call(this, true, true, d, cb, e);
17856         }else{
17857             if(!v){
17858                 this.hideUnders(true);
17859             }
17860             var cb = c;
17861             if(a){
17862                 cb = function(){
17863                     this.hideAction();
17864                     if(c){
17865                         c();
17866                     }
17867                 }.createDelegate(this);
17868             }
17869             supr.setVisible.call(this, v, a, d, cb, e);
17870             if(v){
17871                 this.sync(true);
17872             }else if(!a){
17873                 this.hideAction();
17874             }
17875         }
17876     },
17877
17878     storeXY : function(xy){
17879         delete this.lastLT;
17880         this.lastXY = xy;
17881     },
17882
17883     storeLeftTop : function(left, top){
17884         delete this.lastXY;
17885         this.lastLT = [left, top];
17886     },
17887
17888     // private
17889     beforeFx : function(){
17890         this.beforeAction();
17891         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
17892     },
17893
17894     // private
17895     afterFx : function(){
17896         Roo.Layer.superclass.afterFx.apply(this, arguments);
17897         this.sync(this.isVisible());
17898     },
17899
17900     // private
17901     beforeAction : function(){
17902         if(!this.updating && this.shadow){
17903             this.shadow.hide();
17904         }
17905     },
17906
17907     // overridden Element method
17908     setLeft : function(left){
17909         this.storeLeftTop(left, this.getTop(true));
17910         supr.setLeft.apply(this, arguments);
17911         this.sync();
17912     },
17913
17914     setTop : function(top){
17915         this.storeLeftTop(this.getLeft(true), top);
17916         supr.setTop.apply(this, arguments);
17917         this.sync();
17918     },
17919
17920     setLeftTop : function(left, top){
17921         this.storeLeftTop(left, top);
17922         supr.setLeftTop.apply(this, arguments);
17923         this.sync();
17924     },
17925
17926     setXY : function(xy, a, d, c, e){
17927         this.fixDisplay();
17928         this.beforeAction();
17929         this.storeXY(xy);
17930         var cb = this.createCB(c);
17931         supr.setXY.call(this, xy, a, d, cb, e);
17932         if(!a){
17933             cb();
17934         }
17935     },
17936
17937     // private
17938     createCB : function(c){
17939         var el = this;
17940         return function(){
17941             el.constrainXY();
17942             el.sync(true);
17943             if(c){
17944                 c();
17945             }
17946         };
17947     },
17948
17949     // overridden Element method
17950     setX : function(x, a, d, c, e){
17951         this.setXY([x, this.getY()], a, d, c, e);
17952     },
17953
17954     // overridden Element method
17955     setY : function(y, a, d, c, e){
17956         this.setXY([this.getX(), y], a, d, c, e);
17957     },
17958
17959     // overridden Element method
17960     setSize : function(w, h, a, d, c, e){
17961         this.beforeAction();
17962         var cb = this.createCB(c);
17963         supr.setSize.call(this, w, h, a, d, cb, e);
17964         if(!a){
17965             cb();
17966         }
17967     },
17968
17969     // overridden Element method
17970     setWidth : function(w, a, d, c, e){
17971         this.beforeAction();
17972         var cb = this.createCB(c);
17973         supr.setWidth.call(this, w, a, d, cb, e);
17974         if(!a){
17975             cb();
17976         }
17977     },
17978
17979     // overridden Element method
17980     setHeight : function(h, a, d, c, e){
17981         this.beforeAction();
17982         var cb = this.createCB(c);
17983         supr.setHeight.call(this, h, a, d, cb, e);
17984         if(!a){
17985             cb();
17986         }
17987     },
17988
17989     // overridden Element method
17990     setBounds : function(x, y, w, h, a, d, c, e){
17991         this.beforeAction();
17992         var cb = this.createCB(c);
17993         if(!a){
17994             this.storeXY([x, y]);
17995             supr.setXY.call(this, [x, y]);
17996             supr.setSize.call(this, w, h, a, d, cb, e);
17997             cb();
17998         }else{
17999             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
18000         }
18001         return this;
18002     },
18003     
18004     /**
18005      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
18006      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
18007      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
18008      * @param {Number} zindex The new z-index to set
18009      * @return {this} The Layer
18010      */
18011     setZIndex : function(zindex){
18012         this.zindex = zindex;
18013         this.setStyle("z-index", zindex + 2);
18014         if(this.shadow){
18015             this.shadow.setZIndex(zindex + 1);
18016         }
18017         if(this.shim){
18018             this.shim.setStyle("z-index", zindex);
18019         }
18020     }
18021 });
18022 })();/*
18023  * Original code for Roojs - LGPL
18024  * <script type="text/javascript">
18025  */
18026  
18027 /**
18028  * @class Roo.XComponent
18029  * A delayed Element creator...
18030  * Or a way to group chunks of interface together.
18031  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
18032  *  used in conjunction with XComponent.build() it will create an instance of each element,
18033  *  then call addxtype() to build the User interface.
18034  * 
18035  * Mypart.xyx = new Roo.XComponent({
18036
18037     parent : 'Mypart.xyz', // empty == document.element.!!
18038     order : '001',
18039     name : 'xxxx'
18040     region : 'xxxx'
18041     disabled : function() {} 
18042      
18043     tree : function() { // return an tree of xtype declared components
18044         var MODULE = this;
18045         return 
18046         {
18047             xtype : 'NestedLayoutPanel',
18048             // technicall
18049         }
18050      ]
18051  *})
18052  *
18053  *
18054  * It can be used to build a big heiracy, with parent etc.
18055  * or you can just use this to render a single compoent to a dom element
18056  * MYPART.render(Roo.Element | String(id) | dom_element )
18057  *
18058  *
18059  * Usage patterns.
18060  *
18061  * Classic Roo
18062  *
18063  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
18064  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
18065  *
18066  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
18067  *
18068  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
18069  * - if mulitple topModules exist, the last one is defined as the top module.
18070  *
18071  * Embeded Roo
18072  * 
18073  * When the top level or multiple modules are to embedded into a existing HTML page,
18074  * the parent element can container '#id' of the element where the module will be drawn.
18075  *
18076  * Bootstrap Roo
18077  *
18078  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
18079  * it relies more on a include mechanism, where sub modules are included into an outer page.
18080  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
18081  * 
18082  * Bootstrap Roo Included elements
18083  *
18084  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
18085  * hence confusing the component builder as it thinks there are multiple top level elements. 
18086  *
18087  * String Over-ride & Translations
18088  *
18089  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
18090  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
18091  * are needed. @see Roo.XComponent.overlayString  
18092  * 
18093  * 
18094  * 
18095  * @extends Roo.util.Observable
18096  * @constructor
18097  * @param cfg {Object} configuration of component
18098  * 
18099  */
18100 Roo.XComponent = function(cfg) {
18101     Roo.apply(this, cfg);
18102     this.addEvents({ 
18103         /**
18104              * @event built
18105              * Fires when this the componnt is built
18106              * @param {Roo.XComponent} c the component
18107              */
18108         'built' : true
18109         
18110     });
18111     this.region = this.region || 'center'; // default..
18112     Roo.XComponent.register(this);
18113     this.modules = false;
18114     this.el = false; // where the layout goes..
18115     
18116     
18117 }
18118 Roo.extend(Roo.XComponent, Roo.util.Observable, {
18119     /**
18120      * @property el
18121      * The created element (with Roo.factory())
18122      * @type {Roo.Layout}
18123      */
18124     el  : false,
18125     
18126     /**
18127      * @property el
18128      * for BC  - use el in new code
18129      * @type {Roo.Layout}
18130      */
18131     panel : false,
18132     
18133     /**
18134      * @property layout
18135      * for BC  - use el in new code
18136      * @type {Roo.Layout}
18137      */
18138     layout : false,
18139     
18140      /**
18141      * @cfg {Function|boolean} disabled
18142      * If this module is disabled by some rule, return true from the funtion
18143      */
18144     disabled : false,
18145     
18146     /**
18147      * @cfg {String} parent 
18148      * Name of parent element which it get xtype added to..
18149      */
18150     parent: false,
18151     
18152     /**
18153      * @cfg {String} order
18154      * Used to set the order in which elements are created (usefull for multiple tabs)
18155      */
18156     
18157     order : false,
18158     /**
18159      * @cfg {String} name
18160      * String to display while loading.
18161      */
18162     name : false,
18163     /**
18164      * @cfg {String} region
18165      * Region to render component to (defaults to center)
18166      */
18167     region : 'center',
18168     
18169     /**
18170      * @cfg {Array} items
18171      * A single item array - the first element is the root of the tree..
18172      * It's done this way to stay compatible with the Xtype system...
18173      */
18174     items : false,
18175     
18176     /**
18177      * @property _tree
18178      * The method that retuns the tree of parts that make up this compoennt 
18179      * @type {function}
18180      */
18181     _tree  : false,
18182     
18183      /**
18184      * render
18185      * render element to dom or tree
18186      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
18187      */
18188     
18189     render : function(el)
18190     {
18191         
18192         el = el || false;
18193         var hp = this.parent ? 1 : 0;
18194         Roo.debug &&  Roo.log(this);
18195         
18196         var tree = this._tree ? this._tree() : this.tree();
18197
18198         
18199         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
18200             // if parent is a '#.....' string, then let's use that..
18201             var ename = this.parent.substr(1);
18202             this.parent = false;
18203             Roo.debug && Roo.log(ename);
18204             switch (ename) {
18205                 case 'bootstrap-body':
18206                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
18207                         // this is the BorderLayout standard?
18208                        this.parent = { el : true };
18209                        break;
18210                     }
18211                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
18212                         // need to insert stuff...
18213                         this.parent =  {
18214                              el : new Roo.bootstrap.layout.Border({
18215                                  el : document.body, 
18216                      
18217                                  center: {
18218                                     titlebar: false,
18219                                     autoScroll:false,
18220                                     closeOnTab: true,
18221                                     tabPosition: 'top',
18222                                       //resizeTabs: true,
18223                                     alwaysShowTabs: true,
18224                                     hideTabs: false
18225                                      //minTabWidth: 140
18226                                  }
18227                              })
18228                         
18229                          };
18230                          break;
18231                     }
18232                          
18233                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
18234                         this.parent = { el :  new  Roo.bootstrap.Body() };
18235                         Roo.debug && Roo.log("setting el to doc body");
18236                          
18237                     } else {
18238                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
18239                     }
18240                     break;
18241                 case 'bootstrap':
18242                     this.parent = { el : true};
18243                     // fall through
18244                 default:
18245                     el = Roo.get(ename);
18246                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
18247                         this.parent = { el : true};
18248                     }
18249                     
18250                     break;
18251             }
18252                 
18253             
18254             if (!el && !this.parent) {
18255                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
18256                 return;
18257             }
18258         }
18259         
18260         Roo.debug && Roo.log("EL:");
18261         Roo.debug && Roo.log(el);
18262         Roo.debug && Roo.log("this.parent.el:");
18263         Roo.debug && Roo.log(this.parent.el);
18264         
18265
18266         // altertive root elements ??? - we need a better way to indicate these.
18267         var is_alt = Roo.XComponent.is_alt ||
18268                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
18269                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
18270                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
18271         
18272         
18273         
18274         if (!this.parent && is_alt) {
18275             //el = Roo.get(document.body);
18276             this.parent = { el : true };
18277         }
18278             
18279             
18280         
18281         if (!this.parent) {
18282             
18283             Roo.debug && Roo.log("no parent - creating one");
18284             
18285             el = el ? Roo.get(el) : false;      
18286             
18287             if (typeof(Roo.BorderLayout) == 'undefined' ) {
18288                 
18289                 this.parent =  {
18290                     el : new Roo.bootstrap.layout.Border({
18291                         el: el || document.body,
18292                     
18293                         center: {
18294                             titlebar: false,
18295                             autoScroll:false,
18296                             closeOnTab: true,
18297                             tabPosition: 'top',
18298                              //resizeTabs: true,
18299                             alwaysShowTabs: false,
18300                             hideTabs: true,
18301                             minTabWidth: 140,
18302                             overflow: 'visible'
18303                          }
18304                      })
18305                 };
18306             } else {
18307             
18308                 // it's a top level one..
18309                 this.parent =  {
18310                     el : new Roo.BorderLayout(el || document.body, {
18311                         center: {
18312                             titlebar: false,
18313                             autoScroll:false,
18314                             closeOnTab: true,
18315                             tabPosition: 'top',
18316                              //resizeTabs: true,
18317                             alwaysShowTabs: el && hp? false :  true,
18318                             hideTabs: el || !hp ? true :  false,
18319                             minTabWidth: 140
18320                          }
18321                     })
18322                 };
18323             }
18324         }
18325         
18326         if (!this.parent.el) {
18327                 // probably an old style ctor, which has been disabled.
18328                 return;
18329
18330         }
18331                 // The 'tree' method is  '_tree now' 
18332             
18333         tree.region = tree.region || this.region;
18334         var is_body = false;
18335         if (this.parent.el === true) {
18336             // bootstrap... - body..
18337             if (el) {
18338                 tree.el = el;
18339             }
18340             this.parent.el = Roo.factory(tree);
18341             is_body = true;
18342         }
18343         
18344         this.el = this.parent.el.addxtype(tree, undefined, is_body);
18345         this.fireEvent('built', this);
18346         
18347         this.panel = this.el;
18348         this.layout = this.panel.layout;
18349         this.parentLayout = this.parent.layout  || false;  
18350          
18351     }
18352     
18353 });
18354
18355 Roo.apply(Roo.XComponent, {
18356     /**
18357      * @property  hideProgress
18358      * true to disable the building progress bar.. usefull on single page renders.
18359      * @type Boolean
18360      */
18361     hideProgress : false,
18362     /**
18363      * @property  buildCompleted
18364      * True when the builder has completed building the interface.
18365      * @type Boolean
18366      */
18367     buildCompleted : false,
18368      
18369     /**
18370      * @property  topModule
18371      * the upper most module - uses document.element as it's constructor.
18372      * @type Object
18373      */
18374      
18375     topModule  : false,
18376       
18377     /**
18378      * @property  modules
18379      * array of modules to be created by registration system.
18380      * @type {Array} of Roo.XComponent
18381      */
18382     
18383     modules : [],
18384     /**
18385      * @property  elmodules
18386      * array of modules to be created by which use #ID 
18387      * @type {Array} of Roo.XComponent
18388      */
18389      
18390     elmodules : [],
18391
18392      /**
18393      * @property  is_alt
18394      * Is an alternative Root - normally used by bootstrap or other systems,
18395      *    where the top element in the tree can wrap 'body' 
18396      * @type {boolean}  (default false)
18397      */
18398      
18399     is_alt : false,
18400     /**
18401      * @property  build_from_html
18402      * Build elements from html - used by bootstrap HTML stuff 
18403      *    - this is cleared after build is completed
18404      * @type {boolean}    (default false)
18405      */
18406      
18407     build_from_html : false,
18408     /**
18409      * Register components to be built later.
18410      *
18411      * This solves the following issues
18412      * - Building is not done on page load, but after an authentication process has occured.
18413      * - Interface elements are registered on page load
18414      * - Parent Interface elements may not be loaded before child, so this handles that..
18415      * 
18416      *
18417      * example:
18418      * 
18419      * MyApp.register({
18420           order : '000001',
18421           module : 'Pman.Tab.projectMgr',
18422           region : 'center',
18423           parent : 'Pman.layout',
18424           disabled : false,  // or use a function..
18425         })
18426      
18427      * * @param {Object} details about module
18428      */
18429     register : function(obj) {
18430                 
18431         Roo.XComponent.event.fireEvent('register', obj);
18432         switch(typeof(obj.disabled) ) {
18433                 
18434             case 'undefined':
18435                 break;
18436             
18437             case 'function':
18438                 if ( obj.disabled() ) {
18439                         return;
18440                 }
18441                 break;
18442             
18443             default:
18444                 if (obj.disabled || obj.region == '#disabled') {
18445                         return;
18446                 }
18447                 break;
18448         }
18449                 
18450         this.modules.push(obj);
18451          
18452     },
18453     /**
18454      * convert a string to an object..
18455      * eg. 'AAA.BBB' -> finds AAA.BBB
18456
18457      */
18458     
18459     toObject : function(str)
18460     {
18461         if (!str || typeof(str) == 'object') {
18462             return str;
18463         }
18464         if (str.substring(0,1) == '#') {
18465             return str;
18466         }
18467
18468         var ar = str.split('.');
18469         var rt, o;
18470         rt = ar.shift();
18471             /** eval:var:o */
18472         try {
18473             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
18474         } catch (e) {
18475             throw "Module not found : " + str;
18476         }
18477         
18478         if (o === false) {
18479             throw "Module not found : " + str;
18480         }
18481         Roo.each(ar, function(e) {
18482             if (typeof(o[e]) == 'undefined') {
18483                 throw "Module not found : " + str;
18484             }
18485             o = o[e];
18486         });
18487         
18488         return o;
18489         
18490     },
18491     
18492     
18493     /**
18494      * move modules into their correct place in the tree..
18495      * 
18496      */
18497     preBuild : function ()
18498     {
18499         var _t = this;
18500         Roo.each(this.modules , function (obj)
18501         {
18502             Roo.XComponent.event.fireEvent('beforebuild', obj);
18503             
18504             var opar = obj.parent;
18505             try { 
18506                 obj.parent = this.toObject(opar);
18507             } catch(e) {
18508                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
18509                 return;
18510             }
18511             
18512             if (!obj.parent) {
18513                 Roo.debug && Roo.log("GOT top level module");
18514                 Roo.debug && Roo.log(obj);
18515                 obj.modules = new Roo.util.MixedCollection(false, 
18516                     function(o) { return o.order + '' }
18517                 );
18518                 this.topModule = obj;
18519                 return;
18520             }
18521                         // parent is a string (usually a dom element name..)
18522             if (typeof(obj.parent) == 'string') {
18523                 this.elmodules.push(obj);
18524                 return;
18525             }
18526             if (obj.parent.constructor != Roo.XComponent) {
18527                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
18528             }
18529             if (!obj.parent.modules) {
18530                 obj.parent.modules = new Roo.util.MixedCollection(false, 
18531                     function(o) { return o.order + '' }
18532                 );
18533             }
18534             if (obj.parent.disabled) {
18535                 obj.disabled = true;
18536             }
18537             obj.parent.modules.add(obj);
18538         }, this);
18539     },
18540     
18541      /**
18542      * make a list of modules to build.
18543      * @return {Array} list of modules. 
18544      */ 
18545     
18546     buildOrder : function()
18547     {
18548         var _this = this;
18549         var cmp = function(a,b) {   
18550             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
18551         };
18552         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
18553             throw "No top level modules to build";
18554         }
18555         
18556         // make a flat list in order of modules to build.
18557         var mods = this.topModule ? [ this.topModule ] : [];
18558                 
18559         
18560         // elmodules (is a list of DOM based modules )
18561         Roo.each(this.elmodules, function(e) {
18562             mods.push(e);
18563             if (!this.topModule &&
18564                 typeof(e.parent) == 'string' &&
18565                 e.parent.substring(0,1) == '#' &&
18566                 Roo.get(e.parent.substr(1))
18567                ) {
18568                 
18569                 _this.topModule = e;
18570             }
18571             
18572         });
18573
18574         
18575         // add modules to their parents..
18576         var addMod = function(m) {
18577             Roo.debug && Roo.log("build Order: add: " + m.name);
18578                 
18579             mods.push(m);
18580             if (m.modules && !m.disabled) {
18581                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
18582                 m.modules.keySort('ASC',  cmp );
18583                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
18584     
18585                 m.modules.each(addMod);
18586             } else {
18587                 Roo.debug && Roo.log("build Order: no child modules");
18588             }
18589             // not sure if this is used any more..
18590             if (m.finalize) {
18591                 m.finalize.name = m.name + " (clean up) ";
18592                 mods.push(m.finalize);
18593             }
18594             
18595         }
18596         if (this.topModule && this.topModule.modules) { 
18597             this.topModule.modules.keySort('ASC',  cmp );
18598             this.topModule.modules.each(addMod);
18599         } 
18600         return mods;
18601     },
18602     
18603      /**
18604      * Build the registered modules.
18605      * @param {Object} parent element.
18606      * @param {Function} optional method to call after module has been added.
18607      * 
18608      */ 
18609    
18610     build : function(opts) 
18611     {
18612         
18613         if (typeof(opts) != 'undefined') {
18614             Roo.apply(this,opts);
18615         }
18616         
18617         this.preBuild();
18618         var mods = this.buildOrder();
18619       
18620         //this.allmods = mods;
18621         //Roo.debug && Roo.log(mods);
18622         //return;
18623         if (!mods.length) { // should not happen
18624             throw "NO modules!!!";
18625         }
18626         
18627         
18628         var msg = "Building Interface...";
18629         // flash it up as modal - so we store the mask!?
18630         if (!this.hideProgress && Roo.MessageBox) {
18631             Roo.MessageBox.show({ title: 'loading' });
18632             Roo.MessageBox.show({
18633                title: "Please wait...",
18634                msg: msg,
18635                width:450,
18636                progress:true,
18637                buttons : false,
18638                closable:false,
18639                modal: false
18640               
18641             });
18642         }
18643         var total = mods.length;
18644         
18645         var _this = this;
18646         var progressRun = function() {
18647             if (!mods.length) {
18648                 Roo.debug && Roo.log('hide?');
18649                 if (!this.hideProgress && Roo.MessageBox) {
18650                     Roo.MessageBox.hide();
18651                 }
18652                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
18653                 
18654                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
18655                 
18656                 // THE END...
18657                 return false;   
18658             }
18659             
18660             var m = mods.shift();
18661             
18662             
18663             Roo.debug && Roo.log(m);
18664             // not sure if this is supported any more.. - modules that are are just function
18665             if (typeof(m) == 'function') { 
18666                 m.call(this);
18667                 return progressRun.defer(10, _this);
18668             } 
18669             
18670             
18671             msg = "Building Interface " + (total  - mods.length) + 
18672                     " of " + total + 
18673                     (m.name ? (' - ' + m.name) : '');
18674                         Roo.debug && Roo.log(msg);
18675             if (!_this.hideProgress &&  Roo.MessageBox) { 
18676                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
18677             }
18678             
18679          
18680             // is the module disabled?
18681             var disabled = (typeof(m.disabled) == 'function') ?
18682                 m.disabled.call(m.module.disabled) : m.disabled;    
18683             
18684             
18685             if (disabled) {
18686                 return progressRun(); // we do not update the display!
18687             }
18688             
18689             // now build 
18690             
18691                         
18692                         
18693             m.render();
18694             // it's 10 on top level, and 1 on others??? why...
18695             return progressRun.defer(10, _this);
18696              
18697         }
18698         progressRun.defer(1, _this);
18699      
18700         
18701         
18702     },
18703     /**
18704      * Overlay a set of modified strings onto a component
18705      * This is dependant on our builder exporting the strings and 'named strings' elements.
18706      * 
18707      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
18708      * @param {Object} associative array of 'named' string and it's new value.
18709      * 
18710      */
18711         overlayStrings : function( component, strings )
18712     {
18713         if (typeof(component['_named_strings']) == 'undefined') {
18714             throw "ERROR: component does not have _named_strings";
18715         }
18716         for ( var k in strings ) {
18717             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
18718             if (md !== false) {
18719                 component['_strings'][md] = strings[k];
18720             } else {
18721                 Roo.log('could not find named string: ' + k + ' in');
18722                 Roo.log(component);
18723             }
18724             
18725         }
18726         
18727     },
18728     
18729         
18730         /**
18731          * Event Object.
18732          *
18733          *
18734          */
18735         event: false, 
18736     /**
18737          * wrapper for event.on - aliased later..  
18738          * Typically use to register a event handler for register:
18739          *
18740          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
18741          *
18742          */
18743     on : false
18744    
18745     
18746     
18747 });
18748
18749 Roo.XComponent.event = new Roo.util.Observable({
18750                 events : { 
18751                         /**
18752                          * @event register
18753                          * Fires when an Component is registered,
18754                          * set the disable property on the Component to stop registration.
18755                          * @param {Roo.XComponent} c the component being registerd.
18756                          * 
18757                          */
18758                         'register' : true,
18759             /**
18760                          * @event beforebuild
18761                          * Fires before each Component is built
18762                          * can be used to apply permissions.
18763                          * @param {Roo.XComponent} c the component being registerd.
18764                          * 
18765                          */
18766                         'beforebuild' : true,
18767                         /**
18768                          * @event buildcomplete
18769                          * Fires on the top level element when all elements have been built
18770                          * @param {Roo.XComponent} the top level component.
18771                          */
18772                         'buildcomplete' : true
18773                         
18774                 }
18775 });
18776
18777 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
18778  //
18779  /**
18780  * marked - a markdown parser
18781  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
18782  * https://github.com/chjj/marked
18783  */
18784
18785
18786 /**
18787  *
18788  * Roo.Markdown - is a very crude wrapper around marked..
18789  *
18790  * usage:
18791  * 
18792  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
18793  * 
18794  * Note: move the sample code to the bottom of this
18795  * file before uncommenting it.
18796  *
18797  */
18798
18799 Roo.Markdown = {};
18800 Roo.Markdown.toHtml = function(text) {
18801     
18802     var c = new Roo.Markdown.marked.setOptions({
18803             renderer: new Roo.Markdown.marked.Renderer(),
18804             gfm: true,
18805             tables: true,
18806             breaks: false,
18807             pedantic: false,
18808             sanitize: false,
18809             smartLists: true,
18810             smartypants: false
18811           });
18812     // A FEW HACKS!!?
18813     
18814     text = text.replace(/\\\n/g,' ');
18815     return Roo.Markdown.marked(text);
18816 };
18817 //
18818 // converter
18819 //
18820 // Wraps all "globals" so that the only thing
18821 // exposed is makeHtml().
18822 //
18823 (function() {
18824     
18825      /**
18826          * eval:var:escape
18827          * eval:var:unescape
18828          * eval:var:replace
18829          */
18830       
18831     /**
18832      * Helpers
18833      */
18834     
18835     var escape = function (html, encode) {
18836       return html
18837         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
18838         .replace(/</g, '&lt;')
18839         .replace(/>/g, '&gt;')
18840         .replace(/"/g, '&quot;')
18841         .replace(/'/g, '&#39;');
18842     }
18843     
18844     var unescape = function (html) {
18845         // explicitly match decimal, hex, and named HTML entities 
18846       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
18847         n = n.toLowerCase();
18848         if (n === 'colon') { return ':'; }
18849         if (n.charAt(0) === '#') {
18850           return n.charAt(1) === 'x'
18851             ? String.fromCharCode(parseInt(n.substring(2), 16))
18852             : String.fromCharCode(+n.substring(1));
18853         }
18854         return '';
18855       });
18856     }
18857     
18858     var replace = function (regex, opt) {
18859       regex = regex.source;
18860       opt = opt || '';
18861       return function self(name, val) {
18862         if (!name) { return new RegExp(regex, opt); }
18863         val = val.source || val;
18864         val = val.replace(/(^|[^\[])\^/g, '$1');
18865         regex = regex.replace(name, val);
18866         return self;
18867       };
18868     }
18869
18870
18871          /**
18872          * eval:var:noop
18873     */
18874     var noop = function () {}
18875     noop.exec = noop;
18876     
18877          /**
18878          * eval:var:merge
18879     */
18880     var merge = function (obj) {
18881       var i = 1
18882         , target
18883         , key;
18884     
18885       for (; i < arguments.length; i++) {
18886         target = arguments[i];
18887         for (key in target) {
18888           if (Object.prototype.hasOwnProperty.call(target, key)) {
18889             obj[key] = target[key];
18890           }
18891         }
18892       }
18893     
18894       return obj;
18895     }
18896     
18897     
18898     /**
18899      * Block-Level Grammar
18900      */
18901     
18902     
18903     
18904     
18905     var block = {
18906       newline: /^\n+/,
18907       code: /^( {4}[^\n]+\n*)+/,
18908       fences: noop,
18909       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
18910       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
18911       nptable: noop,
18912       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
18913       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
18914       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
18915       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
18916       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
18917       table: noop,
18918       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
18919       text: /^[^\n]+/
18920     };
18921     
18922     block.bullet = /(?:[*+-]|\d+\.)/;
18923     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
18924     block.item = replace(block.item, 'gm')
18925       (/bull/g, block.bullet)
18926       ();
18927     
18928     block.list = replace(block.list)
18929       (/bull/g, block.bullet)
18930       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
18931       ('def', '\\n+(?=' + block.def.source + ')')
18932       ();
18933     
18934     block.blockquote = replace(block.blockquote)
18935       ('def', block.def)
18936       ();
18937     
18938     block._tag = '(?!(?:'
18939       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
18940       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
18941       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
18942     
18943     block.html = replace(block.html)
18944       ('comment', /<!--[\s\S]*?-->/)
18945       ('closed', /<(tag)[\s\S]+?<\/\1>/)
18946       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
18947       (/tag/g, block._tag)
18948       ();
18949     
18950     block.paragraph = replace(block.paragraph)
18951       ('hr', block.hr)
18952       ('heading', block.heading)
18953       ('lheading', block.lheading)
18954       ('blockquote', block.blockquote)
18955       ('tag', '<' + block._tag)
18956       ('def', block.def)
18957       ();
18958     
18959     /**
18960      * Normal Block Grammar
18961      */
18962     
18963     block.normal = merge({}, block);
18964     
18965     /**
18966      * GFM Block Grammar
18967      */
18968     
18969     block.gfm = merge({}, block.normal, {
18970       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
18971       paragraph: /^/,
18972       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
18973     });
18974     
18975     block.gfm.paragraph = replace(block.paragraph)
18976       ('(?!', '(?!'
18977         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
18978         + block.list.source.replace('\\1', '\\3') + '|')
18979       ();
18980     
18981     /**
18982      * GFM + Tables Block Grammar
18983      */
18984     
18985     block.tables = merge({}, block.gfm, {
18986       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
18987       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
18988     });
18989     
18990     /**
18991      * Block Lexer
18992      */
18993     
18994     var Lexer = function (options) {
18995       this.tokens = [];
18996       this.tokens.links = {};
18997       this.options = options || marked.defaults;
18998       this.rules = block.normal;
18999     
19000       if (this.options.gfm) {
19001         if (this.options.tables) {
19002           this.rules = block.tables;
19003         } else {
19004           this.rules = block.gfm;
19005         }
19006       }
19007     }
19008     
19009     /**
19010      * Expose Block Rules
19011      */
19012     
19013     Lexer.rules = block;
19014     
19015     /**
19016      * Static Lex Method
19017      */
19018     
19019     Lexer.lex = function(src, options) {
19020       var lexer = new Lexer(options);
19021       return lexer.lex(src);
19022     };
19023     
19024     /**
19025      * Preprocessing
19026      */
19027     
19028     Lexer.prototype.lex = function(src) {
19029       src = src
19030         .replace(/\r\n|\r/g, '\n')
19031         .replace(/\t/g, '    ')
19032         .replace(/\u00a0/g, ' ')
19033         .replace(/\u2424/g, '\n');
19034     
19035       return this.token(src, true);
19036     };
19037     
19038     /**
19039      * Lexing
19040      */
19041     
19042     Lexer.prototype.token = function(src, top, bq) {
19043       var src = src.replace(/^ +$/gm, '')
19044         , next
19045         , loose
19046         , cap
19047         , bull
19048         , b
19049         , item
19050         , space
19051         , i
19052         , l;
19053     
19054       while (src) {
19055         // newline
19056         if (cap = this.rules.newline.exec(src)) {
19057           src = src.substring(cap[0].length);
19058           if (cap[0].length > 1) {
19059             this.tokens.push({
19060               type: 'space'
19061             });
19062           }
19063         }
19064     
19065         // code
19066         if (cap = this.rules.code.exec(src)) {
19067           src = src.substring(cap[0].length);
19068           cap = cap[0].replace(/^ {4}/gm, '');
19069           this.tokens.push({
19070             type: 'code',
19071             text: !this.options.pedantic
19072               ? cap.replace(/\n+$/, '')
19073               : cap
19074           });
19075           continue;
19076         }
19077     
19078         // fences (gfm)
19079         if (cap = this.rules.fences.exec(src)) {
19080           src = src.substring(cap[0].length);
19081           this.tokens.push({
19082             type: 'code',
19083             lang: cap[2],
19084             text: cap[3] || ''
19085           });
19086           continue;
19087         }
19088     
19089         // heading
19090         if (cap = this.rules.heading.exec(src)) {
19091           src = src.substring(cap[0].length);
19092           this.tokens.push({
19093             type: 'heading',
19094             depth: cap[1].length,
19095             text: cap[2]
19096           });
19097           continue;
19098         }
19099     
19100         // table no leading pipe (gfm)
19101         if (top && (cap = this.rules.nptable.exec(src))) {
19102           src = src.substring(cap[0].length);
19103     
19104           item = {
19105             type: 'table',
19106             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
19107             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
19108             cells: cap[3].replace(/\n$/, '').split('\n')
19109           };
19110     
19111           for (i = 0; i < item.align.length; i++) {
19112             if (/^ *-+: *$/.test(item.align[i])) {
19113               item.align[i] = 'right';
19114             } else if (/^ *:-+: *$/.test(item.align[i])) {
19115               item.align[i] = 'center';
19116             } else if (/^ *:-+ *$/.test(item.align[i])) {
19117               item.align[i] = 'left';
19118             } else {
19119               item.align[i] = null;
19120             }
19121           }
19122     
19123           for (i = 0; i < item.cells.length; i++) {
19124             item.cells[i] = item.cells[i].split(/ *\| */);
19125           }
19126     
19127           this.tokens.push(item);
19128     
19129           continue;
19130         }
19131     
19132         // lheading
19133         if (cap = this.rules.lheading.exec(src)) {
19134           src = src.substring(cap[0].length);
19135           this.tokens.push({
19136             type: 'heading',
19137             depth: cap[2] === '=' ? 1 : 2,
19138             text: cap[1]
19139           });
19140           continue;
19141         }
19142     
19143         // hr
19144         if (cap = this.rules.hr.exec(src)) {
19145           src = src.substring(cap[0].length);
19146           this.tokens.push({
19147             type: 'hr'
19148           });
19149           continue;
19150         }
19151     
19152         // blockquote
19153         if (cap = this.rules.blockquote.exec(src)) {
19154           src = src.substring(cap[0].length);
19155     
19156           this.tokens.push({
19157             type: 'blockquote_start'
19158           });
19159     
19160           cap = cap[0].replace(/^ *> ?/gm, '');
19161     
19162           // Pass `top` to keep the current
19163           // "toplevel" state. This is exactly
19164           // how markdown.pl works.
19165           this.token(cap, top, true);
19166     
19167           this.tokens.push({
19168             type: 'blockquote_end'
19169           });
19170     
19171           continue;
19172         }
19173     
19174         // list
19175         if (cap = this.rules.list.exec(src)) {
19176           src = src.substring(cap[0].length);
19177           bull = cap[2];
19178     
19179           this.tokens.push({
19180             type: 'list_start',
19181             ordered: bull.length > 1
19182           });
19183     
19184           // Get each top-level item.
19185           cap = cap[0].match(this.rules.item);
19186     
19187           next = false;
19188           l = cap.length;
19189           i = 0;
19190     
19191           for (; i < l; i++) {
19192             item = cap[i];
19193     
19194             // Remove the list item's bullet
19195             // so it is seen as the next token.
19196             space = item.length;
19197             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
19198     
19199             // Outdent whatever the
19200             // list item contains. Hacky.
19201             if (~item.indexOf('\n ')) {
19202               space -= item.length;
19203               item = !this.options.pedantic
19204                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
19205                 : item.replace(/^ {1,4}/gm, '');
19206             }
19207     
19208             // Determine whether the next list item belongs here.
19209             // Backpedal if it does not belong in this list.
19210             if (this.options.smartLists && i !== l - 1) {
19211               b = block.bullet.exec(cap[i + 1])[0];
19212               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
19213                 src = cap.slice(i + 1).join('\n') + src;
19214                 i = l - 1;
19215               }
19216             }
19217     
19218             // Determine whether item is loose or not.
19219             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
19220             // for discount behavior.
19221             loose = next || /\n\n(?!\s*$)/.test(item);
19222             if (i !== l - 1) {
19223               next = item.charAt(item.length - 1) === '\n';
19224               if (!loose) { loose = next; }
19225             }
19226     
19227             this.tokens.push({
19228               type: loose
19229                 ? 'loose_item_start'
19230                 : 'list_item_start'
19231             });
19232     
19233             // Recurse.
19234             this.token(item, false, bq);
19235     
19236             this.tokens.push({
19237               type: 'list_item_end'
19238             });
19239           }
19240     
19241           this.tokens.push({
19242             type: 'list_end'
19243           });
19244     
19245           continue;
19246         }
19247     
19248         // html
19249         if (cap = this.rules.html.exec(src)) {
19250           src = src.substring(cap[0].length);
19251           this.tokens.push({
19252             type: this.options.sanitize
19253               ? 'paragraph'
19254               : 'html',
19255             pre: !this.options.sanitizer
19256               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
19257             text: cap[0]
19258           });
19259           continue;
19260         }
19261     
19262         // def
19263         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
19264           src = src.substring(cap[0].length);
19265           this.tokens.links[cap[1].toLowerCase()] = {
19266             href: cap[2],
19267             title: cap[3]
19268           };
19269           continue;
19270         }
19271     
19272         // table (gfm)
19273         if (top && (cap = this.rules.table.exec(src))) {
19274           src = src.substring(cap[0].length);
19275     
19276           item = {
19277             type: 'table',
19278             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
19279             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
19280             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
19281           };
19282     
19283           for (i = 0; i < item.align.length; i++) {
19284             if (/^ *-+: *$/.test(item.align[i])) {
19285               item.align[i] = 'right';
19286             } else if (/^ *:-+: *$/.test(item.align[i])) {
19287               item.align[i] = 'center';
19288             } else if (/^ *:-+ *$/.test(item.align[i])) {
19289               item.align[i] = 'left';
19290             } else {
19291               item.align[i] = null;
19292             }
19293           }
19294     
19295           for (i = 0; i < item.cells.length; i++) {
19296             item.cells[i] = item.cells[i]
19297               .replace(/^ *\| *| *\| *$/g, '')
19298               .split(/ *\| */);
19299           }
19300     
19301           this.tokens.push(item);
19302     
19303           continue;
19304         }
19305     
19306         // top-level paragraph
19307         if (top && (cap = this.rules.paragraph.exec(src))) {
19308           src = src.substring(cap[0].length);
19309           this.tokens.push({
19310             type: 'paragraph',
19311             text: cap[1].charAt(cap[1].length - 1) === '\n'
19312               ? cap[1].slice(0, -1)
19313               : cap[1]
19314           });
19315           continue;
19316         }
19317     
19318         // text
19319         if (cap = this.rules.text.exec(src)) {
19320           // Top-level should never reach here.
19321           src = src.substring(cap[0].length);
19322           this.tokens.push({
19323             type: 'text',
19324             text: cap[0]
19325           });
19326           continue;
19327         }
19328     
19329         if (src) {
19330           throw new
19331             Error('Infinite loop on byte: ' + src.charCodeAt(0));
19332         }
19333       }
19334     
19335       return this.tokens;
19336     };
19337     
19338     /**
19339      * Inline-Level Grammar
19340      */
19341     
19342     var inline = {
19343       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
19344       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
19345       url: noop,
19346       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
19347       link: /^!?\[(inside)\]\(href\)/,
19348       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
19349       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
19350       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
19351       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
19352       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
19353       br: /^ {2,}\n(?!\s*$)/,
19354       del: noop,
19355       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
19356     };
19357     
19358     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
19359     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
19360     
19361     inline.link = replace(inline.link)
19362       ('inside', inline._inside)
19363       ('href', inline._href)
19364       ();
19365     
19366     inline.reflink = replace(inline.reflink)
19367       ('inside', inline._inside)
19368       ();
19369     
19370     /**
19371      * Normal Inline Grammar
19372      */
19373     
19374     inline.normal = merge({}, inline);
19375     
19376     /**
19377      * Pedantic Inline Grammar
19378      */
19379     
19380     inline.pedantic = merge({}, inline.normal, {
19381       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
19382       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
19383     });
19384     
19385     /**
19386      * GFM Inline Grammar
19387      */
19388     
19389     inline.gfm = merge({}, inline.normal, {
19390       escape: replace(inline.escape)('])', '~|])')(),
19391       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
19392       del: /^~~(?=\S)([\s\S]*?\S)~~/,
19393       text: replace(inline.text)
19394         (']|', '~]|')
19395         ('|', '|https?://|')
19396         ()
19397     });
19398     
19399     /**
19400      * GFM + Line Breaks Inline Grammar
19401      */
19402     
19403     inline.breaks = merge({}, inline.gfm, {
19404       br: replace(inline.br)('{2,}', '*')(),
19405       text: replace(inline.gfm.text)('{2,}', '*')()
19406     });
19407     
19408     /**
19409      * Inline Lexer & Compiler
19410      */
19411     
19412     var InlineLexer  = function (links, options) {
19413       this.options = options || marked.defaults;
19414       this.links = links;
19415       this.rules = inline.normal;
19416       this.renderer = this.options.renderer || new Renderer;
19417       this.renderer.options = this.options;
19418     
19419       if (!this.links) {
19420         throw new
19421           Error('Tokens array requires a `links` property.');
19422       }
19423     
19424       if (this.options.gfm) {
19425         if (this.options.breaks) {
19426           this.rules = inline.breaks;
19427         } else {
19428           this.rules = inline.gfm;
19429         }
19430       } else if (this.options.pedantic) {
19431         this.rules = inline.pedantic;
19432       }
19433     }
19434     
19435     /**
19436      * Expose Inline Rules
19437      */
19438     
19439     InlineLexer.rules = inline;
19440     
19441     /**
19442      * Static Lexing/Compiling Method
19443      */
19444     
19445     InlineLexer.output = function(src, links, options) {
19446       var inline = new InlineLexer(links, options);
19447       return inline.output(src);
19448     };
19449     
19450     /**
19451      * Lexing/Compiling
19452      */
19453     
19454     InlineLexer.prototype.output = function(src) {
19455       var out = ''
19456         , link
19457         , text
19458         , href
19459         , cap;
19460     
19461       while (src) {
19462         // escape
19463         if (cap = this.rules.escape.exec(src)) {
19464           src = src.substring(cap[0].length);
19465           out += cap[1];
19466           continue;
19467         }
19468     
19469         // autolink
19470         if (cap = this.rules.autolink.exec(src)) {
19471           src = src.substring(cap[0].length);
19472           if (cap[2] === '@') {
19473             text = cap[1].charAt(6) === ':'
19474               ? this.mangle(cap[1].substring(7))
19475               : this.mangle(cap[1]);
19476             href = this.mangle('mailto:') + text;
19477           } else {
19478             text = escape(cap[1]);
19479             href = text;
19480           }
19481           out += this.renderer.link(href, null, text);
19482           continue;
19483         }
19484     
19485         // url (gfm)
19486         if (!this.inLink && (cap = this.rules.url.exec(src))) {
19487           src = src.substring(cap[0].length);
19488           text = escape(cap[1]);
19489           href = text;
19490           out += this.renderer.link(href, null, text);
19491           continue;
19492         }
19493     
19494         // tag
19495         if (cap = this.rules.tag.exec(src)) {
19496           if (!this.inLink && /^<a /i.test(cap[0])) {
19497             this.inLink = true;
19498           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
19499             this.inLink = false;
19500           }
19501           src = src.substring(cap[0].length);
19502           out += this.options.sanitize
19503             ? this.options.sanitizer
19504               ? this.options.sanitizer(cap[0])
19505               : escape(cap[0])
19506             : cap[0];
19507           continue;
19508         }
19509     
19510         // link
19511         if (cap = this.rules.link.exec(src)) {
19512           src = src.substring(cap[0].length);
19513           this.inLink = true;
19514           out += this.outputLink(cap, {
19515             href: cap[2],
19516             title: cap[3]
19517           });
19518           this.inLink = false;
19519           continue;
19520         }
19521     
19522         // reflink, nolink
19523         if ((cap = this.rules.reflink.exec(src))
19524             || (cap = this.rules.nolink.exec(src))) {
19525           src = src.substring(cap[0].length);
19526           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
19527           link = this.links[link.toLowerCase()];
19528           if (!link || !link.href) {
19529             out += cap[0].charAt(0);
19530             src = cap[0].substring(1) + src;
19531             continue;
19532           }
19533           this.inLink = true;
19534           out += this.outputLink(cap, link);
19535           this.inLink = false;
19536           continue;
19537         }
19538     
19539         // strong
19540         if (cap = this.rules.strong.exec(src)) {
19541           src = src.substring(cap[0].length);
19542           out += this.renderer.strong(this.output(cap[2] || cap[1]));
19543           continue;
19544         }
19545     
19546         // em
19547         if (cap = this.rules.em.exec(src)) {
19548           src = src.substring(cap[0].length);
19549           out += this.renderer.em(this.output(cap[2] || cap[1]));
19550           continue;
19551         }
19552     
19553         // code
19554         if (cap = this.rules.code.exec(src)) {
19555           src = src.substring(cap[0].length);
19556           out += this.renderer.codespan(escape(cap[2], true));
19557           continue;
19558         }
19559     
19560         // br
19561         if (cap = this.rules.br.exec(src)) {
19562           src = src.substring(cap[0].length);
19563           out += this.renderer.br();
19564           continue;
19565         }
19566     
19567         // del (gfm)
19568         if (cap = this.rules.del.exec(src)) {
19569           src = src.substring(cap[0].length);
19570           out += this.renderer.del(this.output(cap[1]));
19571           continue;
19572         }
19573     
19574         // text
19575         if (cap = this.rules.text.exec(src)) {
19576           src = src.substring(cap[0].length);
19577           out += this.renderer.text(escape(this.smartypants(cap[0])));
19578           continue;
19579         }
19580     
19581         if (src) {
19582           throw new
19583             Error('Infinite loop on byte: ' + src.charCodeAt(0));
19584         }
19585       }
19586     
19587       return out;
19588     };
19589     
19590     /**
19591      * Compile Link
19592      */
19593     
19594     InlineLexer.prototype.outputLink = function(cap, link) {
19595       var href = escape(link.href)
19596         , title = link.title ? escape(link.title) : null;
19597     
19598       return cap[0].charAt(0) !== '!'
19599         ? this.renderer.link(href, title, this.output(cap[1]))
19600         : this.renderer.image(href, title, escape(cap[1]));
19601     };
19602     
19603     /**
19604      * Smartypants Transformations
19605      */
19606     
19607     InlineLexer.prototype.smartypants = function(text) {
19608       if (!this.options.smartypants)  { return text; }
19609       return text
19610         // em-dashes
19611         .replace(/---/g, '\u2014')
19612         // en-dashes
19613         .replace(/--/g, '\u2013')
19614         // opening singles
19615         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
19616         // closing singles & apostrophes
19617         .replace(/'/g, '\u2019')
19618         // opening doubles
19619         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
19620         // closing doubles
19621         .replace(/"/g, '\u201d')
19622         // ellipses
19623         .replace(/\.{3}/g, '\u2026');
19624     };
19625     
19626     /**
19627      * Mangle Links
19628      */
19629     
19630     InlineLexer.prototype.mangle = function(text) {
19631       if (!this.options.mangle) { return text; }
19632       var out = ''
19633         , l = text.length
19634         , i = 0
19635         , ch;
19636     
19637       for (; i < l; i++) {
19638         ch = text.charCodeAt(i);
19639         if (Math.random() > 0.5) {
19640           ch = 'x' + ch.toString(16);
19641         }
19642         out += '&#' + ch + ';';
19643       }
19644     
19645       return out;
19646     };
19647     
19648     /**
19649      * Renderer
19650      */
19651     
19652      /**
19653          * eval:var:Renderer
19654     */
19655     
19656     var Renderer   = function (options) {
19657       this.options = options || {};
19658     }
19659     
19660     Renderer.prototype.code = function(code, lang, escaped) {
19661       if (this.options.highlight) {
19662         var out = this.options.highlight(code, lang);
19663         if (out != null && out !== code) {
19664           escaped = true;
19665           code = out;
19666         }
19667       } else {
19668             // hack!!! - it's already escapeD?
19669             escaped = true;
19670       }
19671     
19672       if (!lang) {
19673         return '<pre><code>'
19674           + (escaped ? code : escape(code, true))
19675           + '\n</code></pre>';
19676       }
19677     
19678       return '<pre><code class="'
19679         + this.options.langPrefix
19680         + escape(lang, true)
19681         + '">'
19682         + (escaped ? code : escape(code, true))
19683         + '\n</code></pre>\n';
19684     };
19685     
19686     Renderer.prototype.blockquote = function(quote) {
19687       return '<blockquote>\n' + quote + '</blockquote>\n';
19688     };
19689     
19690     Renderer.prototype.html = function(html) {
19691       return html;
19692     };
19693     
19694     Renderer.prototype.heading = function(text, level, raw) {
19695       return '<h'
19696         + level
19697         + ' id="'
19698         + this.options.headerPrefix
19699         + raw.toLowerCase().replace(/[^\w]+/g, '-')
19700         + '">'
19701         + text
19702         + '</h'
19703         + level
19704         + '>\n';
19705     };
19706     
19707     Renderer.prototype.hr = function() {
19708       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
19709     };
19710     
19711     Renderer.prototype.list = function(body, ordered) {
19712       var type = ordered ? 'ol' : 'ul';
19713       return '<' + type + '>\n' + body + '</' + type + '>\n';
19714     };
19715     
19716     Renderer.prototype.listitem = function(text) {
19717       return '<li>' + text + '</li>\n';
19718     };
19719     
19720     Renderer.prototype.paragraph = function(text) {
19721       return '<p>' + text + '</p>\n';
19722     };
19723     
19724     Renderer.prototype.table = function(header, body) {
19725       return '<table class="table table-striped">\n'
19726         + '<thead>\n'
19727         + header
19728         + '</thead>\n'
19729         + '<tbody>\n'
19730         + body
19731         + '</tbody>\n'
19732         + '</table>\n';
19733     };
19734     
19735     Renderer.prototype.tablerow = function(content) {
19736       return '<tr>\n' + content + '</tr>\n';
19737     };
19738     
19739     Renderer.prototype.tablecell = function(content, flags) {
19740       var type = flags.header ? 'th' : 'td';
19741       var tag = flags.align
19742         ? '<' + type + ' style="text-align:' + flags.align + '">'
19743         : '<' + type + '>';
19744       return tag + content + '</' + type + '>\n';
19745     };
19746     
19747     // span level renderer
19748     Renderer.prototype.strong = function(text) {
19749       return '<strong>' + text + '</strong>';
19750     };
19751     
19752     Renderer.prototype.em = function(text) {
19753       return '<em>' + text + '</em>';
19754     };
19755     
19756     Renderer.prototype.codespan = function(text) {
19757       return '<code>' + text + '</code>';
19758     };
19759     
19760     Renderer.prototype.br = function() {
19761       return this.options.xhtml ? '<br/>' : '<br>';
19762     };
19763     
19764     Renderer.prototype.del = function(text) {
19765       return '<del>' + text + '</del>';
19766     };
19767     
19768     Renderer.prototype.link = function(href, title, text) {
19769       if (this.options.sanitize) {
19770         try {
19771           var prot = decodeURIComponent(unescape(href))
19772             .replace(/[^\w:]/g, '')
19773             .toLowerCase();
19774         } catch (e) {
19775           return '';
19776         }
19777         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
19778           return '';
19779         }
19780       }
19781       var out = '<a href="' + href + '"';
19782       if (title) {
19783         out += ' title="' + title + '"';
19784       }
19785       out += '>' + text + '</a>';
19786       return out;
19787     };
19788     
19789     Renderer.prototype.image = function(href, title, text) {
19790       var out = '<img src="' + href + '" alt="' + text + '"';
19791       if (title) {
19792         out += ' title="' + title + '"';
19793       }
19794       out += this.options.xhtml ? '/>' : '>';
19795       return out;
19796     };
19797     
19798     Renderer.prototype.text = function(text) {
19799       return text;
19800     };
19801     
19802     /**
19803      * Parsing & Compiling
19804      */
19805          /**
19806          * eval:var:Parser
19807     */
19808     
19809     var Parser= function (options) {
19810       this.tokens = [];
19811       this.token = null;
19812       this.options = options || marked.defaults;
19813       this.options.renderer = this.options.renderer || new Renderer;
19814       this.renderer = this.options.renderer;
19815       this.renderer.options = this.options;
19816     }
19817     
19818     /**
19819      * Static Parse Method
19820      */
19821     
19822     Parser.parse = function(src, options, renderer) {
19823       var parser = new Parser(options, renderer);
19824       return parser.parse(src);
19825     };
19826     
19827     /**
19828      * Parse Loop
19829      */
19830     
19831     Parser.prototype.parse = function(src) {
19832       this.inline = new InlineLexer(src.links, this.options, this.renderer);
19833       this.tokens = src.reverse();
19834     
19835       var out = '';
19836       while (this.next()) {
19837         out += this.tok();
19838       }
19839     
19840       return out;
19841     };
19842     
19843     /**
19844      * Next Token
19845      */
19846     
19847     Parser.prototype.next = function() {
19848       return this.token = this.tokens.pop();
19849     };
19850     
19851     /**
19852      * Preview Next Token
19853      */
19854     
19855     Parser.prototype.peek = function() {
19856       return this.tokens[this.tokens.length - 1] || 0;
19857     };
19858     
19859     /**
19860      * Parse Text Tokens
19861      */
19862     
19863     Parser.prototype.parseText = function() {
19864       var body = this.token.text;
19865     
19866       while (this.peek().type === 'text') {
19867         body += '\n' + this.next().text;
19868       }
19869     
19870       return this.inline.output(body);
19871     };
19872     
19873     /**
19874      * Parse Current Token
19875      */
19876     
19877     Parser.prototype.tok = function() {
19878       switch (this.token.type) {
19879         case 'space': {
19880           return '';
19881         }
19882         case 'hr': {
19883           return this.renderer.hr();
19884         }
19885         case 'heading': {
19886           return this.renderer.heading(
19887             this.inline.output(this.token.text),
19888             this.token.depth,
19889             this.token.text);
19890         }
19891         case 'code': {
19892           return this.renderer.code(this.token.text,
19893             this.token.lang,
19894             this.token.escaped);
19895         }
19896         case 'table': {
19897           var header = ''
19898             , body = ''
19899             , i
19900             , row
19901             , cell
19902             , flags
19903             , j;
19904     
19905           // header
19906           cell = '';
19907           for (i = 0; i < this.token.header.length; i++) {
19908             flags = { header: true, align: this.token.align[i] };
19909             cell += this.renderer.tablecell(
19910               this.inline.output(this.token.header[i]),
19911               { header: true, align: this.token.align[i] }
19912             );
19913           }
19914           header += this.renderer.tablerow(cell);
19915     
19916           for (i = 0; i < this.token.cells.length; i++) {
19917             row = this.token.cells[i];
19918     
19919             cell = '';
19920             for (j = 0; j < row.length; j++) {
19921               cell += this.renderer.tablecell(
19922                 this.inline.output(row[j]),
19923                 { header: false, align: this.token.align[j] }
19924               );
19925             }
19926     
19927             body += this.renderer.tablerow(cell);
19928           }
19929           return this.renderer.table(header, body);
19930         }
19931         case 'blockquote_start': {
19932           var body = '';
19933     
19934           while (this.next().type !== 'blockquote_end') {
19935             body += this.tok();
19936           }
19937     
19938           return this.renderer.blockquote(body);
19939         }
19940         case 'list_start': {
19941           var body = ''
19942             , ordered = this.token.ordered;
19943     
19944           while (this.next().type !== 'list_end') {
19945             body += this.tok();
19946           }
19947     
19948           return this.renderer.list(body, ordered);
19949         }
19950         case 'list_item_start': {
19951           var body = '';
19952     
19953           while (this.next().type !== 'list_item_end') {
19954             body += this.token.type === 'text'
19955               ? this.parseText()
19956               : this.tok();
19957           }
19958     
19959           return this.renderer.listitem(body);
19960         }
19961         case 'loose_item_start': {
19962           var body = '';
19963     
19964           while (this.next().type !== 'list_item_end') {
19965             body += this.tok();
19966           }
19967     
19968           return this.renderer.listitem(body);
19969         }
19970         case 'html': {
19971           var html = !this.token.pre && !this.options.pedantic
19972             ? this.inline.output(this.token.text)
19973             : this.token.text;
19974           return this.renderer.html(html);
19975         }
19976         case 'paragraph': {
19977           return this.renderer.paragraph(this.inline.output(this.token.text));
19978         }
19979         case 'text': {
19980           return this.renderer.paragraph(this.parseText());
19981         }
19982       }
19983     };
19984   
19985     
19986     /**
19987      * Marked
19988      */
19989          /**
19990          * eval:var:marked
19991     */
19992     var marked = function (src, opt, callback) {
19993       if (callback || typeof opt === 'function') {
19994         if (!callback) {
19995           callback = opt;
19996           opt = null;
19997         }
19998     
19999         opt = merge({}, marked.defaults, opt || {});
20000     
20001         var highlight = opt.highlight
20002           , tokens
20003           , pending
20004           , i = 0;
20005     
20006         try {
20007           tokens = Lexer.lex(src, opt)
20008         } catch (e) {
20009           return callback(e);
20010         }
20011     
20012         pending = tokens.length;
20013          /**
20014          * eval:var:done
20015     */
20016         var done = function(err) {
20017           if (err) {
20018             opt.highlight = highlight;
20019             return callback(err);
20020           }
20021     
20022           var out;
20023     
20024           try {
20025             out = Parser.parse(tokens, opt);
20026           } catch (e) {
20027             err = e;
20028           }
20029     
20030           opt.highlight = highlight;
20031     
20032           return err
20033             ? callback(err)
20034             : callback(null, out);
20035         };
20036     
20037         if (!highlight || highlight.length < 3) {
20038           return done();
20039         }
20040     
20041         delete opt.highlight;
20042     
20043         if (!pending) { return done(); }
20044     
20045         for (; i < tokens.length; i++) {
20046           (function(token) {
20047             if (token.type !== 'code') {
20048               return --pending || done();
20049             }
20050             return highlight(token.text, token.lang, function(err, code) {
20051               if (err) { return done(err); }
20052               if (code == null || code === token.text) {
20053                 return --pending || done();
20054               }
20055               token.text = code;
20056               token.escaped = true;
20057               --pending || done();
20058             });
20059           })(tokens[i]);
20060         }
20061     
20062         return;
20063       }
20064       try {
20065         if (opt) { opt = merge({}, marked.defaults, opt); }
20066         return Parser.parse(Lexer.lex(src, opt), opt);
20067       } catch (e) {
20068         e.message += '\nPlease report this to https://github.com/chjj/marked.';
20069         if ((opt || marked.defaults).silent) {
20070           return '<p>An error occured:</p><pre>'
20071             + escape(e.message + '', true)
20072             + '</pre>';
20073         }
20074         throw e;
20075       }
20076     }
20077     
20078     /**
20079      * Options
20080      */
20081     
20082     marked.options =
20083     marked.setOptions = function(opt) {
20084       merge(marked.defaults, opt);
20085       return marked;
20086     };
20087     
20088     marked.defaults = {
20089       gfm: true,
20090       tables: true,
20091       breaks: false,
20092       pedantic: false,
20093       sanitize: false,
20094       sanitizer: null,
20095       mangle: true,
20096       smartLists: false,
20097       silent: false,
20098       highlight: null,
20099       langPrefix: 'lang-',
20100       smartypants: false,
20101       headerPrefix: '',
20102       renderer: new Renderer,
20103       xhtml: false
20104     };
20105     
20106     /**
20107      * Expose
20108      */
20109     
20110     marked.Parser = Parser;
20111     marked.parser = Parser.parse;
20112     
20113     marked.Renderer = Renderer;
20114     
20115     marked.Lexer = Lexer;
20116     marked.lexer = Lexer.lex;
20117     
20118     marked.InlineLexer = InlineLexer;
20119     marked.inlineLexer = InlineLexer.output;
20120     
20121     marked.parse = marked;
20122     
20123     Roo.Markdown.marked = marked;
20124
20125 })();/*
20126  * Based on:
20127  * Ext JS Library 1.1.1
20128  * Copyright(c) 2006-2007, Ext JS, LLC.
20129  *
20130  * Originally Released Under LGPL - original licence link has changed is not relivant.
20131  *
20132  * Fork - LGPL
20133  * <script type="text/javascript">
20134  */
20135
20136
20137
20138 /*
20139  * These classes are derivatives of the similarly named classes in the YUI Library.
20140  * The original license:
20141  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
20142  * Code licensed under the BSD License:
20143  * http://developer.yahoo.net/yui/license.txt
20144  */
20145
20146 (function() {
20147
20148 var Event=Roo.EventManager;
20149 var Dom=Roo.lib.Dom;
20150
20151 /**
20152  * @class Roo.dd.DragDrop
20153  * @extends Roo.util.Observable
20154  * Defines the interface and base operation of items that that can be
20155  * dragged or can be drop targets.  It was designed to be extended, overriding
20156  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
20157  * Up to three html elements can be associated with a DragDrop instance:
20158  * <ul>
20159  * <li>linked element: the element that is passed into the constructor.
20160  * This is the element which defines the boundaries for interaction with
20161  * other DragDrop objects.</li>
20162  * <li>handle element(s): The drag operation only occurs if the element that
20163  * was clicked matches a handle element.  By default this is the linked
20164  * element, but there are times that you will want only a portion of the
20165  * linked element to initiate the drag operation, and the setHandleElId()
20166  * method provides a way to define this.</li>
20167  * <li>drag element: this represents the element that would be moved along
20168  * with the cursor during a drag operation.  By default, this is the linked
20169  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
20170  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
20171  * </li>
20172  * </ul>
20173  * This class should not be instantiated until the onload event to ensure that
20174  * the associated elements are available.
20175  * The following would define a DragDrop obj that would interact with any
20176  * other DragDrop obj in the "group1" group:
20177  * <pre>
20178  *  dd = new Roo.dd.DragDrop("div1", "group1");
20179  * </pre>
20180  * Since none of the event handlers have been implemented, nothing would
20181  * actually happen if you were to run the code above.  Normally you would
20182  * override this class or one of the default implementations, but you can
20183  * also override the methods you want on an instance of the class...
20184  * <pre>
20185  *  dd.onDragDrop = function(e, id) {
20186  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
20187  *  }
20188  * </pre>
20189  * @constructor
20190  * @param {String} id of the element that is linked to this instance
20191  * @param {String} sGroup the group of related DragDrop objects
20192  * @param {object} config an object containing configurable attributes
20193  *                Valid properties for DragDrop:
20194  *                    padding, isTarget, maintainOffset, primaryButtonOnly
20195  */
20196 Roo.dd.DragDrop = function(id, sGroup, config) {
20197     if (id) {
20198         this.init(id, sGroup, config);
20199     }
20200     
20201 };
20202
20203 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
20204
20205     /**
20206      * The id of the element associated with this object.  This is what we
20207      * refer to as the "linked element" because the size and position of
20208      * this element is used to determine when the drag and drop objects have
20209      * interacted.
20210      * @property id
20211      * @type String
20212      */
20213     id: null,
20214
20215     /**
20216      * Configuration attributes passed into the constructor
20217      * @property config
20218      * @type object
20219      */
20220     config: null,
20221
20222     /**
20223      * The id of the element that will be dragged.  By default this is same
20224      * as the linked element , but could be changed to another element. Ex:
20225      * Roo.dd.DDProxy
20226      * @property dragElId
20227      * @type String
20228      * @private
20229      */
20230     dragElId: null,
20231
20232     /**
20233      * the id of the element that initiates the drag operation.  By default
20234      * this is the linked element, but could be changed to be a child of this
20235      * element.  This lets us do things like only starting the drag when the
20236      * header element within the linked html element is clicked.
20237      * @property handleElId
20238      * @type String
20239      * @private
20240      */
20241     handleElId: null,
20242
20243     /**
20244      * An associative array of HTML tags that will be ignored if clicked.
20245      * @property invalidHandleTypes
20246      * @type {string: string}
20247      */
20248     invalidHandleTypes: null,
20249
20250     /**
20251      * An associative array of ids for elements that will be ignored if clicked
20252      * @property invalidHandleIds
20253      * @type {string: string}
20254      */
20255     invalidHandleIds: null,
20256
20257     /**
20258      * An indexted array of css class names for elements that will be ignored
20259      * if clicked.
20260      * @property invalidHandleClasses
20261      * @type string[]
20262      */
20263     invalidHandleClasses: null,
20264
20265     /**
20266      * The linked element's absolute X position at the time the drag was
20267      * started
20268      * @property startPageX
20269      * @type int
20270      * @private
20271      */
20272     startPageX: 0,
20273
20274     /**
20275      * The linked element's absolute X position at the time the drag was
20276      * started
20277      * @property startPageY
20278      * @type int
20279      * @private
20280      */
20281     startPageY: 0,
20282
20283     /**
20284      * The group defines a logical collection of DragDrop objects that are
20285      * related.  Instances only get events when interacting with other
20286      * DragDrop object in the same group.  This lets us define multiple
20287      * groups using a single DragDrop subclass if we want.
20288      * @property groups
20289      * @type {string: string}
20290      */
20291     groups: null,
20292
20293     /**
20294      * Individual drag/drop instances can be locked.  This will prevent
20295      * onmousedown start drag.
20296      * @property locked
20297      * @type boolean
20298      * @private
20299      */
20300     locked: false,
20301
20302     /**
20303      * Lock this instance
20304      * @method lock
20305      */
20306     lock: function() { this.locked = true; },
20307
20308     /**
20309      * Unlock this instace
20310      * @method unlock
20311      */
20312     unlock: function() { this.locked = false; },
20313
20314     /**
20315      * By default, all insances can be a drop target.  This can be disabled by
20316      * setting isTarget to false.
20317      * @method isTarget
20318      * @type boolean
20319      */
20320     isTarget: true,
20321
20322     /**
20323      * The padding configured for this drag and drop object for calculating
20324      * the drop zone intersection with this object.
20325      * @method padding
20326      * @type int[]
20327      */
20328     padding: null,
20329
20330     /**
20331      * Cached reference to the linked element
20332      * @property _domRef
20333      * @private
20334      */
20335     _domRef: null,
20336
20337     /**
20338      * Internal typeof flag
20339      * @property __ygDragDrop
20340      * @private
20341      */
20342     __ygDragDrop: true,
20343
20344     /**
20345      * Set to true when horizontal contraints are applied
20346      * @property constrainX
20347      * @type boolean
20348      * @private
20349      */
20350     constrainX: false,
20351
20352     /**
20353      * Set to true when vertical contraints are applied
20354      * @property constrainY
20355      * @type boolean
20356      * @private
20357      */
20358     constrainY: false,
20359
20360     /**
20361      * The left constraint
20362      * @property minX
20363      * @type int
20364      * @private
20365      */
20366     minX: 0,
20367
20368     /**
20369      * The right constraint
20370      * @property maxX
20371      * @type int
20372      * @private
20373      */
20374     maxX: 0,
20375
20376     /**
20377      * The up constraint
20378      * @property minY
20379      * @type int
20380      * @type int
20381      * @private
20382      */
20383     minY: 0,
20384
20385     /**
20386      * The down constraint
20387      * @property maxY
20388      * @type int
20389      * @private
20390      */
20391     maxY: 0,
20392
20393     /**
20394      * Maintain offsets when we resetconstraints.  Set to true when you want
20395      * the position of the element relative to its parent to stay the same
20396      * when the page changes
20397      *
20398      * @property maintainOffset
20399      * @type boolean
20400      */
20401     maintainOffset: false,
20402
20403     /**
20404      * Array of pixel locations the element will snap to if we specified a
20405      * horizontal graduation/interval.  This array is generated automatically
20406      * when you define a tick interval.
20407      * @property xTicks
20408      * @type int[]
20409      */
20410     xTicks: null,
20411
20412     /**
20413      * Array of pixel locations the element will snap to if we specified a
20414      * vertical graduation/interval.  This array is generated automatically
20415      * when you define a tick interval.
20416      * @property yTicks
20417      * @type int[]
20418      */
20419     yTicks: null,
20420
20421     /**
20422      * By default the drag and drop instance will only respond to the primary
20423      * button click (left button for a right-handed mouse).  Set to true to
20424      * allow drag and drop to start with any mouse click that is propogated
20425      * by the browser
20426      * @property primaryButtonOnly
20427      * @type boolean
20428      */
20429     primaryButtonOnly: true,
20430
20431     /**
20432      * The availabe property is false until the linked dom element is accessible.
20433      * @property available
20434      * @type boolean
20435      */
20436     available: false,
20437
20438     /**
20439      * By default, drags can only be initiated if the mousedown occurs in the
20440      * region the linked element is.  This is done in part to work around a
20441      * bug in some browsers that mis-report the mousedown if the previous
20442      * mouseup happened outside of the window.  This property is set to true
20443      * if outer handles are defined.
20444      *
20445      * @property hasOuterHandles
20446      * @type boolean
20447      * @default false
20448      */
20449     hasOuterHandles: false,
20450
20451     /**
20452      * Code that executes immediately before the startDrag event
20453      * @method b4StartDrag
20454      * @private
20455      */
20456     b4StartDrag: function(x, y) { },
20457
20458     /**
20459      * Abstract method called after a drag/drop object is clicked
20460      * and the drag or mousedown time thresholds have beeen met.
20461      * @method startDrag
20462      * @param {int} X click location
20463      * @param {int} Y click location
20464      */
20465     startDrag: function(x, y) { /* override this */ },
20466
20467     /**
20468      * Code that executes immediately before the onDrag event
20469      * @method b4Drag
20470      * @private
20471      */
20472     b4Drag: function(e) { },
20473
20474     /**
20475      * Abstract method called during the onMouseMove event while dragging an
20476      * object.
20477      * @method onDrag
20478      * @param {Event} e the mousemove event
20479      */
20480     onDrag: function(e) { /* override this */ },
20481
20482     /**
20483      * Abstract method called when this element fist begins hovering over
20484      * another DragDrop obj
20485      * @method onDragEnter
20486      * @param {Event} e the mousemove event
20487      * @param {String|DragDrop[]} id In POINT mode, the element
20488      * id this is hovering over.  In INTERSECT mode, an array of one or more
20489      * dragdrop items being hovered over.
20490      */
20491     onDragEnter: function(e, id) { /* override this */ },
20492
20493     /**
20494      * Code that executes immediately before the onDragOver event
20495      * @method b4DragOver
20496      * @private
20497      */
20498     b4DragOver: function(e) { },
20499
20500     /**
20501      * Abstract method called when this element is hovering over another
20502      * DragDrop obj
20503      * @method onDragOver
20504      * @param {Event} e the mousemove event
20505      * @param {String|DragDrop[]} id In POINT mode, the element
20506      * id this is hovering over.  In INTERSECT mode, an array of dd items
20507      * being hovered over.
20508      */
20509     onDragOver: function(e, id) { /* override this */ },
20510
20511     /**
20512      * Code that executes immediately before the onDragOut event
20513      * @method b4DragOut
20514      * @private
20515      */
20516     b4DragOut: function(e) { },
20517
20518     /**
20519      * Abstract method called when we are no longer hovering over an element
20520      * @method onDragOut
20521      * @param {Event} e the mousemove event
20522      * @param {String|DragDrop[]} id In POINT mode, the element
20523      * id this was hovering over.  In INTERSECT mode, an array of dd items
20524      * that the mouse is no longer over.
20525      */
20526     onDragOut: function(e, id) { /* override this */ },
20527
20528     /**
20529      * Code that executes immediately before the onDragDrop event
20530      * @method b4DragDrop
20531      * @private
20532      */
20533     b4DragDrop: function(e) { },
20534
20535     /**
20536      * Abstract method called when this item is dropped on another DragDrop
20537      * obj
20538      * @method onDragDrop
20539      * @param {Event} e the mouseup event
20540      * @param {String|DragDrop[]} id In POINT mode, the element
20541      * id this was dropped on.  In INTERSECT mode, an array of dd items this
20542      * was dropped on.
20543      */
20544     onDragDrop: function(e, id) { /* override this */ },
20545
20546     /**
20547      * Abstract method called when this item is dropped on an area with no
20548      * drop target
20549      * @method onInvalidDrop
20550      * @param {Event} e the mouseup event
20551      */
20552     onInvalidDrop: function(e) { /* override this */ },
20553
20554     /**
20555      * Code that executes immediately before the endDrag event
20556      * @method b4EndDrag
20557      * @private
20558      */
20559     b4EndDrag: function(e) { },
20560
20561     /**
20562      * Fired when we are done dragging the object
20563      * @method endDrag
20564      * @param {Event} e the mouseup event
20565      */
20566     endDrag: function(e) { /* override this */ },
20567
20568     /**
20569      * Code executed immediately before the onMouseDown event
20570      * @method b4MouseDown
20571      * @param {Event} e the mousedown event
20572      * @private
20573      */
20574     b4MouseDown: function(e) {  },
20575
20576     /**
20577      * Event handler that fires when a drag/drop obj gets a mousedown
20578      * @method onMouseDown
20579      * @param {Event} e the mousedown event
20580      */
20581     onMouseDown: function(e) { /* override this */ },
20582
20583     /**
20584      * Event handler that fires when a drag/drop obj gets a mouseup
20585      * @method onMouseUp
20586      * @param {Event} e the mouseup event
20587      */
20588     onMouseUp: function(e) { /* override this */ },
20589
20590     /**
20591      * Override the onAvailable method to do what is needed after the initial
20592      * position was determined.
20593      * @method onAvailable
20594      */
20595     onAvailable: function () {
20596     },
20597
20598     /*
20599      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
20600      * @type Object
20601      */
20602     defaultPadding : {left:0, right:0, top:0, bottom:0},
20603
20604     /*
20605      * Initializes the drag drop object's constraints to restrict movement to a certain element.
20606  *
20607  * Usage:
20608  <pre><code>
20609  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
20610                 { dragElId: "existingProxyDiv" });
20611  dd.startDrag = function(){
20612      this.constrainTo("parent-id");
20613  };
20614  </code></pre>
20615  * Or you can initalize it using the {@link Roo.Element} object:
20616  <pre><code>
20617  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
20618      startDrag : function(){
20619          this.constrainTo("parent-id");
20620      }
20621  });
20622  </code></pre>
20623      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
20624      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
20625      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
20626      * an object containing the sides to pad. For example: {right:10, bottom:10}
20627      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
20628      */
20629     constrainTo : function(constrainTo, pad, inContent){
20630         if(typeof pad == "number"){
20631             pad = {left: pad, right:pad, top:pad, bottom:pad};
20632         }
20633         pad = pad || this.defaultPadding;
20634         var b = Roo.get(this.getEl()).getBox();
20635         var ce = Roo.get(constrainTo);
20636         var s = ce.getScroll();
20637         var c, cd = ce.dom;
20638         if(cd == document.body){
20639             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
20640         }else{
20641             xy = ce.getXY();
20642             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
20643         }
20644
20645
20646         var topSpace = b.y - c.y;
20647         var leftSpace = b.x - c.x;
20648
20649         this.resetConstraints();
20650         this.setXConstraint(leftSpace - (pad.left||0), // left
20651                 c.width - leftSpace - b.width - (pad.right||0) //right
20652         );
20653         this.setYConstraint(topSpace - (pad.top||0), //top
20654                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
20655         );
20656     },
20657
20658     /**
20659      * Returns a reference to the linked element
20660      * @method getEl
20661      * @return {HTMLElement} the html element
20662      */
20663     getEl: function() {
20664         if (!this._domRef) {
20665             this._domRef = Roo.getDom(this.id);
20666         }
20667
20668         return this._domRef;
20669     },
20670
20671     /**
20672      * Returns a reference to the actual element to drag.  By default this is
20673      * the same as the html element, but it can be assigned to another
20674      * element. An example of this can be found in Roo.dd.DDProxy
20675      * @method getDragEl
20676      * @return {HTMLElement} the html element
20677      */
20678     getDragEl: function() {
20679         return Roo.getDom(this.dragElId);
20680     },
20681
20682     /**
20683      * Sets up the DragDrop object.  Must be called in the constructor of any
20684      * Roo.dd.DragDrop subclass
20685      * @method init
20686      * @param id the id of the linked element
20687      * @param {String} sGroup the group of related items
20688      * @param {object} config configuration attributes
20689      */
20690     init: function(id, sGroup, config) {
20691         this.initTarget(id, sGroup, config);
20692         if (!Roo.isTouch) {
20693             Event.on(this.id, "mousedown", this.handleMouseDown, this);
20694         }
20695         Event.on(this.id, "touchstart", this.handleMouseDown, this);
20696         // Event.on(this.id, "selectstart", Event.preventDefault);
20697     },
20698
20699     /**
20700      * Initializes Targeting functionality only... the object does not
20701      * get a mousedown handler.
20702      * @method initTarget
20703      * @param id the id of the linked element
20704      * @param {String} sGroup the group of related items
20705      * @param {object} config configuration attributes
20706      */
20707     initTarget: function(id, sGroup, config) {
20708
20709         // configuration attributes
20710         this.config = config || {};
20711
20712         // create a local reference to the drag and drop manager
20713         this.DDM = Roo.dd.DDM;
20714         // initialize the groups array
20715         this.groups = {};
20716
20717         // assume that we have an element reference instead of an id if the
20718         // parameter is not a string
20719         if (typeof id !== "string") {
20720             id = Roo.id(id);
20721         }
20722
20723         // set the id
20724         this.id = id;
20725
20726         // add to an interaction group
20727         this.addToGroup((sGroup) ? sGroup : "default");
20728
20729         // We don't want to register this as the handle with the manager
20730         // so we just set the id rather than calling the setter.
20731         this.handleElId = id;
20732
20733         // the linked element is the element that gets dragged by default
20734         this.setDragElId(id);
20735
20736         // by default, clicked anchors will not start drag operations.
20737         this.invalidHandleTypes = { A: "A" };
20738         this.invalidHandleIds = {};
20739         this.invalidHandleClasses = [];
20740
20741         this.applyConfig();
20742
20743         this.handleOnAvailable();
20744     },
20745
20746     /**
20747      * Applies the configuration parameters that were passed into the constructor.
20748      * This is supposed to happen at each level through the inheritance chain.  So
20749      * a DDProxy implentation will execute apply config on DDProxy, DD, and
20750      * DragDrop in order to get all of the parameters that are available in
20751      * each object.
20752      * @method applyConfig
20753      */
20754     applyConfig: function() {
20755
20756         // configurable properties:
20757         //    padding, isTarget, maintainOffset, primaryButtonOnly
20758         this.padding           = this.config.padding || [0, 0, 0, 0];
20759         this.isTarget          = (this.config.isTarget !== false);
20760         this.maintainOffset    = (this.config.maintainOffset);
20761         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
20762
20763     },
20764
20765     /**
20766      * Executed when the linked element is available
20767      * @method handleOnAvailable
20768      * @private
20769      */
20770     handleOnAvailable: function() {
20771         this.available = true;
20772         this.resetConstraints();
20773         this.onAvailable();
20774     },
20775
20776      /**
20777      * Configures the padding for the target zone in px.  Effectively expands
20778      * (or reduces) the virtual object size for targeting calculations.
20779      * Supports css-style shorthand; if only one parameter is passed, all sides
20780      * will have that padding, and if only two are passed, the top and bottom
20781      * will have the first param, the left and right the second.
20782      * @method setPadding
20783      * @param {int} iTop    Top pad
20784      * @param {int} iRight  Right pad
20785      * @param {int} iBot    Bot pad
20786      * @param {int} iLeft   Left pad
20787      */
20788     setPadding: function(iTop, iRight, iBot, iLeft) {
20789         // this.padding = [iLeft, iRight, iTop, iBot];
20790         if (!iRight && 0 !== iRight) {
20791             this.padding = [iTop, iTop, iTop, iTop];
20792         } else if (!iBot && 0 !== iBot) {
20793             this.padding = [iTop, iRight, iTop, iRight];
20794         } else {
20795             this.padding = [iTop, iRight, iBot, iLeft];
20796         }
20797     },
20798
20799     /**
20800      * Stores the initial placement of the linked element.
20801      * @method setInitialPosition
20802      * @param {int} diffX   the X offset, default 0
20803      * @param {int} diffY   the Y offset, default 0
20804      */
20805     setInitPosition: function(diffX, diffY) {
20806         var el = this.getEl();
20807
20808         if (!this.DDM.verifyEl(el)) {
20809             return;
20810         }
20811
20812         var dx = diffX || 0;
20813         var dy = diffY || 0;
20814
20815         var p = Dom.getXY( el );
20816
20817         this.initPageX = p[0] - dx;
20818         this.initPageY = p[1] - dy;
20819
20820         this.lastPageX = p[0];
20821         this.lastPageY = p[1];
20822
20823
20824         this.setStartPosition(p);
20825     },
20826
20827     /**
20828      * Sets the start position of the element.  This is set when the obj
20829      * is initialized, the reset when a drag is started.
20830      * @method setStartPosition
20831      * @param pos current position (from previous lookup)
20832      * @private
20833      */
20834     setStartPosition: function(pos) {
20835         var p = pos || Dom.getXY( this.getEl() );
20836         this.deltaSetXY = null;
20837
20838         this.startPageX = p[0];
20839         this.startPageY = p[1];
20840     },
20841
20842     /**
20843      * Add this instance to a group of related drag/drop objects.  All
20844      * instances belong to at least one group, and can belong to as many
20845      * groups as needed.
20846      * @method addToGroup
20847      * @param sGroup {string} the name of the group
20848      */
20849     addToGroup: function(sGroup) {
20850         this.groups[sGroup] = true;
20851         this.DDM.regDragDrop(this, sGroup);
20852     },
20853
20854     /**
20855      * Remove's this instance from the supplied interaction group
20856      * @method removeFromGroup
20857      * @param {string}  sGroup  The group to drop
20858      */
20859     removeFromGroup: function(sGroup) {
20860         if (this.groups[sGroup]) {
20861             delete this.groups[sGroup];
20862         }
20863
20864         this.DDM.removeDDFromGroup(this, sGroup);
20865     },
20866
20867     /**
20868      * Allows you to specify that an element other than the linked element
20869      * will be moved with the cursor during a drag
20870      * @method setDragElId
20871      * @param id {string} the id of the element that will be used to initiate the drag
20872      */
20873     setDragElId: function(id) {
20874         this.dragElId = id;
20875     },
20876
20877     /**
20878      * Allows you to specify a child of the linked element that should be
20879      * used to initiate the drag operation.  An example of this would be if
20880      * you have a content div with text and links.  Clicking anywhere in the
20881      * content area would normally start the drag operation.  Use this method
20882      * to specify that an element inside of the content div is the element
20883      * that starts the drag operation.
20884      * @method setHandleElId
20885      * @param id {string} the id of the element that will be used to
20886      * initiate the drag.
20887      */
20888     setHandleElId: function(id) {
20889         if (typeof id !== "string") {
20890             id = Roo.id(id);
20891         }
20892         this.handleElId = id;
20893         this.DDM.regHandle(this.id, id);
20894     },
20895
20896     /**
20897      * Allows you to set an element outside of the linked element as a drag
20898      * handle
20899      * @method setOuterHandleElId
20900      * @param id the id of the element that will be used to initiate the drag
20901      */
20902     setOuterHandleElId: function(id) {
20903         if (typeof id !== "string") {
20904             id = Roo.id(id);
20905         }
20906         Event.on(id, "mousedown",
20907                 this.handleMouseDown, this);
20908         this.setHandleElId(id);
20909
20910         this.hasOuterHandles = true;
20911     },
20912
20913     /**
20914      * Remove all drag and drop hooks for this element
20915      * @method unreg
20916      */
20917     unreg: function() {
20918         Event.un(this.id, "mousedown",
20919                 this.handleMouseDown);
20920         Event.un(this.id, "touchstart",
20921                 this.handleMouseDown);
20922         this._domRef = null;
20923         this.DDM._remove(this);
20924     },
20925
20926     destroy : function(){
20927         this.unreg();
20928     },
20929
20930     /**
20931      * Returns true if this instance is locked, or the drag drop mgr is locked
20932      * (meaning that all drag/drop is disabled on the page.)
20933      * @method isLocked
20934      * @return {boolean} true if this obj or all drag/drop is locked, else
20935      * false
20936      */
20937     isLocked: function() {
20938         return (this.DDM.isLocked() || this.locked);
20939     },
20940
20941     /**
20942      * Fired when this object is clicked
20943      * @method handleMouseDown
20944      * @param {Event} e
20945      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
20946      * @private
20947      */
20948     handleMouseDown: function(e, oDD){
20949      
20950         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
20951             //Roo.log('not touch/ button !=0');
20952             return;
20953         }
20954         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
20955             return; // double touch..
20956         }
20957         
20958
20959         if (this.isLocked()) {
20960             //Roo.log('locked');
20961             return;
20962         }
20963
20964         this.DDM.refreshCache(this.groups);
20965 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
20966         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
20967         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
20968             //Roo.log('no outer handes or not over target');
20969                 // do nothing.
20970         } else {
20971 //            Roo.log('check validator');
20972             if (this.clickValidator(e)) {
20973 //                Roo.log('validate success');
20974                 // set the initial element position
20975                 this.setStartPosition();
20976
20977
20978                 this.b4MouseDown(e);
20979                 this.onMouseDown(e);
20980
20981                 this.DDM.handleMouseDown(e, this);
20982
20983                 this.DDM.stopEvent(e);
20984             } else {
20985
20986
20987             }
20988         }
20989     },
20990
20991     clickValidator: function(e) {
20992         var target = e.getTarget();
20993         return ( this.isValidHandleChild(target) &&
20994                     (this.id == this.handleElId ||
20995                         this.DDM.handleWasClicked(target, this.id)) );
20996     },
20997
20998     /**
20999      * Allows you to specify a tag name that should not start a drag operation
21000      * when clicked.  This is designed to facilitate embedding links within a
21001      * drag handle that do something other than start the drag.
21002      * @method addInvalidHandleType
21003      * @param {string} tagName the type of element to exclude
21004      */
21005     addInvalidHandleType: function(tagName) {
21006         var type = tagName.toUpperCase();
21007         this.invalidHandleTypes[type] = type;
21008     },
21009
21010     /**
21011      * Lets you to specify an element id for a child of a drag handle
21012      * that should not initiate a drag
21013      * @method addInvalidHandleId
21014      * @param {string} id the element id of the element you wish to ignore
21015      */
21016     addInvalidHandleId: function(id) {
21017         if (typeof id !== "string") {
21018             id = Roo.id(id);
21019         }
21020         this.invalidHandleIds[id] = id;
21021     },
21022
21023     /**
21024      * Lets you specify a css class of elements that will not initiate a drag
21025      * @method addInvalidHandleClass
21026      * @param {string} cssClass the class of the elements you wish to ignore
21027      */
21028     addInvalidHandleClass: function(cssClass) {
21029         this.invalidHandleClasses.push(cssClass);
21030     },
21031
21032     /**
21033      * Unsets an excluded tag name set by addInvalidHandleType
21034      * @method removeInvalidHandleType
21035      * @param {string} tagName the type of element to unexclude
21036      */
21037     removeInvalidHandleType: function(tagName) {
21038         var type = tagName.toUpperCase();
21039         // this.invalidHandleTypes[type] = null;
21040         delete this.invalidHandleTypes[type];
21041     },
21042
21043     /**
21044      * Unsets an invalid handle id
21045      * @method removeInvalidHandleId
21046      * @param {string} id the id of the element to re-enable
21047      */
21048     removeInvalidHandleId: function(id) {
21049         if (typeof id !== "string") {
21050             id = Roo.id(id);
21051         }
21052         delete this.invalidHandleIds[id];
21053     },
21054
21055     /**
21056      * Unsets an invalid css class
21057      * @method removeInvalidHandleClass
21058      * @param {string} cssClass the class of the element(s) you wish to
21059      * re-enable
21060      */
21061     removeInvalidHandleClass: function(cssClass) {
21062         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
21063             if (this.invalidHandleClasses[i] == cssClass) {
21064                 delete this.invalidHandleClasses[i];
21065             }
21066         }
21067     },
21068
21069     /**
21070      * Checks the tag exclusion list to see if this click should be ignored
21071      * @method isValidHandleChild
21072      * @param {HTMLElement} node the HTMLElement to evaluate
21073      * @return {boolean} true if this is a valid tag type, false if not
21074      */
21075     isValidHandleChild: function(node) {
21076
21077         var valid = true;
21078         // var n = (node.nodeName == "#text") ? node.parentNode : node;
21079         var nodeName;
21080         try {
21081             nodeName = node.nodeName.toUpperCase();
21082         } catch(e) {
21083             nodeName = node.nodeName;
21084         }
21085         valid = valid && !this.invalidHandleTypes[nodeName];
21086         valid = valid && !this.invalidHandleIds[node.id];
21087
21088         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
21089             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
21090         }
21091
21092
21093         return valid;
21094
21095     },
21096
21097     /**
21098      * Create the array of horizontal tick marks if an interval was specified
21099      * in setXConstraint().
21100      * @method setXTicks
21101      * @private
21102      */
21103     setXTicks: function(iStartX, iTickSize) {
21104         this.xTicks = [];
21105         this.xTickSize = iTickSize;
21106
21107         var tickMap = {};
21108
21109         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
21110             if (!tickMap[i]) {
21111                 this.xTicks[this.xTicks.length] = i;
21112                 tickMap[i] = true;
21113             }
21114         }
21115
21116         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
21117             if (!tickMap[i]) {
21118                 this.xTicks[this.xTicks.length] = i;
21119                 tickMap[i] = true;
21120             }
21121         }
21122
21123         this.xTicks.sort(this.DDM.numericSort) ;
21124     },
21125
21126     /**
21127      * Create the array of vertical tick marks if an interval was specified in
21128      * setYConstraint().
21129      * @method setYTicks
21130      * @private
21131      */
21132     setYTicks: function(iStartY, iTickSize) {
21133         this.yTicks = [];
21134         this.yTickSize = iTickSize;
21135
21136         var tickMap = {};
21137
21138         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
21139             if (!tickMap[i]) {
21140                 this.yTicks[this.yTicks.length] = i;
21141                 tickMap[i] = true;
21142             }
21143         }
21144
21145         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
21146             if (!tickMap[i]) {
21147                 this.yTicks[this.yTicks.length] = i;
21148                 tickMap[i] = true;
21149             }
21150         }
21151
21152         this.yTicks.sort(this.DDM.numericSort) ;
21153     },
21154
21155     /**
21156      * By default, the element can be dragged any place on the screen.  Use
21157      * this method to limit the horizontal travel of the element.  Pass in
21158      * 0,0 for the parameters if you want to lock the drag to the y axis.
21159      * @method setXConstraint
21160      * @param {int} iLeft the number of pixels the element can move to the left
21161      * @param {int} iRight the number of pixels the element can move to the
21162      * right
21163      * @param {int} iTickSize optional parameter for specifying that the
21164      * element
21165      * should move iTickSize pixels at a time.
21166      */
21167     setXConstraint: function(iLeft, iRight, iTickSize) {
21168         this.leftConstraint = iLeft;
21169         this.rightConstraint = iRight;
21170
21171         this.minX = this.initPageX - iLeft;
21172         this.maxX = this.initPageX + iRight;
21173         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
21174
21175         this.constrainX = true;
21176     },
21177
21178     /**
21179      * Clears any constraints applied to this instance.  Also clears ticks
21180      * since they can't exist independent of a constraint at this time.
21181      * @method clearConstraints
21182      */
21183     clearConstraints: function() {
21184         this.constrainX = false;
21185         this.constrainY = false;
21186         this.clearTicks();
21187     },
21188
21189     /**
21190      * Clears any tick interval defined for this instance
21191      * @method clearTicks
21192      */
21193     clearTicks: function() {
21194         this.xTicks = null;
21195         this.yTicks = null;
21196         this.xTickSize = 0;
21197         this.yTickSize = 0;
21198     },
21199
21200     /**
21201      * By default, the element can be dragged any place on the screen.  Set
21202      * this to limit the vertical travel of the element.  Pass in 0,0 for the
21203      * parameters if you want to lock the drag to the x axis.
21204      * @method setYConstraint
21205      * @param {int} iUp the number of pixels the element can move up
21206      * @param {int} iDown the number of pixels the element can move down
21207      * @param {int} iTickSize optional parameter for specifying that the
21208      * element should move iTickSize pixels at a time.
21209      */
21210     setYConstraint: function(iUp, iDown, iTickSize) {
21211         this.topConstraint = iUp;
21212         this.bottomConstraint = iDown;
21213
21214         this.minY = this.initPageY - iUp;
21215         this.maxY = this.initPageY + iDown;
21216         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
21217
21218         this.constrainY = true;
21219
21220     },
21221
21222     /**
21223      * resetConstraints must be called if you manually reposition a dd element.
21224      * @method resetConstraints
21225      * @param {boolean} maintainOffset
21226      */
21227     resetConstraints: function() {
21228
21229
21230         // Maintain offsets if necessary
21231         if (this.initPageX || this.initPageX === 0) {
21232             // figure out how much this thing has moved
21233             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
21234             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
21235
21236             this.setInitPosition(dx, dy);
21237
21238         // This is the first time we have detected the element's position
21239         } else {
21240             this.setInitPosition();
21241         }
21242
21243         if (this.constrainX) {
21244             this.setXConstraint( this.leftConstraint,
21245                                  this.rightConstraint,
21246                                  this.xTickSize        );
21247         }
21248
21249         if (this.constrainY) {
21250             this.setYConstraint( this.topConstraint,
21251                                  this.bottomConstraint,
21252                                  this.yTickSize         );
21253         }
21254     },
21255
21256     /**
21257      * Normally the drag element is moved pixel by pixel, but we can specify
21258      * that it move a number of pixels at a time.  This method resolves the
21259      * location when we have it set up like this.
21260      * @method getTick
21261      * @param {int} val where we want to place the object
21262      * @param {int[]} tickArray sorted array of valid points
21263      * @return {int} the closest tick
21264      * @private
21265      */
21266     getTick: function(val, tickArray) {
21267
21268         if (!tickArray) {
21269             // If tick interval is not defined, it is effectively 1 pixel,
21270             // so we return the value passed to us.
21271             return val;
21272         } else if (tickArray[0] >= val) {
21273             // The value is lower than the first tick, so we return the first
21274             // tick.
21275             return tickArray[0];
21276         } else {
21277             for (var i=0, len=tickArray.length; i<len; ++i) {
21278                 var next = i + 1;
21279                 if (tickArray[next] && tickArray[next] >= val) {
21280                     var diff1 = val - tickArray[i];
21281                     var diff2 = tickArray[next] - val;
21282                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
21283                 }
21284             }
21285
21286             // The value is larger than the last tick, so we return the last
21287             // tick.
21288             return tickArray[tickArray.length - 1];
21289         }
21290     },
21291
21292     /**
21293      * toString method
21294      * @method toString
21295      * @return {string} string representation of the dd obj
21296      */
21297     toString: function() {
21298         return ("DragDrop " + this.id);
21299     }
21300
21301 });
21302
21303 })();
21304 /*
21305  * Based on:
21306  * Ext JS Library 1.1.1
21307  * Copyright(c) 2006-2007, Ext JS, LLC.
21308  *
21309  * Originally Released Under LGPL - original licence link has changed is not relivant.
21310  *
21311  * Fork - LGPL
21312  * <script type="text/javascript">
21313  */
21314
21315
21316 /**
21317  * The drag and drop utility provides a framework for building drag and drop
21318  * applications.  In addition to enabling drag and drop for specific elements,
21319  * the drag and drop elements are tracked by the manager class, and the
21320  * interactions between the various elements are tracked during the drag and
21321  * the implementing code is notified about these important moments.
21322  */
21323
21324 // Only load the library once.  Rewriting the manager class would orphan
21325 // existing drag and drop instances.
21326 if (!Roo.dd.DragDropMgr) {
21327
21328 /**
21329  * @class Roo.dd.DragDropMgr
21330  * DragDropMgr is a singleton that tracks the element interaction for
21331  * all DragDrop items in the window.  Generally, you will not call
21332  * this class directly, but it does have helper methods that could
21333  * be useful in your DragDrop implementations.
21334  * @static
21335  */
21336 Roo.dd.DragDropMgr = function() {
21337
21338     var Event = Roo.EventManager;
21339
21340     return {
21341
21342         /**
21343          * Two dimensional Array of registered DragDrop objects.  The first
21344          * dimension is the DragDrop item group, the second the DragDrop
21345          * object.
21346          * @property ids
21347          * @type {string: string}
21348          * @private
21349          * @static
21350          */
21351         ids: {},
21352
21353         /**
21354          * Array of element ids defined as drag handles.  Used to determine
21355          * if the element that generated the mousedown event is actually the
21356          * handle and not the html element itself.
21357          * @property handleIds
21358          * @type {string: string}
21359          * @private
21360          * @static
21361          */
21362         handleIds: {},
21363
21364         /**
21365          * the DragDrop object that is currently being dragged
21366          * @property dragCurrent
21367          * @type DragDrop
21368          * @private
21369          * @static
21370          **/
21371         dragCurrent: null,
21372
21373         /**
21374          * the DragDrop object(s) that are being hovered over
21375          * @property dragOvers
21376          * @type Array
21377          * @private
21378          * @static
21379          */
21380         dragOvers: {},
21381
21382         /**
21383          * the X distance between the cursor and the object being dragged
21384          * @property deltaX
21385          * @type int
21386          * @private
21387          * @static
21388          */
21389         deltaX: 0,
21390
21391         /**
21392          * the Y distance between the cursor and the object being dragged
21393          * @property deltaY
21394          * @type int
21395          * @private
21396          * @static
21397          */
21398         deltaY: 0,
21399
21400         /**
21401          * Flag to determine if we should prevent the default behavior of the
21402          * events we define. By default this is true, but this can be set to
21403          * false if you need the default behavior (not recommended)
21404          * @property preventDefault
21405          * @type boolean
21406          * @static
21407          */
21408         preventDefault: true,
21409
21410         /**
21411          * Flag to determine if we should stop the propagation of the events
21412          * we generate. This is true by default but you may want to set it to
21413          * false if the html element contains other features that require the
21414          * mouse click.
21415          * @property stopPropagation
21416          * @type boolean
21417          * @static
21418          */
21419         stopPropagation: true,
21420
21421         /**
21422          * Internal flag that is set to true when drag and drop has been
21423          * intialized
21424          * @property initialized
21425          * @private
21426          * @static
21427          */
21428         initalized: false,
21429
21430         /**
21431          * All drag and drop can be disabled.
21432          * @property locked
21433          * @private
21434          * @static
21435          */
21436         locked: false,
21437
21438         /**
21439          * Called the first time an element is registered.
21440          * @method init
21441          * @private
21442          * @static
21443          */
21444         init: function() {
21445             this.initialized = true;
21446         },
21447
21448         /**
21449          * In point mode, drag and drop interaction is defined by the
21450          * location of the cursor during the drag/drop
21451          * @property POINT
21452          * @type int
21453          * @static
21454          */
21455         POINT: 0,
21456
21457         /**
21458          * In intersect mode, drag and drop interactio nis defined by the
21459          * overlap of two or more drag and drop objects.
21460          * @property INTERSECT
21461          * @type int
21462          * @static
21463          */
21464         INTERSECT: 1,
21465
21466         /**
21467          * The current drag and drop mode.  Default: POINT
21468          * @property mode
21469          * @type int
21470          * @static
21471          */
21472         mode: 0,
21473
21474         /**
21475          * Runs method on all drag and drop objects
21476          * @method _execOnAll
21477          * @private
21478          * @static
21479          */
21480         _execOnAll: function(sMethod, args) {
21481             for (var i in this.ids) {
21482                 for (var j in this.ids[i]) {
21483                     var oDD = this.ids[i][j];
21484                     if (! this.isTypeOfDD(oDD)) {
21485                         continue;
21486                     }
21487                     oDD[sMethod].apply(oDD, args);
21488                 }
21489             }
21490         },
21491
21492         /**
21493          * Drag and drop initialization.  Sets up the global event handlers
21494          * @method _onLoad
21495          * @private
21496          * @static
21497          */
21498         _onLoad: function() {
21499
21500             this.init();
21501
21502             if (!Roo.isTouch) {
21503                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
21504                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
21505             }
21506             Event.on(document, "touchend",   this.handleMouseUp, this, true);
21507             Event.on(document, "touchmove", this.handleMouseMove, this, true);
21508             
21509             Event.on(window,   "unload",    this._onUnload, this, true);
21510             Event.on(window,   "resize",    this._onResize, this, true);
21511             // Event.on(window,   "mouseout",    this._test);
21512
21513         },
21514
21515         /**
21516          * Reset constraints on all drag and drop objs
21517          * @method _onResize
21518          * @private
21519          * @static
21520          */
21521         _onResize: function(e) {
21522             this._execOnAll("resetConstraints", []);
21523         },
21524
21525         /**
21526          * Lock all drag and drop functionality
21527          * @method lock
21528          * @static
21529          */
21530         lock: function() { this.locked = true; },
21531
21532         /**
21533          * Unlock all drag and drop functionality
21534          * @method unlock
21535          * @static
21536          */
21537         unlock: function() { this.locked = false; },
21538
21539         /**
21540          * Is drag and drop locked?
21541          * @method isLocked
21542          * @return {boolean} True if drag and drop is locked, false otherwise.
21543          * @static
21544          */
21545         isLocked: function() { return this.locked; },
21546
21547         /**
21548          * Location cache that is set for all drag drop objects when a drag is
21549          * initiated, cleared when the drag is finished.
21550          * @property locationCache
21551          * @private
21552          * @static
21553          */
21554         locationCache: {},
21555
21556         /**
21557          * Set useCache to false if you want to force object the lookup of each
21558          * drag and drop linked element constantly during a drag.
21559          * @property useCache
21560          * @type boolean
21561          * @static
21562          */
21563         useCache: true,
21564
21565         /**
21566          * The number of pixels that the mouse needs to move after the
21567          * mousedown before the drag is initiated.  Default=3;
21568          * @property clickPixelThresh
21569          * @type int
21570          * @static
21571          */
21572         clickPixelThresh: 3,
21573
21574         /**
21575          * The number of milliseconds after the mousedown event to initiate the
21576          * drag if we don't get a mouseup event. Default=1000
21577          * @property clickTimeThresh
21578          * @type int
21579          * @static
21580          */
21581         clickTimeThresh: 350,
21582
21583         /**
21584          * Flag that indicates that either the drag pixel threshold or the
21585          * mousdown time threshold has been met
21586          * @property dragThreshMet
21587          * @type boolean
21588          * @private
21589          * @static
21590          */
21591         dragThreshMet: false,
21592
21593         /**
21594          * Timeout used for the click time threshold
21595          * @property clickTimeout
21596          * @type Object
21597          * @private
21598          * @static
21599          */
21600         clickTimeout: null,
21601
21602         /**
21603          * The X position of the mousedown event stored for later use when a
21604          * drag threshold is met.
21605          * @property startX
21606          * @type int
21607          * @private
21608          * @static
21609          */
21610         startX: 0,
21611
21612         /**
21613          * The Y position of the mousedown event stored for later use when a
21614          * drag threshold is met.
21615          * @property startY
21616          * @type int
21617          * @private
21618          * @static
21619          */
21620         startY: 0,
21621
21622         /**
21623          * Each DragDrop instance must be registered with the DragDropMgr.
21624          * This is executed in DragDrop.init()
21625          * @method regDragDrop
21626          * @param {DragDrop} oDD the DragDrop object to register
21627          * @param {String} sGroup the name of the group this element belongs to
21628          * @static
21629          */
21630         regDragDrop: function(oDD, sGroup) {
21631             if (!this.initialized) { this.init(); }
21632
21633             if (!this.ids[sGroup]) {
21634                 this.ids[sGroup] = {};
21635             }
21636             this.ids[sGroup][oDD.id] = oDD;
21637         },
21638
21639         /**
21640          * Removes the supplied dd instance from the supplied group. Executed
21641          * by DragDrop.removeFromGroup, so don't call this function directly.
21642          * @method removeDDFromGroup
21643          * @private
21644          * @static
21645          */
21646         removeDDFromGroup: function(oDD, sGroup) {
21647             if (!this.ids[sGroup]) {
21648                 this.ids[sGroup] = {};
21649             }
21650
21651             var obj = this.ids[sGroup];
21652             if (obj && obj[oDD.id]) {
21653                 delete obj[oDD.id];
21654             }
21655         },
21656
21657         /**
21658          * Unregisters a drag and drop item.  This is executed in
21659          * DragDrop.unreg, use that method instead of calling this directly.
21660          * @method _remove
21661          * @private
21662          * @static
21663          */
21664         _remove: function(oDD) {
21665             for (var g in oDD.groups) {
21666                 if (g && this.ids[g][oDD.id]) {
21667                     delete this.ids[g][oDD.id];
21668                 }
21669             }
21670             delete this.handleIds[oDD.id];
21671         },
21672
21673         /**
21674          * Each DragDrop handle element must be registered.  This is done
21675          * automatically when executing DragDrop.setHandleElId()
21676          * @method regHandle
21677          * @param {String} sDDId the DragDrop id this element is a handle for
21678          * @param {String} sHandleId the id of the element that is the drag
21679          * handle
21680          * @static
21681          */
21682         regHandle: function(sDDId, sHandleId) {
21683             if (!this.handleIds[sDDId]) {
21684                 this.handleIds[sDDId] = {};
21685             }
21686             this.handleIds[sDDId][sHandleId] = sHandleId;
21687         },
21688
21689         /**
21690          * Utility function to determine if a given element has been
21691          * registered as a drag drop item.
21692          * @method isDragDrop
21693          * @param {String} id the element id to check
21694          * @return {boolean} true if this element is a DragDrop item,
21695          * false otherwise
21696          * @static
21697          */
21698         isDragDrop: function(id) {
21699             return ( this.getDDById(id) ) ? true : false;
21700         },
21701
21702         /**
21703          * Returns the drag and drop instances that are in all groups the
21704          * passed in instance belongs to.
21705          * @method getRelated
21706          * @param {DragDrop} p_oDD the obj to get related data for
21707          * @param {boolean} bTargetsOnly if true, only return targetable objs
21708          * @return {DragDrop[]} the related instances
21709          * @static
21710          */
21711         getRelated: function(p_oDD, bTargetsOnly) {
21712             var oDDs = [];
21713             for (var i in p_oDD.groups) {
21714                 for (j in this.ids[i]) {
21715                     var dd = this.ids[i][j];
21716                     if (! this.isTypeOfDD(dd)) {
21717                         continue;
21718                     }
21719                     if (!bTargetsOnly || dd.isTarget) {
21720                         oDDs[oDDs.length] = dd;
21721                     }
21722                 }
21723             }
21724
21725             return oDDs;
21726         },
21727
21728         /**
21729          * Returns true if the specified dd target is a legal target for
21730          * the specifice drag obj
21731          * @method isLegalTarget
21732          * @param {DragDrop} the drag obj
21733          * @param {DragDrop} the target
21734          * @return {boolean} true if the target is a legal target for the
21735          * dd obj
21736          * @static
21737          */
21738         isLegalTarget: function (oDD, oTargetDD) {
21739             var targets = this.getRelated(oDD, true);
21740             for (var i=0, len=targets.length;i<len;++i) {
21741                 if (targets[i].id == oTargetDD.id) {
21742                     return true;
21743                 }
21744             }
21745
21746             return false;
21747         },
21748
21749         /**
21750          * My goal is to be able to transparently determine if an object is
21751          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
21752          * returns "object", oDD.constructor.toString() always returns
21753          * "DragDrop" and not the name of the subclass.  So for now it just
21754          * evaluates a well-known variable in DragDrop.
21755          * @method isTypeOfDD
21756          * @param {Object} the object to evaluate
21757          * @return {boolean} true if typeof oDD = DragDrop
21758          * @static
21759          */
21760         isTypeOfDD: function (oDD) {
21761             return (oDD && oDD.__ygDragDrop);
21762         },
21763
21764         /**
21765          * Utility function to determine if a given element has been
21766          * registered as a drag drop handle for the given Drag Drop object.
21767          * @method isHandle
21768          * @param {String} id the element id to check
21769          * @return {boolean} true if this element is a DragDrop handle, false
21770          * otherwise
21771          * @static
21772          */
21773         isHandle: function(sDDId, sHandleId) {
21774             return ( this.handleIds[sDDId] &&
21775                             this.handleIds[sDDId][sHandleId] );
21776         },
21777
21778         /**
21779          * Returns the DragDrop instance for a given id
21780          * @method getDDById
21781          * @param {String} id the id of the DragDrop object
21782          * @return {DragDrop} the drag drop object, null if it is not found
21783          * @static
21784          */
21785         getDDById: function(id) {
21786             for (var i in this.ids) {
21787                 if (this.ids[i][id]) {
21788                     return this.ids[i][id];
21789                 }
21790             }
21791             return null;
21792         },
21793
21794         /**
21795          * Fired after a registered DragDrop object gets the mousedown event.
21796          * Sets up the events required to track the object being dragged
21797          * @method handleMouseDown
21798          * @param {Event} e the event
21799          * @param oDD the DragDrop object being dragged
21800          * @private
21801          * @static
21802          */
21803         handleMouseDown: function(e, oDD) {
21804             if(Roo.QuickTips){
21805                 Roo.QuickTips.disable();
21806             }
21807             this.currentTarget = e.getTarget();
21808
21809             this.dragCurrent = oDD;
21810
21811             var el = oDD.getEl();
21812
21813             // track start position
21814             this.startX = e.getPageX();
21815             this.startY = e.getPageY();
21816
21817             this.deltaX = this.startX - el.offsetLeft;
21818             this.deltaY = this.startY - el.offsetTop;
21819
21820             this.dragThreshMet = false;
21821
21822             this.clickTimeout = setTimeout(
21823                     function() {
21824                         var DDM = Roo.dd.DDM;
21825                         DDM.startDrag(DDM.startX, DDM.startY);
21826                     },
21827                     this.clickTimeThresh );
21828         },
21829
21830         /**
21831          * Fired when either the drag pixel threshol or the mousedown hold
21832          * time threshold has been met.
21833          * @method startDrag
21834          * @param x {int} the X position of the original mousedown
21835          * @param y {int} the Y position of the original mousedown
21836          * @static
21837          */
21838         startDrag: function(x, y) {
21839             clearTimeout(this.clickTimeout);
21840             if (this.dragCurrent) {
21841                 this.dragCurrent.b4StartDrag(x, y);
21842                 this.dragCurrent.startDrag(x, y);
21843             }
21844             this.dragThreshMet = true;
21845         },
21846
21847         /**
21848          * Internal function to handle the mouseup event.  Will be invoked
21849          * from the context of the document.
21850          * @method handleMouseUp
21851          * @param {Event} e the event
21852          * @private
21853          * @static
21854          */
21855         handleMouseUp: function(e) {
21856
21857             if(Roo.QuickTips){
21858                 Roo.QuickTips.enable();
21859             }
21860             if (! this.dragCurrent) {
21861                 return;
21862             }
21863
21864             clearTimeout(this.clickTimeout);
21865
21866             if (this.dragThreshMet) {
21867                 this.fireEvents(e, true);
21868             } else {
21869             }
21870
21871             this.stopDrag(e);
21872
21873             this.stopEvent(e);
21874         },
21875
21876         /**
21877          * Utility to stop event propagation and event default, if these
21878          * features are turned on.
21879          * @method stopEvent
21880          * @param {Event} e the event as returned by this.getEvent()
21881          * @static
21882          */
21883         stopEvent: function(e){
21884             if(this.stopPropagation) {
21885                 e.stopPropagation();
21886             }
21887
21888             if (this.preventDefault) {
21889                 e.preventDefault();
21890             }
21891         },
21892
21893         /**
21894          * Internal function to clean up event handlers after the drag
21895          * operation is complete
21896          * @method stopDrag
21897          * @param {Event} e the event
21898          * @private
21899          * @static
21900          */
21901         stopDrag: function(e) {
21902             // Fire the drag end event for the item that was dragged
21903             if (this.dragCurrent) {
21904                 if (this.dragThreshMet) {
21905                     this.dragCurrent.b4EndDrag(e);
21906                     this.dragCurrent.endDrag(e);
21907                 }
21908
21909                 this.dragCurrent.onMouseUp(e);
21910             }
21911
21912             this.dragCurrent = null;
21913             this.dragOvers = {};
21914         },
21915
21916         /**
21917          * Internal function to handle the mousemove event.  Will be invoked
21918          * from the context of the html element.
21919          *
21920          * @TODO figure out what we can do about mouse events lost when the
21921          * user drags objects beyond the window boundary.  Currently we can
21922          * detect this in internet explorer by verifying that the mouse is
21923          * down during the mousemove event.  Firefox doesn't give us the
21924          * button state on the mousemove event.
21925          * @method handleMouseMove
21926          * @param {Event} e the event
21927          * @private
21928          * @static
21929          */
21930         handleMouseMove: function(e) {
21931             if (! this.dragCurrent) {
21932                 return true;
21933             }
21934
21935             // var button = e.which || e.button;
21936
21937             // check for IE mouseup outside of page boundary
21938             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
21939                 this.stopEvent(e);
21940                 return this.handleMouseUp(e);
21941             }
21942
21943             if (!this.dragThreshMet) {
21944                 var diffX = Math.abs(this.startX - e.getPageX());
21945                 var diffY = Math.abs(this.startY - e.getPageY());
21946                 if (diffX > this.clickPixelThresh ||
21947                             diffY > this.clickPixelThresh) {
21948                     this.startDrag(this.startX, this.startY);
21949                 }
21950             }
21951
21952             if (this.dragThreshMet) {
21953                 this.dragCurrent.b4Drag(e);
21954                 this.dragCurrent.onDrag(e);
21955                 if(!this.dragCurrent.moveOnly){
21956                     this.fireEvents(e, false);
21957                 }
21958             }
21959
21960             this.stopEvent(e);
21961
21962             return true;
21963         },
21964
21965         /**
21966          * Iterates over all of the DragDrop elements to find ones we are
21967          * hovering over or dropping on
21968          * @method fireEvents
21969          * @param {Event} e the event
21970          * @param {boolean} isDrop is this a drop op or a mouseover op?
21971          * @private
21972          * @static
21973          */
21974         fireEvents: function(e, isDrop) {
21975             var dc = this.dragCurrent;
21976
21977             // If the user did the mouse up outside of the window, we could
21978             // get here even though we have ended the drag.
21979             if (!dc || dc.isLocked()) {
21980                 return;
21981             }
21982
21983             var pt = e.getPoint();
21984
21985             // cache the previous dragOver array
21986             var oldOvers = [];
21987
21988             var outEvts   = [];
21989             var overEvts  = [];
21990             var dropEvts  = [];
21991             var enterEvts = [];
21992
21993             // Check to see if the object(s) we were hovering over is no longer
21994             // being hovered over so we can fire the onDragOut event
21995             for (var i in this.dragOvers) {
21996
21997                 var ddo = this.dragOvers[i];
21998
21999                 if (! this.isTypeOfDD(ddo)) {
22000                     continue;
22001                 }
22002
22003                 if (! this.isOverTarget(pt, ddo, this.mode)) {
22004                     outEvts.push( ddo );
22005                 }
22006
22007                 oldOvers[i] = true;
22008                 delete this.dragOvers[i];
22009             }
22010
22011             for (var sGroup in dc.groups) {
22012
22013                 if ("string" != typeof sGroup) {
22014                     continue;
22015                 }
22016
22017                 for (i in this.ids[sGroup]) {
22018                     var oDD = this.ids[sGroup][i];
22019                     if (! this.isTypeOfDD(oDD)) {
22020                         continue;
22021                     }
22022
22023                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
22024                         if (this.isOverTarget(pt, oDD, this.mode)) {
22025                             // look for drop interactions
22026                             if (isDrop) {
22027                                 dropEvts.push( oDD );
22028                             // look for drag enter and drag over interactions
22029                             } else {
22030
22031                                 // initial drag over: dragEnter fires
22032                                 if (!oldOvers[oDD.id]) {
22033                                     enterEvts.push( oDD );
22034                                 // subsequent drag overs: dragOver fires
22035                                 } else {
22036                                     overEvts.push( oDD );
22037                                 }
22038
22039                                 this.dragOvers[oDD.id] = oDD;
22040                             }
22041                         }
22042                     }
22043                 }
22044             }
22045
22046             if (this.mode) {
22047                 if (outEvts.length) {
22048                     dc.b4DragOut(e, outEvts);
22049                     dc.onDragOut(e, outEvts);
22050                 }
22051
22052                 if (enterEvts.length) {
22053                     dc.onDragEnter(e, enterEvts);
22054                 }
22055
22056                 if (overEvts.length) {
22057                     dc.b4DragOver(e, overEvts);
22058                     dc.onDragOver(e, overEvts);
22059                 }
22060
22061                 if (dropEvts.length) {
22062                     dc.b4DragDrop(e, dropEvts);
22063                     dc.onDragDrop(e, dropEvts);
22064                 }
22065
22066             } else {
22067                 // fire dragout events
22068                 var len = 0;
22069                 for (i=0, len=outEvts.length; i<len; ++i) {
22070                     dc.b4DragOut(e, outEvts[i].id);
22071                     dc.onDragOut(e, outEvts[i].id);
22072                 }
22073
22074                 // fire enter events
22075                 for (i=0,len=enterEvts.length; i<len; ++i) {
22076                     // dc.b4DragEnter(e, oDD.id);
22077                     dc.onDragEnter(e, enterEvts[i].id);
22078                 }
22079
22080                 // fire over events
22081                 for (i=0,len=overEvts.length; i<len; ++i) {
22082                     dc.b4DragOver(e, overEvts[i].id);
22083                     dc.onDragOver(e, overEvts[i].id);
22084                 }
22085
22086                 // fire drop events
22087                 for (i=0, len=dropEvts.length; i<len; ++i) {
22088                     dc.b4DragDrop(e, dropEvts[i].id);
22089                     dc.onDragDrop(e, dropEvts[i].id);
22090                 }
22091
22092             }
22093
22094             // notify about a drop that did not find a target
22095             if (isDrop && !dropEvts.length) {
22096                 dc.onInvalidDrop(e);
22097             }
22098
22099         },
22100
22101         /**
22102          * Helper function for getting the best match from the list of drag
22103          * and drop objects returned by the drag and drop events when we are
22104          * in INTERSECT mode.  It returns either the first object that the
22105          * cursor is over, or the object that has the greatest overlap with
22106          * the dragged element.
22107          * @method getBestMatch
22108          * @param  {DragDrop[]} dds The array of drag and drop objects
22109          * targeted
22110          * @return {DragDrop}       The best single match
22111          * @static
22112          */
22113         getBestMatch: function(dds) {
22114             var winner = null;
22115             // Return null if the input is not what we expect
22116             //if (!dds || !dds.length || dds.length == 0) {
22117                // winner = null;
22118             // If there is only one item, it wins
22119             //} else if (dds.length == 1) {
22120
22121             var len = dds.length;
22122
22123             if (len == 1) {
22124                 winner = dds[0];
22125             } else {
22126                 // Loop through the targeted items
22127                 for (var i=0; i<len; ++i) {
22128                     var dd = dds[i];
22129                     // If the cursor is over the object, it wins.  If the
22130                     // cursor is over multiple matches, the first one we come
22131                     // to wins.
22132                     if (dd.cursorIsOver) {
22133                         winner = dd;
22134                         break;
22135                     // Otherwise the object with the most overlap wins
22136                     } else {
22137                         if (!winner ||
22138                             winner.overlap.getArea() < dd.overlap.getArea()) {
22139                             winner = dd;
22140                         }
22141                     }
22142                 }
22143             }
22144
22145             return winner;
22146         },
22147
22148         /**
22149          * Refreshes the cache of the top-left and bottom-right points of the
22150          * drag and drop objects in the specified group(s).  This is in the
22151          * format that is stored in the drag and drop instance, so typical
22152          * usage is:
22153          * <code>
22154          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
22155          * </code>
22156          * Alternatively:
22157          * <code>
22158          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
22159          * </code>
22160          * @TODO this really should be an indexed array.  Alternatively this
22161          * method could accept both.
22162          * @method refreshCache
22163          * @param {Object} groups an associative array of groups to refresh
22164          * @static
22165          */
22166         refreshCache: function(groups) {
22167             for (var sGroup in groups) {
22168                 if ("string" != typeof sGroup) {
22169                     continue;
22170                 }
22171                 for (var i in this.ids[sGroup]) {
22172                     var oDD = this.ids[sGroup][i];
22173
22174                     if (this.isTypeOfDD(oDD)) {
22175                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
22176                         var loc = this.getLocation(oDD);
22177                         if (loc) {
22178                             this.locationCache[oDD.id] = loc;
22179                         } else {
22180                             delete this.locationCache[oDD.id];
22181                             // this will unregister the drag and drop object if
22182                             // the element is not in a usable state
22183                             // oDD.unreg();
22184                         }
22185                     }
22186                 }
22187             }
22188         },
22189
22190         /**
22191          * This checks to make sure an element exists and is in the DOM.  The
22192          * main purpose is to handle cases where innerHTML is used to remove
22193          * drag and drop objects from the DOM.  IE provides an 'unspecified
22194          * error' when trying to access the offsetParent of such an element
22195          * @method verifyEl
22196          * @param {HTMLElement} el the element to check
22197          * @return {boolean} true if the element looks usable
22198          * @static
22199          */
22200         verifyEl: function(el) {
22201             if (el) {
22202                 var parent;
22203                 if(Roo.isIE){
22204                     try{
22205                         parent = el.offsetParent;
22206                     }catch(e){}
22207                 }else{
22208                     parent = el.offsetParent;
22209                 }
22210                 if (parent) {
22211                     return true;
22212                 }
22213             }
22214
22215             return false;
22216         },
22217
22218         /**
22219          * Returns a Region object containing the drag and drop element's position
22220          * and size, including the padding configured for it
22221          * @method getLocation
22222          * @param {DragDrop} oDD the drag and drop object to get the
22223          *                       location for
22224          * @return {Roo.lib.Region} a Region object representing the total area
22225          *                             the element occupies, including any padding
22226          *                             the instance is configured for.
22227          * @static
22228          */
22229         getLocation: function(oDD) {
22230             if (! this.isTypeOfDD(oDD)) {
22231                 return null;
22232             }
22233
22234             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
22235
22236             try {
22237                 pos= Roo.lib.Dom.getXY(el);
22238             } catch (e) { }
22239
22240             if (!pos) {
22241                 return null;
22242             }
22243
22244             x1 = pos[0];
22245             x2 = x1 + el.offsetWidth;
22246             y1 = pos[1];
22247             y2 = y1 + el.offsetHeight;
22248
22249             t = y1 - oDD.padding[0];
22250             r = x2 + oDD.padding[1];
22251             b = y2 + oDD.padding[2];
22252             l = x1 - oDD.padding[3];
22253
22254             return new Roo.lib.Region( t, r, b, l );
22255         },
22256
22257         /**
22258          * Checks the cursor location to see if it over the target
22259          * @method isOverTarget
22260          * @param {Roo.lib.Point} pt The point to evaluate
22261          * @param {DragDrop} oTarget the DragDrop object we are inspecting
22262          * @return {boolean} true if the mouse is over the target
22263          * @private
22264          * @static
22265          */
22266         isOverTarget: function(pt, oTarget, intersect) {
22267             // use cache if available
22268             var loc = this.locationCache[oTarget.id];
22269             if (!loc || !this.useCache) {
22270                 loc = this.getLocation(oTarget);
22271                 this.locationCache[oTarget.id] = loc;
22272
22273             }
22274
22275             if (!loc) {
22276                 return false;
22277             }
22278
22279             oTarget.cursorIsOver = loc.contains( pt );
22280
22281             // DragDrop is using this as a sanity check for the initial mousedown
22282             // in this case we are done.  In POINT mode, if the drag obj has no
22283             // contraints, we are also done. Otherwise we need to evaluate the
22284             // location of the target as related to the actual location of the
22285             // dragged element.
22286             var dc = this.dragCurrent;
22287             if (!dc || !dc.getTargetCoord ||
22288                     (!intersect && !dc.constrainX && !dc.constrainY)) {
22289                 return oTarget.cursorIsOver;
22290             }
22291
22292             oTarget.overlap = null;
22293
22294             // Get the current location of the drag element, this is the
22295             // location of the mouse event less the delta that represents
22296             // where the original mousedown happened on the element.  We
22297             // need to consider constraints and ticks as well.
22298             var pos = dc.getTargetCoord(pt.x, pt.y);
22299
22300             var el = dc.getDragEl();
22301             var curRegion = new Roo.lib.Region( pos.y,
22302                                                    pos.x + el.offsetWidth,
22303                                                    pos.y + el.offsetHeight,
22304                                                    pos.x );
22305
22306             var overlap = curRegion.intersect(loc);
22307
22308             if (overlap) {
22309                 oTarget.overlap = overlap;
22310                 return (intersect) ? true : oTarget.cursorIsOver;
22311             } else {
22312                 return false;
22313             }
22314         },
22315
22316         /**
22317          * unload event handler
22318          * @method _onUnload
22319          * @private
22320          * @static
22321          */
22322         _onUnload: function(e, me) {
22323             Roo.dd.DragDropMgr.unregAll();
22324         },
22325
22326         /**
22327          * Cleans up the drag and drop events and objects.
22328          * @method unregAll
22329          * @private
22330          * @static
22331          */
22332         unregAll: function() {
22333
22334             if (this.dragCurrent) {
22335                 this.stopDrag();
22336                 this.dragCurrent = null;
22337             }
22338
22339             this._execOnAll("unreg", []);
22340
22341             for (i in this.elementCache) {
22342                 delete this.elementCache[i];
22343             }
22344
22345             this.elementCache = {};
22346             this.ids = {};
22347         },
22348
22349         /**
22350          * A cache of DOM elements
22351          * @property elementCache
22352          * @private
22353          * @static
22354          */
22355         elementCache: {},
22356
22357         /**
22358          * Get the wrapper for the DOM element specified
22359          * @method getElWrapper
22360          * @param {String} id the id of the element to get
22361          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
22362          * @private
22363          * @deprecated This wrapper isn't that useful
22364          * @static
22365          */
22366         getElWrapper: function(id) {
22367             var oWrapper = this.elementCache[id];
22368             if (!oWrapper || !oWrapper.el) {
22369                 oWrapper = this.elementCache[id] =
22370                     new this.ElementWrapper(Roo.getDom(id));
22371             }
22372             return oWrapper;
22373         },
22374
22375         /**
22376          * Returns the actual DOM element
22377          * @method getElement
22378          * @param {String} id the id of the elment to get
22379          * @return {Object} The element
22380          * @deprecated use Roo.getDom instead
22381          * @static
22382          */
22383         getElement: function(id) {
22384             return Roo.getDom(id);
22385         },
22386
22387         /**
22388          * Returns the style property for the DOM element (i.e.,
22389          * document.getElById(id).style)
22390          * @method getCss
22391          * @param {String} id the id of the elment to get
22392          * @return {Object} The style property of the element
22393          * @deprecated use Roo.getDom instead
22394          * @static
22395          */
22396         getCss: function(id) {
22397             var el = Roo.getDom(id);
22398             return (el) ? el.style : null;
22399         },
22400
22401         /**
22402          * Inner class for cached elements
22403          * @class DragDropMgr.ElementWrapper
22404          * @for DragDropMgr
22405          * @private
22406          * @deprecated
22407          */
22408         ElementWrapper: function(el) {
22409                 /**
22410                  * The element
22411                  * @property el
22412                  */
22413                 this.el = el || null;
22414                 /**
22415                  * The element id
22416                  * @property id
22417                  */
22418                 this.id = this.el && el.id;
22419                 /**
22420                  * A reference to the style property
22421                  * @property css
22422                  */
22423                 this.css = this.el && el.style;
22424             },
22425
22426         /**
22427          * Returns the X position of an html element
22428          * @method getPosX
22429          * @param el the element for which to get the position
22430          * @return {int} the X coordinate
22431          * @for DragDropMgr
22432          * @deprecated use Roo.lib.Dom.getX instead
22433          * @static
22434          */
22435         getPosX: function(el) {
22436             return Roo.lib.Dom.getX(el);
22437         },
22438
22439         /**
22440          * Returns the Y position of an html element
22441          * @method getPosY
22442          * @param el the element for which to get the position
22443          * @return {int} the Y coordinate
22444          * @deprecated use Roo.lib.Dom.getY instead
22445          * @static
22446          */
22447         getPosY: function(el) {
22448             return Roo.lib.Dom.getY(el);
22449         },
22450
22451         /**
22452          * Swap two nodes.  In IE, we use the native method, for others we
22453          * emulate the IE behavior
22454          * @method swapNode
22455          * @param n1 the first node to swap
22456          * @param n2 the other node to swap
22457          * @static
22458          */
22459         swapNode: function(n1, n2) {
22460             if (n1.swapNode) {
22461                 n1.swapNode(n2);
22462             } else {
22463                 var p = n2.parentNode;
22464                 var s = n2.nextSibling;
22465
22466                 if (s == n1) {
22467                     p.insertBefore(n1, n2);
22468                 } else if (n2 == n1.nextSibling) {
22469                     p.insertBefore(n2, n1);
22470                 } else {
22471                     n1.parentNode.replaceChild(n2, n1);
22472                     p.insertBefore(n1, s);
22473                 }
22474             }
22475         },
22476
22477         /**
22478          * Returns the current scroll position
22479          * @method getScroll
22480          * @private
22481          * @static
22482          */
22483         getScroll: function () {
22484             var t, l, dde=document.documentElement, db=document.body;
22485             if (dde && (dde.scrollTop || dde.scrollLeft)) {
22486                 t = dde.scrollTop;
22487                 l = dde.scrollLeft;
22488             } else if (db) {
22489                 t = db.scrollTop;
22490                 l = db.scrollLeft;
22491             } else {
22492
22493             }
22494             return { top: t, left: l };
22495         },
22496
22497         /**
22498          * Returns the specified element style property
22499          * @method getStyle
22500          * @param {HTMLElement} el          the element
22501          * @param {string}      styleProp   the style property
22502          * @return {string} The value of the style property
22503          * @deprecated use Roo.lib.Dom.getStyle
22504          * @static
22505          */
22506         getStyle: function(el, styleProp) {
22507             return Roo.fly(el).getStyle(styleProp);
22508         },
22509
22510         /**
22511          * Gets the scrollTop
22512          * @method getScrollTop
22513          * @return {int} the document's scrollTop
22514          * @static
22515          */
22516         getScrollTop: function () { return this.getScroll().top; },
22517
22518         /**
22519          * Gets the scrollLeft
22520          * @method getScrollLeft
22521          * @return {int} the document's scrollTop
22522          * @static
22523          */
22524         getScrollLeft: function () { return this.getScroll().left; },
22525
22526         /**
22527          * Sets the x/y position of an element to the location of the
22528          * target element.
22529          * @method moveToEl
22530          * @param {HTMLElement} moveEl      The element to move
22531          * @param {HTMLElement} targetEl    The position reference element
22532          * @static
22533          */
22534         moveToEl: function (moveEl, targetEl) {
22535             var aCoord = Roo.lib.Dom.getXY(targetEl);
22536             Roo.lib.Dom.setXY(moveEl, aCoord);
22537         },
22538
22539         /**
22540          * Numeric array sort function
22541          * @method numericSort
22542          * @static
22543          */
22544         numericSort: function(a, b) { return (a - b); },
22545
22546         /**
22547          * Internal counter
22548          * @property _timeoutCount
22549          * @private
22550          * @static
22551          */
22552         _timeoutCount: 0,
22553
22554         /**
22555          * Trying to make the load order less important.  Without this we get
22556          * an error if this file is loaded before the Event Utility.
22557          * @method _addListeners
22558          * @private
22559          * @static
22560          */
22561         _addListeners: function() {
22562             var DDM = Roo.dd.DDM;
22563             if ( Roo.lib.Event && document ) {
22564                 DDM._onLoad();
22565             } else {
22566                 if (DDM._timeoutCount > 2000) {
22567                 } else {
22568                     setTimeout(DDM._addListeners, 10);
22569                     if (document && document.body) {
22570                         DDM._timeoutCount += 1;
22571                     }
22572                 }
22573             }
22574         },
22575
22576         /**
22577          * Recursively searches the immediate parent and all child nodes for
22578          * the handle element in order to determine wheter or not it was
22579          * clicked.
22580          * @method handleWasClicked
22581          * @param node the html element to inspect
22582          * @static
22583          */
22584         handleWasClicked: function(node, id) {
22585             if (this.isHandle(id, node.id)) {
22586                 return true;
22587             } else {
22588                 // check to see if this is a text node child of the one we want
22589                 var p = node.parentNode;
22590
22591                 while (p) {
22592                     if (this.isHandle(id, p.id)) {
22593                         return true;
22594                     } else {
22595                         p = p.parentNode;
22596                     }
22597                 }
22598             }
22599
22600             return false;
22601         }
22602
22603     };
22604
22605 }();
22606
22607 // shorter alias, save a few bytes
22608 Roo.dd.DDM = Roo.dd.DragDropMgr;
22609 Roo.dd.DDM._addListeners();
22610
22611 }/*
22612  * Based on:
22613  * Ext JS Library 1.1.1
22614  * Copyright(c) 2006-2007, Ext JS, LLC.
22615  *
22616  * Originally Released Under LGPL - original licence link has changed is not relivant.
22617  *
22618  * Fork - LGPL
22619  * <script type="text/javascript">
22620  */
22621
22622 /**
22623  * @class Roo.dd.DD
22624  * A DragDrop implementation where the linked element follows the
22625  * mouse cursor during a drag.
22626  * @extends Roo.dd.DragDrop
22627  * @constructor
22628  * @param {String} id the id of the linked element
22629  * @param {String} sGroup the group of related DragDrop items
22630  * @param {object} config an object containing configurable attributes
22631  *                Valid properties for DD:
22632  *                    scroll
22633  */
22634 Roo.dd.DD = function(id, sGroup, config) {
22635     if (id) {
22636         this.init(id, sGroup, config);
22637     }
22638 };
22639
22640 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
22641
22642     /**
22643      * When set to true, the utility automatically tries to scroll the browser
22644      * window wehn a drag and drop element is dragged near the viewport boundary.
22645      * Defaults to true.
22646      * @property scroll
22647      * @type boolean
22648      */
22649     scroll: true,
22650
22651     /**
22652      * Sets the pointer offset to the distance between the linked element's top
22653      * left corner and the location the element was clicked
22654      * @method autoOffset
22655      * @param {int} iPageX the X coordinate of the click
22656      * @param {int} iPageY the Y coordinate of the click
22657      */
22658     autoOffset: function(iPageX, iPageY) {
22659         var x = iPageX - this.startPageX;
22660         var y = iPageY - this.startPageY;
22661         this.setDelta(x, y);
22662     },
22663
22664     /**
22665      * Sets the pointer offset.  You can call this directly to force the
22666      * offset to be in a particular location (e.g., pass in 0,0 to set it
22667      * to the center of the object)
22668      * @method setDelta
22669      * @param {int} iDeltaX the distance from the left
22670      * @param {int} iDeltaY the distance from the top
22671      */
22672     setDelta: function(iDeltaX, iDeltaY) {
22673         this.deltaX = iDeltaX;
22674         this.deltaY = iDeltaY;
22675     },
22676
22677     /**
22678      * Sets the drag element to the location of the mousedown or click event,
22679      * maintaining the cursor location relative to the location on the element
22680      * that was clicked.  Override this if you want to place the element in a
22681      * location other than where the cursor is.
22682      * @method setDragElPos
22683      * @param {int} iPageX the X coordinate of the mousedown or drag event
22684      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22685      */
22686     setDragElPos: function(iPageX, iPageY) {
22687         // the first time we do this, we are going to check to make sure
22688         // the element has css positioning
22689
22690         var el = this.getDragEl();
22691         this.alignElWithMouse(el, iPageX, iPageY);
22692     },
22693
22694     /**
22695      * Sets the element to the location of the mousedown or click event,
22696      * maintaining the cursor location relative to the location on the element
22697      * that was clicked.  Override this if you want to place the element in a
22698      * location other than where the cursor is.
22699      * @method alignElWithMouse
22700      * @param {HTMLElement} el the element to move
22701      * @param {int} iPageX the X coordinate of the mousedown or drag event
22702      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22703      */
22704     alignElWithMouse: function(el, iPageX, iPageY) {
22705         var oCoord = this.getTargetCoord(iPageX, iPageY);
22706         var fly = el.dom ? el : Roo.fly(el);
22707         if (!this.deltaSetXY) {
22708             var aCoord = [oCoord.x, oCoord.y];
22709             fly.setXY(aCoord);
22710             var newLeft = fly.getLeft(true);
22711             var newTop  = fly.getTop(true);
22712             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
22713         } else {
22714             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
22715         }
22716
22717         this.cachePosition(oCoord.x, oCoord.y);
22718         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
22719         return oCoord;
22720     },
22721
22722     /**
22723      * Saves the most recent position so that we can reset the constraints and
22724      * tick marks on-demand.  We need to know this so that we can calculate the
22725      * number of pixels the element is offset from its original position.
22726      * @method cachePosition
22727      * @param iPageX the current x position (optional, this just makes it so we
22728      * don't have to look it up again)
22729      * @param iPageY the current y position (optional, this just makes it so we
22730      * don't have to look it up again)
22731      */
22732     cachePosition: function(iPageX, iPageY) {
22733         if (iPageX) {
22734             this.lastPageX = iPageX;
22735             this.lastPageY = iPageY;
22736         } else {
22737             var aCoord = Roo.lib.Dom.getXY(this.getEl());
22738             this.lastPageX = aCoord[0];
22739             this.lastPageY = aCoord[1];
22740         }
22741     },
22742
22743     /**
22744      * Auto-scroll the window if the dragged object has been moved beyond the
22745      * visible window boundary.
22746      * @method autoScroll
22747      * @param {int} x the drag element's x position
22748      * @param {int} y the drag element's y position
22749      * @param {int} h the height of the drag element
22750      * @param {int} w the width of the drag element
22751      * @private
22752      */
22753     autoScroll: function(x, y, h, w) {
22754
22755         if (this.scroll) {
22756             // The client height
22757             var clientH = Roo.lib.Dom.getViewWidth();
22758
22759             // The client width
22760             var clientW = Roo.lib.Dom.getViewHeight();
22761
22762             // The amt scrolled down
22763             var st = this.DDM.getScrollTop();
22764
22765             // The amt scrolled right
22766             var sl = this.DDM.getScrollLeft();
22767
22768             // Location of the bottom of the element
22769             var bot = h + y;
22770
22771             // Location of the right of the element
22772             var right = w + x;
22773
22774             // The distance from the cursor to the bottom of the visible area,
22775             // adjusted so that we don't scroll if the cursor is beyond the
22776             // element drag constraints
22777             var toBot = (clientH + st - y - this.deltaY);
22778
22779             // The distance from the cursor to the right of the visible area
22780             var toRight = (clientW + sl - x - this.deltaX);
22781
22782
22783             // How close to the edge the cursor must be before we scroll
22784             // var thresh = (document.all) ? 100 : 40;
22785             var thresh = 40;
22786
22787             // How many pixels to scroll per autoscroll op.  This helps to reduce
22788             // clunky scrolling. IE is more sensitive about this ... it needs this
22789             // value to be higher.
22790             var scrAmt = (document.all) ? 80 : 30;
22791
22792             // Scroll down if we are near the bottom of the visible page and the
22793             // obj extends below the crease
22794             if ( bot > clientH && toBot < thresh ) {
22795                 window.scrollTo(sl, st + scrAmt);
22796             }
22797
22798             // Scroll up if the window is scrolled down and the top of the object
22799             // goes above the top border
22800             if ( y < st && st > 0 && y - st < thresh ) {
22801                 window.scrollTo(sl, st - scrAmt);
22802             }
22803
22804             // Scroll right if the obj is beyond the right border and the cursor is
22805             // near the border.
22806             if ( right > clientW && toRight < thresh ) {
22807                 window.scrollTo(sl + scrAmt, st);
22808             }
22809
22810             // Scroll left if the window has been scrolled to the right and the obj
22811             // extends past the left border
22812             if ( x < sl && sl > 0 && x - sl < thresh ) {
22813                 window.scrollTo(sl - scrAmt, st);
22814             }
22815         }
22816     },
22817
22818     /**
22819      * Finds the location the element should be placed if we want to move
22820      * it to where the mouse location less the click offset would place us.
22821      * @method getTargetCoord
22822      * @param {int} iPageX the X coordinate of the click
22823      * @param {int} iPageY the Y coordinate of the click
22824      * @return an object that contains the coordinates (Object.x and Object.y)
22825      * @private
22826      */
22827     getTargetCoord: function(iPageX, iPageY) {
22828
22829
22830         var x = iPageX - this.deltaX;
22831         var y = iPageY - this.deltaY;
22832
22833         if (this.constrainX) {
22834             if (x < this.minX) { x = this.minX; }
22835             if (x > this.maxX) { x = this.maxX; }
22836         }
22837
22838         if (this.constrainY) {
22839             if (y < this.minY) { y = this.minY; }
22840             if (y > this.maxY) { y = this.maxY; }
22841         }
22842
22843         x = this.getTick(x, this.xTicks);
22844         y = this.getTick(y, this.yTicks);
22845
22846
22847         return {x:x, y:y};
22848     },
22849
22850     /*
22851      * Sets up config options specific to this class. Overrides
22852      * Roo.dd.DragDrop, but all versions of this method through the
22853      * inheritance chain are called
22854      */
22855     applyConfig: function() {
22856         Roo.dd.DD.superclass.applyConfig.call(this);
22857         this.scroll = (this.config.scroll !== false);
22858     },
22859
22860     /*
22861      * Event that fires prior to the onMouseDown event.  Overrides
22862      * Roo.dd.DragDrop.
22863      */
22864     b4MouseDown: function(e) {
22865         // this.resetConstraints();
22866         this.autoOffset(e.getPageX(),
22867                             e.getPageY());
22868     },
22869
22870     /*
22871      * Event that fires prior to the onDrag event.  Overrides
22872      * Roo.dd.DragDrop.
22873      */
22874     b4Drag: function(e) {
22875         this.setDragElPos(e.getPageX(),
22876                             e.getPageY());
22877     },
22878
22879     toString: function() {
22880         return ("DD " + this.id);
22881     }
22882
22883     //////////////////////////////////////////////////////////////////////////
22884     // Debugging ygDragDrop events that can be overridden
22885     //////////////////////////////////////////////////////////////////////////
22886     /*
22887     startDrag: function(x, y) {
22888     },
22889
22890     onDrag: function(e) {
22891     },
22892
22893     onDragEnter: function(e, id) {
22894     },
22895
22896     onDragOver: function(e, id) {
22897     },
22898
22899     onDragOut: function(e, id) {
22900     },
22901
22902     onDragDrop: function(e, id) {
22903     },
22904
22905     endDrag: function(e) {
22906     }
22907
22908     */
22909
22910 });/*
22911  * Based on:
22912  * Ext JS Library 1.1.1
22913  * Copyright(c) 2006-2007, Ext JS, LLC.
22914  *
22915  * Originally Released Under LGPL - original licence link has changed is not relivant.
22916  *
22917  * Fork - LGPL
22918  * <script type="text/javascript">
22919  */
22920
22921 /**
22922  * @class Roo.dd.DDProxy
22923  * A DragDrop implementation that inserts an empty, bordered div into
22924  * the document that follows the cursor during drag operations.  At the time of
22925  * the click, the frame div is resized to the dimensions of the linked html
22926  * element, and moved to the exact location of the linked element.
22927  *
22928  * References to the "frame" element refer to the single proxy element that
22929  * was created to be dragged in place of all DDProxy elements on the
22930  * page.
22931  *
22932  * @extends Roo.dd.DD
22933  * @constructor
22934  * @param {String} id the id of the linked html element
22935  * @param {String} sGroup the group of related DragDrop objects
22936  * @param {object} config an object containing configurable attributes
22937  *                Valid properties for DDProxy in addition to those in DragDrop:
22938  *                   resizeFrame, centerFrame, dragElId
22939  */
22940 Roo.dd.DDProxy = function(id, sGroup, config) {
22941     if (id) {
22942         this.init(id, sGroup, config);
22943         this.initFrame();
22944     }
22945 };
22946
22947 /**
22948  * The default drag frame div id
22949  * @property Roo.dd.DDProxy.dragElId
22950  * @type String
22951  * @static
22952  */
22953 Roo.dd.DDProxy.dragElId = "ygddfdiv";
22954
22955 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
22956
22957     /**
22958      * By default we resize the drag frame to be the same size as the element
22959      * we want to drag (this is to get the frame effect).  We can turn it off
22960      * if we want a different behavior.
22961      * @property resizeFrame
22962      * @type boolean
22963      */
22964     resizeFrame: true,
22965
22966     /**
22967      * By default the frame is positioned exactly where the drag element is, so
22968      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
22969      * you do not have constraints on the obj is to have the drag frame centered
22970      * around the cursor.  Set centerFrame to true for this effect.
22971      * @property centerFrame
22972      * @type boolean
22973      */
22974     centerFrame: false,
22975
22976     /**
22977      * Creates the proxy element if it does not yet exist
22978      * @method createFrame
22979      */
22980     createFrame: function() {
22981         var self = this;
22982         var body = document.body;
22983
22984         if (!body || !body.firstChild) {
22985             setTimeout( function() { self.createFrame(); }, 50 );
22986             return;
22987         }
22988
22989         var div = this.getDragEl();
22990
22991         if (!div) {
22992             div    = document.createElement("div");
22993             div.id = this.dragElId;
22994             var s  = div.style;
22995
22996             s.position   = "absolute";
22997             s.visibility = "hidden";
22998             s.cursor     = "move";
22999             s.border     = "2px solid #aaa";
23000             s.zIndex     = 999;
23001
23002             // appendChild can blow up IE if invoked prior to the window load event
23003             // while rendering a table.  It is possible there are other scenarios
23004             // that would cause this to happen as well.
23005             body.insertBefore(div, body.firstChild);
23006         }
23007     },
23008
23009     /**
23010      * Initialization for the drag frame element.  Must be called in the
23011      * constructor of all subclasses
23012      * @method initFrame
23013      */
23014     initFrame: function() {
23015         this.createFrame();
23016     },
23017
23018     applyConfig: function() {
23019         Roo.dd.DDProxy.superclass.applyConfig.call(this);
23020
23021         this.resizeFrame = (this.config.resizeFrame !== false);
23022         this.centerFrame = (this.config.centerFrame);
23023         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
23024     },
23025
23026     /**
23027      * Resizes the drag frame to the dimensions of the clicked object, positions
23028      * it over the object, and finally displays it
23029      * @method showFrame
23030      * @param {int} iPageX X click position
23031      * @param {int} iPageY Y click position
23032      * @private
23033      */
23034     showFrame: function(iPageX, iPageY) {
23035         var el = this.getEl();
23036         var dragEl = this.getDragEl();
23037         var s = dragEl.style;
23038
23039         this._resizeProxy();
23040
23041         if (this.centerFrame) {
23042             this.setDelta( Math.round(parseInt(s.width,  10)/2),
23043                            Math.round(parseInt(s.height, 10)/2) );
23044         }
23045
23046         this.setDragElPos(iPageX, iPageY);
23047
23048         Roo.fly(dragEl).show();
23049     },
23050
23051     /**
23052      * The proxy is automatically resized to the dimensions of the linked
23053      * element when a drag is initiated, unless resizeFrame is set to false
23054      * @method _resizeProxy
23055      * @private
23056      */
23057     _resizeProxy: function() {
23058         if (this.resizeFrame) {
23059             var el = this.getEl();
23060             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
23061         }
23062     },
23063
23064     // overrides Roo.dd.DragDrop
23065     b4MouseDown: function(e) {
23066         var x = e.getPageX();
23067         var y = e.getPageY();
23068         this.autoOffset(x, y);
23069         this.setDragElPos(x, y);
23070     },
23071
23072     // overrides Roo.dd.DragDrop
23073     b4StartDrag: function(x, y) {
23074         // show the drag frame
23075         this.showFrame(x, y);
23076     },
23077
23078     // overrides Roo.dd.DragDrop
23079     b4EndDrag: function(e) {
23080         Roo.fly(this.getDragEl()).hide();
23081     },
23082
23083     // overrides Roo.dd.DragDrop
23084     // By default we try to move the element to the last location of the frame.
23085     // This is so that the default behavior mirrors that of Roo.dd.DD.
23086     endDrag: function(e) {
23087
23088         var lel = this.getEl();
23089         var del = this.getDragEl();
23090
23091         // Show the drag frame briefly so we can get its position
23092         del.style.visibility = "";
23093
23094         this.beforeMove();
23095         // Hide the linked element before the move to get around a Safari
23096         // rendering bug.
23097         lel.style.visibility = "hidden";
23098         Roo.dd.DDM.moveToEl(lel, del);
23099         del.style.visibility = "hidden";
23100         lel.style.visibility = "";
23101
23102         this.afterDrag();
23103     },
23104
23105     beforeMove : function(){
23106
23107     },
23108
23109     afterDrag : function(){
23110
23111     },
23112
23113     toString: function() {
23114         return ("DDProxy " + this.id);
23115     }
23116
23117 });
23118 /*
23119  * Based on:
23120  * Ext JS Library 1.1.1
23121  * Copyright(c) 2006-2007, Ext JS, LLC.
23122  *
23123  * Originally Released Under LGPL - original licence link has changed is not relivant.
23124  *
23125  * Fork - LGPL
23126  * <script type="text/javascript">
23127  */
23128
23129  /**
23130  * @class Roo.dd.DDTarget
23131  * A DragDrop implementation that does not move, but can be a drop
23132  * target.  You would get the same result by simply omitting implementation
23133  * for the event callbacks, but this way we reduce the processing cost of the
23134  * event listener and the callbacks.
23135  * @extends Roo.dd.DragDrop
23136  * @constructor
23137  * @param {String} id the id of the element that is a drop target
23138  * @param {String} sGroup the group of related DragDrop objects
23139  * @param {object} config an object containing configurable attributes
23140  *                 Valid properties for DDTarget in addition to those in
23141  *                 DragDrop:
23142  *                    none
23143  */
23144 Roo.dd.DDTarget = function(id, sGroup, config) {
23145     if (id) {
23146         this.initTarget(id, sGroup, config);
23147     }
23148     if (config && (config.listeners || config.events)) { 
23149         Roo.dd.DragDrop.superclass.constructor.call(this,  { 
23150             listeners : config.listeners || {}, 
23151             events : config.events || {} 
23152         });    
23153     }
23154 };
23155
23156 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
23157 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
23158     toString: function() {
23159         return ("DDTarget " + this.id);
23160     }
23161 });
23162 /*
23163  * Based on:
23164  * Ext JS Library 1.1.1
23165  * Copyright(c) 2006-2007, Ext JS, LLC.
23166  *
23167  * Originally Released Under LGPL - original licence link has changed is not relivant.
23168  *
23169  * Fork - LGPL
23170  * <script type="text/javascript">
23171  */
23172  
23173
23174 /**
23175  * @class Roo.dd.ScrollManager
23176  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
23177  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
23178  * @static
23179  */
23180 Roo.dd.ScrollManager = function(){
23181     var ddm = Roo.dd.DragDropMgr;
23182     var els = {};
23183     var dragEl = null;
23184     var proc = {};
23185     
23186     
23187     
23188     var onStop = function(e){
23189         dragEl = null;
23190         clearProc();
23191     };
23192     
23193     var triggerRefresh = function(){
23194         if(ddm.dragCurrent){
23195              ddm.refreshCache(ddm.dragCurrent.groups);
23196         }
23197     };
23198     
23199     var doScroll = function(){
23200         if(ddm.dragCurrent){
23201             var dds = Roo.dd.ScrollManager;
23202             if(!dds.animate){
23203                 if(proc.el.scroll(proc.dir, dds.increment)){
23204                     triggerRefresh();
23205                 }
23206             }else{
23207                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
23208             }
23209         }
23210     };
23211     
23212     var clearProc = function(){
23213         if(proc.id){
23214             clearInterval(proc.id);
23215         }
23216         proc.id = 0;
23217         proc.el = null;
23218         proc.dir = "";
23219     };
23220     
23221     var startProc = function(el, dir){
23222          Roo.log('scroll startproc');
23223         clearProc();
23224         proc.el = el;
23225         proc.dir = dir;
23226         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
23227     };
23228     
23229     var onFire = function(e, isDrop){
23230        
23231         if(isDrop || !ddm.dragCurrent){ return; }
23232         var dds = Roo.dd.ScrollManager;
23233         if(!dragEl || dragEl != ddm.dragCurrent){
23234             dragEl = ddm.dragCurrent;
23235             // refresh regions on drag start
23236             dds.refreshCache();
23237         }
23238         
23239         var xy = Roo.lib.Event.getXY(e);
23240         var pt = new Roo.lib.Point(xy[0], xy[1]);
23241         for(var id in els){
23242             var el = els[id], r = el._region;
23243             if(r && r.contains(pt) && el.isScrollable()){
23244                 if(r.bottom - pt.y <= dds.thresh){
23245                     if(proc.el != el){
23246                         startProc(el, "down");
23247                     }
23248                     return;
23249                 }else if(r.right - pt.x <= dds.thresh){
23250                     if(proc.el != el){
23251                         startProc(el, "left");
23252                     }
23253                     return;
23254                 }else if(pt.y - r.top <= dds.thresh){
23255                     if(proc.el != el){
23256                         startProc(el, "up");
23257                     }
23258                     return;
23259                 }else if(pt.x - r.left <= dds.thresh){
23260                     if(proc.el != el){
23261                         startProc(el, "right");
23262                     }
23263                     return;
23264                 }
23265             }
23266         }
23267         clearProc();
23268     };
23269     
23270     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
23271     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
23272     
23273     return {
23274         /**
23275          * Registers new overflow element(s) to auto scroll
23276          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
23277          */
23278         register : function(el){
23279             if(el instanceof Array){
23280                 for(var i = 0, len = el.length; i < len; i++) {
23281                         this.register(el[i]);
23282                 }
23283             }else{
23284                 el = Roo.get(el);
23285                 els[el.id] = el;
23286             }
23287             Roo.dd.ScrollManager.els = els;
23288         },
23289         
23290         /**
23291          * Unregisters overflow element(s) so they are no longer scrolled
23292          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
23293          */
23294         unregister : function(el){
23295             if(el instanceof Array){
23296                 for(var i = 0, len = el.length; i < len; i++) {
23297                         this.unregister(el[i]);
23298                 }
23299             }else{
23300                 el = Roo.get(el);
23301                 delete els[el.id];
23302             }
23303         },
23304         
23305         /**
23306          * The number of pixels from the edge of a container the pointer needs to be to 
23307          * trigger scrolling (defaults to 25)
23308          * @type Number
23309          */
23310         thresh : 25,
23311         
23312         /**
23313          * The number of pixels to scroll in each scroll increment (defaults to 50)
23314          * @type Number
23315          */
23316         increment : 100,
23317         
23318         /**
23319          * The frequency of scrolls in milliseconds (defaults to 500)
23320          * @type Number
23321          */
23322         frequency : 500,
23323         
23324         /**
23325          * True to animate the scroll (defaults to true)
23326          * @type Boolean
23327          */
23328         animate: true,
23329         
23330         /**
23331          * The animation duration in seconds - 
23332          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
23333          * @type Number
23334          */
23335         animDuration: .4,
23336         
23337         /**
23338          * Manually trigger a cache refresh.
23339          */
23340         refreshCache : function(){
23341             for(var id in els){
23342                 if(typeof els[id] == 'object'){ // for people extending the object prototype
23343                     els[id]._region = els[id].getRegion();
23344                 }
23345             }
23346         }
23347     };
23348 }();/*
23349  * Based on:
23350  * Ext JS Library 1.1.1
23351  * Copyright(c) 2006-2007, Ext JS, LLC.
23352  *
23353  * Originally Released Under LGPL - original licence link has changed is not relivant.
23354  *
23355  * Fork - LGPL
23356  * <script type="text/javascript">
23357  */
23358  
23359
23360 /**
23361  * @class Roo.dd.Registry
23362  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
23363  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
23364  * @static
23365  */
23366 Roo.dd.Registry = function(){
23367     var elements = {}; 
23368     var handles = {}; 
23369     var autoIdSeed = 0;
23370
23371     var getId = function(el, autogen){
23372         if(typeof el == "string"){
23373             return el;
23374         }
23375         var id = el.id;
23376         if(!id && autogen !== false){
23377             id = "roodd-" + (++autoIdSeed);
23378             el.id = id;
23379         }
23380         return id;
23381     };
23382     
23383     return {
23384     /**
23385      * Register a drag drop element
23386      * @param {String|HTMLElement} element The id or DOM node to register
23387      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
23388      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
23389      * knows how to interpret, plus there are some specific properties known to the Registry that should be
23390      * populated in the data object (if applicable):
23391      * <pre>
23392 Value      Description<br />
23393 ---------  ------------------------------------------<br />
23394 handles    Array of DOM nodes that trigger dragging<br />
23395            for the element being registered<br />
23396 isHandle   True if the element passed in triggers<br />
23397            dragging itself, else false
23398 </pre>
23399      */
23400         register : function(el, data){
23401             data = data || {};
23402             if(typeof el == "string"){
23403                 el = document.getElementById(el);
23404             }
23405             data.ddel = el;
23406             elements[getId(el)] = data;
23407             if(data.isHandle !== false){
23408                 handles[data.ddel.id] = data;
23409             }
23410             if(data.handles){
23411                 var hs = data.handles;
23412                 for(var i = 0, len = hs.length; i < len; i++){
23413                         handles[getId(hs[i])] = data;
23414                 }
23415             }
23416         },
23417
23418     /**
23419      * Unregister a drag drop element
23420      * @param {String|HTMLElement}  element The id or DOM node to unregister
23421      */
23422         unregister : function(el){
23423             var id = getId(el, false);
23424             var data = elements[id];
23425             if(data){
23426                 delete elements[id];
23427                 if(data.handles){
23428                     var hs = data.handles;
23429                     for(var i = 0, len = hs.length; i < len; i++){
23430                         delete handles[getId(hs[i], false)];
23431                     }
23432                 }
23433             }
23434         },
23435
23436     /**
23437      * Returns the handle registered for a DOM Node by id
23438      * @param {String|HTMLElement} id The DOM node or id to look up
23439      * @return {Object} handle The custom handle data
23440      */
23441         getHandle : function(id){
23442             if(typeof id != "string"){ // must be element?
23443                 id = id.id;
23444             }
23445             return handles[id];
23446         },
23447
23448     /**
23449      * Returns the handle that is registered for the DOM node that is the target of the event
23450      * @param {Event} e The event
23451      * @return {Object} handle The custom handle data
23452      */
23453         getHandleFromEvent : function(e){
23454             var t = Roo.lib.Event.getTarget(e);
23455             return t ? handles[t.id] : null;
23456         },
23457
23458     /**
23459      * Returns a custom data object that is registered for a DOM node by id
23460      * @param {String|HTMLElement} id The DOM node or id to look up
23461      * @return {Object} data The custom data
23462      */
23463         getTarget : function(id){
23464             if(typeof id != "string"){ // must be element?
23465                 id = id.id;
23466             }
23467             return elements[id];
23468         },
23469
23470     /**
23471      * Returns a custom data object that is registered for the DOM node that is the target of the event
23472      * @param {Event} e The event
23473      * @return {Object} data The custom data
23474      */
23475         getTargetFromEvent : function(e){
23476             var t = Roo.lib.Event.getTarget(e);
23477             return t ? elements[t.id] || handles[t.id] : null;
23478         }
23479     };
23480 }();/*
23481  * Based on:
23482  * Ext JS Library 1.1.1
23483  * Copyright(c) 2006-2007, Ext JS, LLC.
23484  *
23485  * Originally Released Under LGPL - original licence link has changed is not relivant.
23486  *
23487  * Fork - LGPL
23488  * <script type="text/javascript">
23489  */
23490  
23491
23492 /**
23493  * @class Roo.dd.StatusProxy
23494  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
23495  * default drag proxy used by all Roo.dd components.
23496  * @constructor
23497  * @param {Object} config
23498  */
23499 Roo.dd.StatusProxy = function(config){
23500     Roo.apply(this, config);
23501     this.id = this.id || Roo.id();
23502     this.el = new Roo.Layer({
23503         dh: {
23504             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
23505                 {tag: "div", cls: "x-dd-drop-icon"},
23506                 {tag: "div", cls: "x-dd-drag-ghost"}
23507             ]
23508         }, 
23509         shadow: !config || config.shadow !== false
23510     });
23511     this.ghost = Roo.get(this.el.dom.childNodes[1]);
23512     this.dropStatus = this.dropNotAllowed;
23513 };
23514
23515 Roo.dd.StatusProxy.prototype = {
23516     /**
23517      * @cfg {String} dropAllowed
23518      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
23519      */
23520     dropAllowed : "x-dd-drop-ok",
23521     /**
23522      * @cfg {String} dropNotAllowed
23523      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
23524      */
23525     dropNotAllowed : "x-dd-drop-nodrop",
23526
23527     /**
23528      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
23529      * over the current target element.
23530      * @param {String} cssClass The css class for the new drop status indicator image
23531      */
23532     setStatus : function(cssClass){
23533         cssClass = cssClass || this.dropNotAllowed;
23534         if(this.dropStatus != cssClass){
23535             this.el.replaceClass(this.dropStatus, cssClass);
23536             this.dropStatus = cssClass;
23537         }
23538     },
23539
23540     /**
23541      * Resets the status indicator to the default dropNotAllowed value
23542      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
23543      */
23544     reset : function(clearGhost){
23545         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
23546         this.dropStatus = this.dropNotAllowed;
23547         if(clearGhost){
23548             this.ghost.update("");
23549         }
23550     },
23551
23552     /**
23553      * Updates the contents of the ghost element
23554      * @param {String} html The html that will replace the current innerHTML of the ghost element
23555      */
23556     update : function(html){
23557         if(typeof html == "string"){
23558             this.ghost.update(html);
23559         }else{
23560             this.ghost.update("");
23561             html.style.margin = "0";
23562             this.ghost.dom.appendChild(html);
23563         }
23564         // ensure float = none set?? cant remember why though.
23565         var el = this.ghost.dom.firstChild;
23566                 if(el){
23567                         Roo.fly(el).setStyle('float', 'none');
23568                 }
23569     },
23570     
23571     /**
23572      * Returns the underlying proxy {@link Roo.Layer}
23573      * @return {Roo.Layer} el
23574     */
23575     getEl : function(){
23576         return this.el;
23577     },
23578
23579     /**
23580      * Returns the ghost element
23581      * @return {Roo.Element} el
23582      */
23583     getGhost : function(){
23584         return this.ghost;
23585     },
23586
23587     /**
23588      * Hides the proxy
23589      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
23590      */
23591     hide : function(clear){
23592         this.el.hide();
23593         if(clear){
23594             this.reset(true);
23595         }
23596     },
23597
23598     /**
23599      * Stops the repair animation if it's currently running
23600      */
23601     stop : function(){
23602         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
23603             this.anim.stop();
23604         }
23605     },
23606
23607     /**
23608      * Displays this proxy
23609      */
23610     show : function(){
23611         this.el.show();
23612     },
23613
23614     /**
23615      * Force the Layer to sync its shadow and shim positions to the element
23616      */
23617     sync : function(){
23618         this.el.sync();
23619     },
23620
23621     /**
23622      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
23623      * invalid drop operation by the item being dragged.
23624      * @param {Array} xy The XY position of the element ([x, y])
23625      * @param {Function} callback The function to call after the repair is complete
23626      * @param {Object} scope The scope in which to execute the callback
23627      */
23628     repair : function(xy, callback, scope){
23629         this.callback = callback;
23630         this.scope = scope;
23631         if(xy && this.animRepair !== false){
23632             this.el.addClass("x-dd-drag-repair");
23633             this.el.hideUnders(true);
23634             this.anim = this.el.shift({
23635                 duration: this.repairDuration || .5,
23636                 easing: 'easeOut',
23637                 xy: xy,
23638                 stopFx: true,
23639                 callback: this.afterRepair,
23640                 scope: this
23641             });
23642         }else{
23643             this.afterRepair();
23644         }
23645     },
23646
23647     // private
23648     afterRepair : function(){
23649         this.hide(true);
23650         if(typeof this.callback == "function"){
23651             this.callback.call(this.scope || this);
23652         }
23653         this.callback = null;
23654         this.scope = null;
23655     }
23656 };/*
23657  * Based on:
23658  * Ext JS Library 1.1.1
23659  * Copyright(c) 2006-2007, Ext JS, LLC.
23660  *
23661  * Originally Released Under LGPL - original licence link has changed is not relivant.
23662  *
23663  * Fork - LGPL
23664  * <script type="text/javascript">
23665  */
23666
23667 /**
23668  * @class Roo.dd.DragSource
23669  * @extends Roo.dd.DDProxy
23670  * A simple class that provides the basic implementation needed to make any element draggable.
23671  * @constructor
23672  * @param {String/HTMLElement/Element} el The container element
23673  * @param {Object} config
23674  */
23675 Roo.dd.DragSource = function(el, config){
23676     this.el = Roo.get(el);
23677     this.dragData = {};
23678     
23679     Roo.apply(this, config);
23680     
23681     if(!this.proxy){
23682         this.proxy = new Roo.dd.StatusProxy();
23683     }
23684
23685     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
23686           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
23687     
23688     this.dragging = false;
23689 };
23690
23691 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
23692     /**
23693      * @cfg {String} dropAllowed
23694      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23695      */
23696     dropAllowed : "x-dd-drop-ok",
23697     /**
23698      * @cfg {String} dropNotAllowed
23699      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23700      */
23701     dropNotAllowed : "x-dd-drop-nodrop",
23702
23703     /**
23704      * Returns the data object associated with this drag source
23705      * @return {Object} data An object containing arbitrary data
23706      */
23707     getDragData : function(e){
23708         return this.dragData;
23709     },
23710
23711     // private
23712     onDragEnter : function(e, id){
23713         var target = Roo.dd.DragDropMgr.getDDById(id);
23714         this.cachedTarget = target;
23715         if(this.beforeDragEnter(target, e, id) !== false){
23716             if(target.isNotifyTarget){
23717                 var status = target.notifyEnter(this, e, this.dragData);
23718                 this.proxy.setStatus(status);
23719             }else{
23720                 this.proxy.setStatus(this.dropAllowed);
23721             }
23722             
23723             if(this.afterDragEnter){
23724                 /**
23725                  * An empty function by default, but provided so that you can perform a custom action
23726                  * when the dragged item enters the drop target by providing an implementation.
23727                  * @param {Roo.dd.DragDrop} target The drop target
23728                  * @param {Event} e The event object
23729                  * @param {String} id The id of the dragged element
23730                  * @method afterDragEnter
23731                  */
23732                 this.afterDragEnter(target, e, id);
23733             }
23734         }
23735     },
23736
23737     /**
23738      * An empty function by default, but provided so that you can perform a custom action
23739      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
23740      * @param {Roo.dd.DragDrop} target The drop target
23741      * @param {Event} e The event object
23742      * @param {String} id The id of the dragged element
23743      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23744      */
23745     beforeDragEnter : function(target, e, id){
23746         return true;
23747     },
23748
23749     // private
23750     alignElWithMouse: function() {
23751         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
23752         this.proxy.sync();
23753     },
23754
23755     // private
23756     onDragOver : function(e, id){
23757         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23758         if(this.beforeDragOver(target, e, id) !== false){
23759             if(target.isNotifyTarget){
23760                 var status = target.notifyOver(this, e, this.dragData);
23761                 this.proxy.setStatus(status);
23762             }
23763
23764             if(this.afterDragOver){
23765                 /**
23766                  * An empty function by default, but provided so that you can perform a custom action
23767                  * while the dragged item is over the drop target by providing an implementation.
23768                  * @param {Roo.dd.DragDrop} target The drop target
23769                  * @param {Event} e The event object
23770                  * @param {String} id The id of the dragged element
23771                  * @method afterDragOver
23772                  */
23773                 this.afterDragOver(target, e, id);
23774             }
23775         }
23776     },
23777
23778     /**
23779      * An empty function by default, but provided so that you can perform a custom action
23780      * while the dragged item is over the drop target and optionally cancel the onDragOver.
23781      * @param {Roo.dd.DragDrop} target The drop target
23782      * @param {Event} e The event object
23783      * @param {String} id The id of the dragged element
23784      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23785      */
23786     beforeDragOver : function(target, e, id){
23787         return true;
23788     },
23789
23790     // private
23791     onDragOut : function(e, id){
23792         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23793         if(this.beforeDragOut(target, e, id) !== false){
23794             if(target.isNotifyTarget){
23795                 target.notifyOut(this, e, this.dragData);
23796             }
23797             this.proxy.reset();
23798             if(this.afterDragOut){
23799                 /**
23800                  * An empty function by default, but provided so that you can perform a custom action
23801                  * after the dragged item is dragged out of the target without dropping.
23802                  * @param {Roo.dd.DragDrop} target The drop target
23803                  * @param {Event} e The event object
23804                  * @param {String} id The id of the dragged element
23805                  * @method afterDragOut
23806                  */
23807                 this.afterDragOut(target, e, id);
23808             }
23809         }
23810         this.cachedTarget = null;
23811     },
23812
23813     /**
23814      * An empty function by default, but provided so that you can perform a custom action before the dragged
23815      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
23816      * @param {Roo.dd.DragDrop} target The drop target
23817      * @param {Event} e The event object
23818      * @param {String} id The id of the dragged element
23819      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23820      */
23821     beforeDragOut : function(target, e, id){
23822         return true;
23823     },
23824     
23825     // private
23826     onDragDrop : function(e, id){
23827         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23828         if(this.beforeDragDrop(target, e, id) !== false){
23829             if(target.isNotifyTarget){
23830                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
23831                     this.onValidDrop(target, e, id);
23832                 }else{
23833                     this.onInvalidDrop(target, e, id);
23834                 }
23835             }else{
23836                 this.onValidDrop(target, e, id);
23837             }
23838             
23839             if(this.afterDragDrop){
23840                 /**
23841                  * An empty function by default, but provided so that you can perform a custom action
23842                  * after a valid drag drop has occurred by providing an implementation.
23843                  * @param {Roo.dd.DragDrop} target The drop target
23844                  * @param {Event} e The event object
23845                  * @param {String} id The id of the dropped element
23846                  * @method afterDragDrop
23847                  */
23848                 this.afterDragDrop(target, e, id);
23849             }
23850         }
23851         delete this.cachedTarget;
23852     },
23853
23854     /**
23855      * An empty function by default, but provided so that you can perform a custom action before the dragged
23856      * item is dropped onto the target and optionally cancel the onDragDrop.
23857      * @param {Roo.dd.DragDrop} target The drop target
23858      * @param {Event} e The event object
23859      * @param {String} id The id of the dragged element
23860      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
23861      */
23862     beforeDragDrop : function(target, e, id){
23863         return true;
23864     },
23865
23866     // private
23867     onValidDrop : function(target, e, id){
23868         this.hideProxy();
23869         if(this.afterValidDrop){
23870             /**
23871              * An empty function by default, but provided so that you can perform a custom action
23872              * after a valid drop has occurred by providing an implementation.
23873              * @param {Object} target The target DD 
23874              * @param {Event} e The event object
23875              * @param {String} id The id of the dropped element
23876              * @method afterInvalidDrop
23877              */
23878             this.afterValidDrop(target, e, id);
23879         }
23880     },
23881
23882     // private
23883     getRepairXY : function(e, data){
23884         return this.el.getXY();  
23885     },
23886
23887     // private
23888     onInvalidDrop : function(target, e, id){
23889         this.beforeInvalidDrop(target, e, id);
23890         if(this.cachedTarget){
23891             if(this.cachedTarget.isNotifyTarget){
23892                 this.cachedTarget.notifyOut(this, e, this.dragData);
23893             }
23894             this.cacheTarget = null;
23895         }
23896         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
23897
23898         if(this.afterInvalidDrop){
23899             /**
23900              * An empty function by default, but provided so that you can perform a custom action
23901              * after an invalid drop has occurred by providing an implementation.
23902              * @param {Event} e The event object
23903              * @param {String} id The id of the dropped element
23904              * @method afterInvalidDrop
23905              */
23906             this.afterInvalidDrop(e, id);
23907         }
23908     },
23909
23910     // private
23911     afterRepair : function(){
23912         if(Roo.enableFx){
23913             this.el.highlight(this.hlColor || "c3daf9");
23914         }
23915         this.dragging = false;
23916     },
23917
23918     /**
23919      * An empty function by default, but provided so that you can perform a custom action after an invalid
23920      * drop has occurred.
23921      * @param {Roo.dd.DragDrop} target The drop target
23922      * @param {Event} e The event object
23923      * @param {String} id The id of the dragged element
23924      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
23925      */
23926     beforeInvalidDrop : function(target, e, id){
23927         return true;
23928     },
23929
23930     // private
23931     handleMouseDown : function(e){
23932         if(this.dragging) {
23933             return;
23934         }
23935         var data = this.getDragData(e);
23936         if(data && this.onBeforeDrag(data, e) !== false){
23937             this.dragData = data;
23938             this.proxy.stop();
23939             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
23940         } 
23941     },
23942
23943     /**
23944      * An empty function by default, but provided so that you can perform a custom action before the initial
23945      * drag event begins and optionally cancel it.
23946      * @param {Object} data An object containing arbitrary data to be shared with drop targets
23947      * @param {Event} e The event object
23948      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23949      */
23950     onBeforeDrag : function(data, e){
23951         return true;
23952     },
23953
23954     /**
23955      * An empty function by default, but provided so that you can perform a custom action once the initial
23956      * drag event has begun.  The drag cannot be canceled from this function.
23957      * @param {Number} x The x position of the click on the dragged object
23958      * @param {Number} y The y position of the click on the dragged object
23959      */
23960     onStartDrag : Roo.emptyFn,
23961
23962     // private - YUI override
23963     startDrag : function(x, y){
23964         this.proxy.reset();
23965         this.dragging = true;
23966         this.proxy.update("");
23967         this.onInitDrag(x, y);
23968         this.proxy.show();
23969     },
23970
23971     // private
23972     onInitDrag : function(x, y){
23973         var clone = this.el.dom.cloneNode(true);
23974         clone.id = Roo.id(); // prevent duplicate ids
23975         this.proxy.update(clone);
23976         this.onStartDrag(x, y);
23977         return true;
23978     },
23979
23980     /**
23981      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
23982      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
23983      */
23984     getProxy : function(){
23985         return this.proxy;  
23986     },
23987
23988     /**
23989      * Hides the drag source's {@link Roo.dd.StatusProxy}
23990      */
23991     hideProxy : function(){
23992         this.proxy.hide();  
23993         this.proxy.reset(true);
23994         this.dragging = false;
23995     },
23996
23997     // private
23998     triggerCacheRefresh : function(){
23999         Roo.dd.DDM.refreshCache(this.groups);
24000     },
24001
24002     // private - override to prevent hiding
24003     b4EndDrag: function(e) {
24004     },
24005
24006     // private - override to prevent moving
24007     endDrag : function(e){
24008         this.onEndDrag(this.dragData, e);
24009     },
24010
24011     // private
24012     onEndDrag : function(data, e){
24013     },
24014     
24015     // private - pin to cursor
24016     autoOffset : function(x, y) {
24017         this.setDelta(-12, -20);
24018     }    
24019 });/*
24020  * Based on:
24021  * Ext JS Library 1.1.1
24022  * Copyright(c) 2006-2007, Ext JS, LLC.
24023  *
24024  * Originally Released Under LGPL - original licence link has changed is not relivant.
24025  *
24026  * Fork - LGPL
24027  * <script type="text/javascript">
24028  */
24029
24030
24031 /**
24032  * @class Roo.dd.DropTarget
24033  * @extends Roo.dd.DDTarget
24034  * A simple class that provides the basic implementation needed to make any element a drop target that can have
24035  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
24036  * @constructor
24037  * @param {String/HTMLElement/Element} el The container element
24038  * @param {Object} config
24039  */
24040 Roo.dd.DropTarget = function(el, config){
24041     this.el = Roo.get(el);
24042     
24043     var listeners = false; ;
24044     if (config && config.listeners) {
24045         listeners= config.listeners;
24046         delete config.listeners;
24047     }
24048     Roo.apply(this, config);
24049     
24050     if(this.containerScroll){
24051         Roo.dd.ScrollManager.register(this.el);
24052     }
24053     this.addEvents( {
24054          /**
24055          * @scope Roo.dd.DropTarget
24056          */
24057          
24058          /**
24059          * @event enter
24060          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
24061          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
24062          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
24063          * 
24064          * IMPORTANT : it should set  this.valid to true|false
24065          * 
24066          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24067          * @param {Event} e The event
24068          * @param {Object} data An object containing arbitrary data supplied by the drag source
24069          */
24070         "enter" : true,
24071         
24072          /**
24073          * @event over
24074          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
24075          * This method will be called on every mouse movement while the drag source is over the drop target.
24076          * This default implementation simply returns the dropAllowed config value.
24077          * 
24078          * IMPORTANT : it should set  this.valid to true|false
24079          * 
24080          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24081          * @param {Event} e The event
24082          * @param {Object} data An object containing arbitrary data supplied by the drag source
24083          
24084          */
24085         "over" : true,
24086         /**
24087          * @event out
24088          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
24089          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
24090          * overClass (if any) from the drop element.
24091          * 
24092          * 
24093          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24094          * @param {Event} e The event
24095          * @param {Object} data An object containing arbitrary data supplied by the drag source
24096          */
24097          "out" : true,
24098          
24099         /**
24100          * @event drop
24101          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
24102          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
24103          * implementation that does something to process the drop event and returns true so that the drag source's
24104          * repair action does not run.
24105          * 
24106          * IMPORTANT : it should set this.success
24107          * 
24108          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24109          * @param {Event} e The event
24110          * @param {Object} data An object containing arbitrary data supplied by the drag source
24111         */
24112          "drop" : true
24113     });
24114             
24115      
24116     Roo.dd.DropTarget.superclass.constructor.call(  this, 
24117         this.el.dom, 
24118         this.ddGroup || this.group,
24119         {
24120             isTarget: true,
24121             listeners : listeners || {} 
24122            
24123         
24124         }
24125     );
24126
24127 };
24128
24129 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
24130     /**
24131      * @cfg {String} overClass
24132      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
24133      */
24134      /**
24135      * @cfg {String} ddGroup
24136      * The drag drop group to handle drop events for
24137      */
24138      
24139     /**
24140      * @cfg {String} dropAllowed
24141      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
24142      */
24143     dropAllowed : "x-dd-drop-ok",
24144     /**
24145      * @cfg {String} dropNotAllowed
24146      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
24147      */
24148     dropNotAllowed : "x-dd-drop-nodrop",
24149     /**
24150      * @cfg {boolean} success
24151      * set this after drop listener.. 
24152      */
24153     success : false,
24154     /**
24155      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
24156      * if the drop point is valid for over/enter..
24157      */
24158     valid : false,
24159     // private
24160     isTarget : true,
24161
24162     // private
24163     isNotifyTarget : true,
24164     
24165     /**
24166      * @hide
24167      */
24168     notifyEnter : function(dd, e, data)
24169     {
24170         this.valid = true;
24171         this.fireEvent('enter', dd, e, data);
24172         if(this.overClass){
24173             this.el.addClass(this.overClass);
24174         }
24175         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
24176             this.valid ? this.dropAllowed : this.dropNotAllowed
24177         );
24178     },
24179
24180     /**
24181      * @hide
24182      */
24183     notifyOver : function(dd, e, data)
24184     {
24185         this.valid = true;
24186         this.fireEvent('over', dd, e, data);
24187         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
24188             this.valid ? this.dropAllowed : this.dropNotAllowed
24189         );
24190     },
24191
24192     /**
24193      * @hide
24194      */
24195     notifyOut : function(dd, e, data)
24196     {
24197         this.fireEvent('out', dd, e, data);
24198         if(this.overClass){
24199             this.el.removeClass(this.overClass);
24200         }
24201     },
24202
24203     /**
24204      * @hide
24205      */
24206     notifyDrop : function(dd, e, data)
24207     {
24208         this.success = false;
24209         this.fireEvent('drop', dd, e, data);
24210         return this.success;
24211     }
24212 });/*
24213  * Based on:
24214  * Ext JS Library 1.1.1
24215  * Copyright(c) 2006-2007, Ext JS, LLC.
24216  *
24217  * Originally Released Under LGPL - original licence link has changed is not relivant.
24218  *
24219  * Fork - LGPL
24220  * <script type="text/javascript">
24221  */
24222
24223
24224 /**
24225  * @class Roo.dd.DragZone
24226  * @extends Roo.dd.DragSource
24227  * This class provides a container DD instance that proxies for multiple child node sources.<br />
24228  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
24229  * @constructor
24230  * @param {String/HTMLElement/Element} el The container element
24231  * @param {Object} config
24232  */
24233 Roo.dd.DragZone = function(el, config){
24234     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
24235     if(this.containerScroll){
24236         Roo.dd.ScrollManager.register(this.el);
24237     }
24238 };
24239
24240 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
24241     /**
24242      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
24243      * for auto scrolling during drag operations.
24244      */
24245     /**
24246      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
24247      * method after a failed drop (defaults to "c3daf9" - light blue)
24248      */
24249
24250     /**
24251      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
24252      * for a valid target to drag based on the mouse down. Override this method
24253      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
24254      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
24255      * @param {EventObject} e The mouse down event
24256      * @return {Object} The dragData
24257      */
24258     getDragData : function(e){
24259         return Roo.dd.Registry.getHandleFromEvent(e);
24260     },
24261     
24262     /**
24263      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
24264      * this.dragData.ddel
24265      * @param {Number} x The x position of the click on the dragged object
24266      * @param {Number} y The y position of the click on the dragged object
24267      * @return {Boolean} true to continue the drag, false to cancel
24268      */
24269     onInitDrag : function(x, y){
24270         this.proxy.update(this.dragData.ddel.cloneNode(true));
24271         this.onStartDrag(x, y);
24272         return true;
24273     },
24274     
24275     /**
24276      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
24277      */
24278     afterRepair : function(){
24279         if(Roo.enableFx){
24280             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
24281         }
24282         this.dragging = false;
24283     },
24284
24285     /**
24286      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
24287      * the XY of this.dragData.ddel
24288      * @param {EventObject} e The mouse up event
24289      * @return {Array} The xy location (e.g. [100, 200])
24290      */
24291     getRepairXY : function(e){
24292         return Roo.Element.fly(this.dragData.ddel).getXY();  
24293     }
24294 });/*
24295  * Based on:
24296  * Ext JS Library 1.1.1
24297  * Copyright(c) 2006-2007, Ext JS, LLC.
24298  *
24299  * Originally Released Under LGPL - original licence link has changed is not relivant.
24300  *
24301  * Fork - LGPL
24302  * <script type="text/javascript">
24303  */
24304 /**
24305  * @class Roo.dd.DropZone
24306  * @extends Roo.dd.DropTarget
24307  * This class provides a container DD instance that proxies for multiple child node targets.<br />
24308  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
24309  * @constructor
24310  * @param {String/HTMLElement/Element} el The container element
24311  * @param {Object} config
24312  */
24313 Roo.dd.DropZone = function(el, config){
24314     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
24315 };
24316
24317 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
24318     /**
24319      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
24320      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
24321      * provide your own custom lookup.
24322      * @param {Event} e The event
24323      * @return {Object} data The custom data
24324      */
24325     getTargetFromEvent : function(e){
24326         return Roo.dd.Registry.getTargetFromEvent(e);
24327     },
24328
24329     /**
24330      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
24331      * that it has registered.  This method has no default implementation and should be overridden to provide
24332      * node-specific processing if necessary.
24333      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
24334      * {@link #getTargetFromEvent} for this node)
24335      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24336      * @param {Event} e The event
24337      * @param {Object} data An object containing arbitrary data supplied by the drag source
24338      */
24339     onNodeEnter : function(n, dd, e, data){
24340         
24341     },
24342
24343     /**
24344      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
24345      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
24346      * overridden to provide the proper feedback.
24347      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24348      * {@link #getTargetFromEvent} for this node)
24349      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24350      * @param {Event} e The event
24351      * @param {Object} data An object containing arbitrary data supplied by the drag source
24352      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24353      * underlying {@link Roo.dd.StatusProxy} can be updated
24354      */
24355     onNodeOver : function(n, dd, e, data){
24356         return this.dropAllowed;
24357     },
24358
24359     /**
24360      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
24361      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
24362      * node-specific processing if necessary.
24363      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24364      * {@link #getTargetFromEvent} for this node)
24365      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24366      * @param {Event} e The event
24367      * @param {Object} data An object containing arbitrary data supplied by the drag source
24368      */
24369     onNodeOut : function(n, dd, e, data){
24370         
24371     },
24372
24373     /**
24374      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
24375      * the drop node.  The default implementation returns false, so it should be overridden to provide the
24376      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
24377      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24378      * {@link #getTargetFromEvent} for this node)
24379      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24380      * @param {Event} e The event
24381      * @param {Object} data An object containing arbitrary data supplied by the drag source
24382      * @return {Boolean} True if the drop was valid, else false
24383      */
24384     onNodeDrop : function(n, dd, e, data){
24385         return false;
24386     },
24387
24388     /**
24389      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
24390      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
24391      * it should be overridden to provide the proper feedback if necessary.
24392      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24393      * @param {Event} e The event
24394      * @param {Object} data An object containing arbitrary data supplied by the drag source
24395      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24396      * underlying {@link Roo.dd.StatusProxy} can be updated
24397      */
24398     onContainerOver : function(dd, e, data){
24399         return this.dropNotAllowed;
24400     },
24401
24402     /**
24403      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
24404      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
24405      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
24406      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
24407      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24408      * @param {Event} e The event
24409      * @param {Object} data An object containing arbitrary data supplied by the drag source
24410      * @return {Boolean} True if the drop was valid, else false
24411      */
24412     onContainerDrop : function(dd, e, data){
24413         return false;
24414     },
24415
24416     /**
24417      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
24418      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
24419      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
24420      * you should override this method and provide a custom implementation.
24421      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24422      * @param {Event} e The event
24423      * @param {Object} data An object containing arbitrary data supplied by the drag source
24424      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24425      * underlying {@link Roo.dd.StatusProxy} can be updated
24426      */
24427     notifyEnter : function(dd, e, data){
24428         return this.dropNotAllowed;
24429     },
24430
24431     /**
24432      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
24433      * This method will be called on every mouse movement while the drag source is over the drop zone.
24434      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
24435      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
24436      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
24437      * registered node, it will call {@link #onContainerOver}.
24438      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24439      * @param {Event} e The event
24440      * @param {Object} data An object containing arbitrary data supplied by the drag source
24441      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24442      * underlying {@link Roo.dd.StatusProxy} can be updated
24443      */
24444     notifyOver : function(dd, e, data){
24445         var n = this.getTargetFromEvent(e);
24446         if(!n){ // not over valid drop target
24447             if(this.lastOverNode){
24448                 this.onNodeOut(this.lastOverNode, dd, e, data);
24449                 this.lastOverNode = null;
24450             }
24451             return this.onContainerOver(dd, e, data);
24452         }
24453         if(this.lastOverNode != n){
24454             if(this.lastOverNode){
24455                 this.onNodeOut(this.lastOverNode, dd, e, data);
24456             }
24457             this.onNodeEnter(n, dd, e, data);
24458             this.lastOverNode = n;
24459         }
24460         return this.onNodeOver(n, dd, e, data);
24461     },
24462
24463     /**
24464      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
24465      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
24466      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
24467      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24468      * @param {Event} e The event
24469      * @param {Object} data An object containing arbitrary data supplied by the drag zone
24470      */
24471     notifyOut : function(dd, e, data){
24472         if(this.lastOverNode){
24473             this.onNodeOut(this.lastOverNode, dd, e, data);
24474             this.lastOverNode = null;
24475         }
24476     },
24477
24478     /**
24479      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
24480      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
24481      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
24482      * otherwise it will call {@link #onContainerDrop}.
24483      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24484      * @param {Event} e The event
24485      * @param {Object} data An object containing arbitrary data supplied by the drag source
24486      * @return {Boolean} True if the drop was valid, else false
24487      */
24488     notifyDrop : function(dd, e, data){
24489         if(this.lastOverNode){
24490             this.onNodeOut(this.lastOverNode, dd, e, data);
24491             this.lastOverNode = null;
24492         }
24493         var n = this.getTargetFromEvent(e);
24494         return n ?
24495             this.onNodeDrop(n, dd, e, data) :
24496             this.onContainerDrop(dd, e, data);
24497     },
24498
24499     // private
24500     triggerCacheRefresh : function(){
24501         Roo.dd.DDM.refreshCache(this.groups);
24502     }  
24503 });/*
24504  * Based on:
24505  * Ext JS Library 1.1.1
24506  * Copyright(c) 2006-2007, Ext JS, LLC.
24507  *
24508  * Originally Released Under LGPL - original licence link has changed is not relivant.
24509  *
24510  * Fork - LGPL
24511  * <script type="text/javascript">
24512  */
24513
24514
24515 /**
24516  * @class Roo.data.SortTypes
24517  * @static
24518  * Defines the default sorting (casting?) comparison functions used when sorting data.
24519  */
24520 Roo.data.SortTypes = {
24521     /**
24522      * Default sort that does nothing
24523      * @param {Mixed} s The value being converted
24524      * @return {Mixed} The comparison value
24525      */
24526     none : function(s){
24527         return s;
24528     },
24529     
24530     /**
24531      * The regular expression used to strip tags
24532      * @type {RegExp}
24533      * @property
24534      */
24535     stripTagsRE : /<\/?[^>]+>/gi,
24536     
24537     /**
24538      * Strips all HTML tags to sort on text only
24539      * @param {Mixed} s The value being converted
24540      * @return {String} The comparison value
24541      */
24542     asText : function(s){
24543         return String(s).replace(this.stripTagsRE, "");
24544     },
24545     
24546     /**
24547      * Strips all HTML tags to sort on text only - Case insensitive
24548      * @param {Mixed} s The value being converted
24549      * @return {String} The comparison value
24550      */
24551     asUCText : function(s){
24552         return String(s).toUpperCase().replace(this.stripTagsRE, "");
24553     },
24554     
24555     /**
24556      * Case insensitive string
24557      * @param {Mixed} s The value being converted
24558      * @return {String} The comparison value
24559      */
24560     asUCString : function(s) {
24561         return String(s).toUpperCase();
24562     },
24563     
24564     /**
24565      * Date sorting
24566      * @param {Mixed} s The value being converted
24567      * @return {Number} The comparison value
24568      */
24569     asDate : function(s) {
24570         if(!s){
24571             return 0;
24572         }
24573         if(s instanceof Date){
24574             return s.getTime();
24575         }
24576         return Date.parse(String(s));
24577     },
24578     
24579     /**
24580      * Float sorting
24581      * @param {Mixed} s The value being converted
24582      * @return {Float} The comparison value
24583      */
24584     asFloat : function(s) {
24585         var val = parseFloat(String(s).replace(/,/g, ""));
24586         if(isNaN(val)) {
24587             val = 0;
24588         }
24589         return val;
24590     },
24591     
24592     /**
24593      * Integer sorting
24594      * @param {Mixed} s The value being converted
24595      * @return {Number} The comparison value
24596      */
24597     asInt : function(s) {
24598         var val = parseInt(String(s).replace(/,/g, ""));
24599         if(isNaN(val)) {
24600             val = 0;
24601         }
24602         return val;
24603     }
24604 };/*
24605  * Based on:
24606  * Ext JS Library 1.1.1
24607  * Copyright(c) 2006-2007, Ext JS, LLC.
24608  *
24609  * Originally Released Under LGPL - original licence link has changed is not relivant.
24610  *
24611  * Fork - LGPL
24612  * <script type="text/javascript">
24613  */
24614
24615 /**
24616 * @class Roo.data.Record
24617  * Instances of this class encapsulate both record <em>definition</em> information, and record
24618  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
24619  * to access Records cached in an {@link Roo.data.Store} object.<br>
24620  * <p>
24621  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
24622  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
24623  * objects.<br>
24624  * <p>
24625  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
24626  * @constructor
24627  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
24628  * {@link #create}. The parameters are the same.
24629  * @param {Array} data An associative Array of data values keyed by the field name.
24630  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
24631  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
24632  * not specified an integer id is generated.
24633  */
24634 Roo.data.Record = function(data, id){
24635     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
24636     this.data = data;
24637 };
24638
24639 /**
24640  * Generate a constructor for a specific record layout.
24641  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
24642  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
24643  * Each field definition object may contain the following properties: <ul>
24644  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
24645  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
24646  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
24647  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
24648  * is being used, then this is a string containing the javascript expression to reference the data relative to 
24649  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
24650  * to the data item relative to the record element. If the mapping expression is the same as the field name,
24651  * this may be omitted.</p></li>
24652  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
24653  * <ul><li>auto (Default, implies no conversion)</li>
24654  * <li>string</li>
24655  * <li>int</li>
24656  * <li>float</li>
24657  * <li>boolean</li>
24658  * <li>date</li></ul></p></li>
24659  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
24660  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
24661  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
24662  * by the Reader into an object that will be stored in the Record. It is passed the
24663  * following parameters:<ul>
24664  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
24665  * </ul></p></li>
24666  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
24667  * </ul>
24668  * <br>usage:<br><pre><code>
24669 var TopicRecord = Roo.data.Record.create(
24670     {name: 'title', mapping: 'topic_title'},
24671     {name: 'author', mapping: 'username'},
24672     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
24673     {name: 'lastPost', mapping: 'post_time', type: 'date'},
24674     {name: 'lastPoster', mapping: 'user2'},
24675     {name: 'excerpt', mapping: 'post_text'}
24676 );
24677
24678 var myNewRecord = new TopicRecord({
24679     title: 'Do my job please',
24680     author: 'noobie',
24681     totalPosts: 1,
24682     lastPost: new Date(),
24683     lastPoster: 'Animal',
24684     excerpt: 'No way dude!'
24685 });
24686 myStore.add(myNewRecord);
24687 </code></pre>
24688  * @method create
24689  * @static
24690  */
24691 Roo.data.Record.create = function(o){
24692     var f = function(){
24693         f.superclass.constructor.apply(this, arguments);
24694     };
24695     Roo.extend(f, Roo.data.Record);
24696     var p = f.prototype;
24697     p.fields = new Roo.util.MixedCollection(false, function(field){
24698         return field.name;
24699     });
24700     for(var i = 0, len = o.length; i < len; i++){
24701         p.fields.add(new Roo.data.Field(o[i]));
24702     }
24703     f.getField = function(name){
24704         return p.fields.get(name);  
24705     };
24706     return f;
24707 };
24708
24709 Roo.data.Record.AUTO_ID = 1000;
24710 Roo.data.Record.EDIT = 'edit';
24711 Roo.data.Record.REJECT = 'reject';
24712 Roo.data.Record.COMMIT = 'commit';
24713
24714 Roo.data.Record.prototype = {
24715     /**
24716      * Readonly flag - true if this record has been modified.
24717      * @type Boolean
24718      */
24719     dirty : false,
24720     editing : false,
24721     error: null,
24722     modified: null,
24723
24724     // private
24725     join : function(store){
24726         this.store = store;
24727     },
24728
24729     /**
24730      * Set the named field to the specified value.
24731      * @param {String} name The name of the field to set.
24732      * @param {Object} value The value to set the field to.
24733      */
24734     set : function(name, value){
24735         if(this.data[name] == value){
24736             return;
24737         }
24738         this.dirty = true;
24739         if(!this.modified){
24740             this.modified = {};
24741         }
24742         if(typeof this.modified[name] == 'undefined'){
24743             this.modified[name] = this.data[name];
24744         }
24745         this.data[name] = value;
24746         if(!this.editing && this.store){
24747             this.store.afterEdit(this);
24748         }       
24749     },
24750
24751     /**
24752      * Get the value of the named field.
24753      * @param {String} name The name of the field to get the value of.
24754      * @return {Object} The value of the field.
24755      */
24756     get : function(name){
24757         return this.data[name]; 
24758     },
24759
24760     // private
24761     beginEdit : function(){
24762         this.editing = true;
24763         this.modified = {}; 
24764     },
24765
24766     // private
24767     cancelEdit : function(){
24768         this.editing = false;
24769         delete this.modified;
24770     },
24771
24772     // private
24773     endEdit : function(){
24774         this.editing = false;
24775         if(this.dirty && this.store){
24776             this.store.afterEdit(this);
24777         }
24778     },
24779
24780     /**
24781      * Usually called by the {@link Roo.data.Store} which owns the Record.
24782      * Rejects all changes made to the Record since either creation, or the last commit operation.
24783      * Modified fields are reverted to their original values.
24784      * <p>
24785      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24786      * of reject operations.
24787      */
24788     reject : function(){
24789         var m = this.modified;
24790         for(var n in m){
24791             if(typeof m[n] != "function"){
24792                 this.data[n] = m[n];
24793             }
24794         }
24795         this.dirty = false;
24796         delete this.modified;
24797         this.editing = false;
24798         if(this.store){
24799             this.store.afterReject(this);
24800         }
24801     },
24802
24803     /**
24804      * Usually called by the {@link Roo.data.Store} which owns the Record.
24805      * Commits all changes made to the Record since either creation, or the last commit operation.
24806      * <p>
24807      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24808      * of commit operations.
24809      */
24810     commit : function(){
24811         this.dirty = false;
24812         delete this.modified;
24813         this.editing = false;
24814         if(this.store){
24815             this.store.afterCommit(this);
24816         }
24817     },
24818
24819     // private
24820     hasError : function(){
24821         return this.error != null;
24822     },
24823
24824     // private
24825     clearError : function(){
24826         this.error = null;
24827     },
24828
24829     /**
24830      * Creates a copy of this record.
24831      * @param {String} id (optional) A new record id if you don't want to use this record's id
24832      * @return {Record}
24833      */
24834     copy : function(newId) {
24835         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
24836     }
24837 };/*
24838  * Based on:
24839  * Ext JS Library 1.1.1
24840  * Copyright(c) 2006-2007, Ext JS, LLC.
24841  *
24842  * Originally Released Under LGPL - original licence link has changed is not relivant.
24843  *
24844  * Fork - LGPL
24845  * <script type="text/javascript">
24846  */
24847
24848
24849
24850 /**
24851  * @class Roo.data.Store
24852  * @extends Roo.util.Observable
24853  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
24854  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
24855  * <p>
24856  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
24857  * has no knowledge of the format of the data returned by the Proxy.<br>
24858  * <p>
24859  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
24860  * instances from the data object. These records are cached and made available through accessor functions.
24861  * @constructor
24862  * Creates a new Store.
24863  * @param {Object} config A config object containing the objects needed for the Store to access data,
24864  * and read the data into Records.
24865  */
24866 Roo.data.Store = function(config){
24867     this.data = new Roo.util.MixedCollection(false);
24868     this.data.getKey = function(o){
24869         return o.id;
24870     };
24871     this.baseParams = {};
24872     // private
24873     this.paramNames = {
24874         "start" : "start",
24875         "limit" : "limit",
24876         "sort" : "sort",
24877         "dir" : "dir",
24878         "multisort" : "_multisort"
24879     };
24880
24881     if(config && config.data){
24882         this.inlineData = config.data;
24883         delete config.data;
24884     }
24885
24886     Roo.apply(this, config);
24887     
24888     if(this.reader){ // reader passed
24889         this.reader = Roo.factory(this.reader, Roo.data);
24890         this.reader.xmodule = this.xmodule || false;
24891         if(!this.recordType){
24892             this.recordType = this.reader.recordType;
24893         }
24894         if(this.reader.onMetaChange){
24895             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
24896         }
24897     }
24898
24899     if(this.recordType){
24900         this.fields = this.recordType.prototype.fields;
24901     }
24902     this.modified = [];
24903
24904     this.addEvents({
24905         /**
24906          * @event datachanged
24907          * Fires when the data cache has changed, and a widget which is using this Store
24908          * as a Record cache should refresh its view.
24909          * @param {Store} this
24910          */
24911         datachanged : true,
24912         /**
24913          * @event metachange
24914          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
24915          * @param {Store} this
24916          * @param {Object} meta The JSON metadata
24917          */
24918         metachange : true,
24919         /**
24920          * @event add
24921          * Fires when Records have been added to the Store
24922          * @param {Store} this
24923          * @param {Roo.data.Record[]} records The array of Records added
24924          * @param {Number} index The index at which the record(s) were added
24925          */
24926         add : true,
24927         /**
24928          * @event remove
24929          * Fires when a Record has been removed from the Store
24930          * @param {Store} this
24931          * @param {Roo.data.Record} record The Record that was removed
24932          * @param {Number} index The index at which the record was removed
24933          */
24934         remove : true,
24935         /**
24936          * @event update
24937          * Fires when a Record has been updated
24938          * @param {Store} this
24939          * @param {Roo.data.Record} record The Record that was updated
24940          * @param {String} operation The update operation being performed.  Value may be one of:
24941          * <pre><code>
24942  Roo.data.Record.EDIT
24943  Roo.data.Record.REJECT
24944  Roo.data.Record.COMMIT
24945          * </code></pre>
24946          */
24947         update : true,
24948         /**
24949          * @event clear
24950          * Fires when the data cache has been cleared.
24951          * @param {Store} this
24952          */
24953         clear : true,
24954         /**
24955          * @event beforeload
24956          * Fires before a request is made for a new data object.  If the beforeload handler returns false
24957          * the load action will be canceled.
24958          * @param {Store} this
24959          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24960          */
24961         beforeload : true,
24962         /**
24963          * @event beforeloadadd
24964          * Fires after a new set of Records has been loaded.
24965          * @param {Store} this
24966          * @param {Roo.data.Record[]} records The Records that were loaded
24967          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24968          */
24969         beforeloadadd : true,
24970         /**
24971          * @event load
24972          * Fires after a new set of Records has been loaded, before they are added to the store.
24973          * @param {Store} this
24974          * @param {Roo.data.Record[]} records The Records that were loaded
24975          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24976          * @params {Object} return from reader
24977          */
24978         load : true,
24979         /**
24980          * @event loadexception
24981          * Fires if an exception occurs in the Proxy during loading.
24982          * Called with the signature of the Proxy's "loadexception" event.
24983          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
24984          * 
24985          * @param {Proxy} 
24986          * @param {Object} ret return data from JsonData.reader() - success, totalRecords, records
24987          * @param {Object} opts - load Options
24988          * @param {Object} jsonData from your request (normally this contains the Exception)
24989          */
24990         loadexception : true
24991     });
24992     
24993     if(this.proxy){
24994         this.proxy = Roo.factory(this.proxy, Roo.data);
24995         this.proxy.xmodule = this.xmodule || false;
24996         this.relayEvents(this.proxy,  ["loadexception"]);
24997     }
24998     this.sortToggle = {};
24999     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
25000
25001     Roo.data.Store.superclass.constructor.call(this);
25002
25003     if(this.inlineData){
25004         this.loadData(this.inlineData);
25005         delete this.inlineData;
25006     }
25007 };
25008
25009 Roo.extend(Roo.data.Store, Roo.util.Observable, {
25010      /**
25011     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
25012     * without a remote query - used by combo/forms at present.
25013     */
25014     
25015     /**
25016     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
25017     */
25018     /**
25019     * @cfg {Array} data Inline data to be loaded when the store is initialized.
25020     */
25021     /**
25022     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
25023     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
25024     */
25025     /**
25026     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
25027     * on any HTTP request
25028     */
25029     /**
25030     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
25031     */
25032     /**
25033     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
25034     */
25035     multiSort: false,
25036     /**
25037     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
25038     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
25039     */
25040     remoteSort : false,
25041
25042     /**
25043     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
25044      * loaded or when a record is removed. (defaults to false).
25045     */
25046     pruneModifiedRecords : false,
25047
25048     // private
25049     lastOptions : null,
25050
25051     /**
25052      * Add Records to the Store and fires the add event.
25053      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
25054      */
25055     add : function(records){
25056         records = [].concat(records);
25057         for(var i = 0, len = records.length; i < len; i++){
25058             records[i].join(this);
25059         }
25060         var index = this.data.length;
25061         this.data.addAll(records);
25062         this.fireEvent("add", this, records, index);
25063     },
25064
25065     /**
25066      * Remove a Record from the Store and fires the remove event.
25067      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
25068      */
25069     remove : function(record){
25070         var index = this.data.indexOf(record);
25071         this.data.removeAt(index);
25072  
25073         if(this.pruneModifiedRecords){
25074             this.modified.remove(record);
25075         }
25076         this.fireEvent("remove", this, record, index);
25077     },
25078
25079     /**
25080      * Remove all Records from the Store and fires the clear event.
25081      */
25082     removeAll : function(){
25083         this.data.clear();
25084         if(this.pruneModifiedRecords){
25085             this.modified = [];
25086         }
25087         this.fireEvent("clear", this);
25088     },
25089
25090     /**
25091      * Inserts Records to the Store at the given index and fires the add event.
25092      * @param {Number} index The start index at which to insert the passed Records.
25093      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
25094      */
25095     insert : function(index, records){
25096         records = [].concat(records);
25097         for(var i = 0, len = records.length; i < len; i++){
25098             this.data.insert(index, records[i]);
25099             records[i].join(this);
25100         }
25101         this.fireEvent("add", this, records, index);
25102     },
25103
25104     /**
25105      * Get the index within the cache of the passed Record.
25106      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
25107      * @return {Number} The index of the passed Record. Returns -1 if not found.
25108      */
25109     indexOf : function(record){
25110         return this.data.indexOf(record);
25111     },
25112
25113     /**
25114      * Get the index within the cache of the Record with the passed id.
25115      * @param {String} id The id of the Record to find.
25116      * @return {Number} The index of the Record. Returns -1 if not found.
25117      */
25118     indexOfId : function(id){
25119         return this.data.indexOfKey(id);
25120     },
25121
25122     /**
25123      * Get the Record with the specified id.
25124      * @param {String} id The id of the Record to find.
25125      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
25126      */
25127     getById : function(id){
25128         return this.data.key(id);
25129     },
25130
25131     /**
25132      * Get the Record at the specified index.
25133      * @param {Number} index The index of the Record to find.
25134      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
25135      */
25136     getAt : function(index){
25137         return this.data.itemAt(index);
25138     },
25139
25140     /**
25141      * Returns a range of Records between specified indices.
25142      * @param {Number} startIndex (optional) The starting index (defaults to 0)
25143      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
25144      * @return {Roo.data.Record[]} An array of Records
25145      */
25146     getRange : function(start, end){
25147         return this.data.getRange(start, end);
25148     },
25149
25150     // private
25151     storeOptions : function(o){
25152         o = Roo.apply({}, o);
25153         delete o.callback;
25154         delete o.scope;
25155         this.lastOptions = o;
25156     },
25157
25158     /**
25159      * Loads the Record cache from the configured Proxy using the configured Reader.
25160      * <p>
25161      * If using remote paging, then the first load call must specify the <em>start</em>
25162      * and <em>limit</em> properties in the options.params property to establish the initial
25163      * position within the dataset, and the number of Records to cache on each read from the Proxy.
25164      * <p>
25165      * <strong>It is important to note that for remote data sources, loading is asynchronous,
25166      * and this call will return before the new data has been loaded. Perform any post-processing
25167      * in a callback function, or in a "load" event handler.</strong>
25168      * <p>
25169      * @param {Object} options An object containing properties which control loading options:<ul>
25170      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
25171      * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
25172      * <pre>
25173                 {
25174                     data : data,  // array of key=>value data like JsonReader
25175                     total : data.length,
25176                     success : true
25177                     
25178                 }
25179         </pre>
25180             }.</li>
25181      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
25182      * passed the following arguments:<ul>
25183      * <li>r : Roo.data.Record[]</li>
25184      * <li>options: Options object from the load call</li>
25185      * <li>success: Boolean success indicator</li></ul></li>
25186      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
25187      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
25188      * </ul>
25189      */
25190     load : function(options){
25191         options = options || {};
25192         if(this.fireEvent("beforeload", this, options) !== false){
25193             this.storeOptions(options);
25194             var p = Roo.apply(options.params || {}, this.baseParams);
25195             // if meta was not loaded from remote source.. try requesting it.
25196             if (!this.reader.metaFromRemote) {
25197                 p._requestMeta = 1;
25198             }
25199             if(this.sortInfo && this.remoteSort){
25200                 var pn = this.paramNames;
25201                 p[pn["sort"]] = this.sortInfo.field;
25202                 p[pn["dir"]] = this.sortInfo.direction;
25203             }
25204             if (this.multiSort) {
25205                 var pn = this.paramNames;
25206                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
25207             }
25208             
25209             this.proxy.load(p, this.reader, this.loadRecords, this, options);
25210         }
25211     },
25212
25213     /**
25214      * Reloads the Record cache from the configured Proxy using the configured Reader and
25215      * the options from the last load operation performed.
25216      * @param {Object} options (optional) An object containing properties which may override the options
25217      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
25218      * the most recently used options are reused).
25219      */
25220     reload : function(options){
25221         this.load(Roo.applyIf(options||{}, this.lastOptions));
25222     },
25223
25224     // private
25225     // Called as a callback by the Reader during a load operation.
25226     loadRecords : function(o, options, success){
25227          
25228         if(!o){
25229             if(success !== false){
25230                 this.fireEvent("load", this, [], options, o);
25231             }
25232             if(options.callback){
25233                 options.callback.call(options.scope || this, [], options, false);
25234             }
25235             return;
25236         }
25237         // if data returned failure - throw an exception.
25238         if (o.success === false) {
25239             // show a message if no listener is registered.
25240             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
25241                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
25242             }
25243             // loadmask wil be hooked into this..
25244             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
25245             return;
25246         }
25247         var r = o.records, t = o.totalRecords || r.length;
25248         
25249         this.fireEvent("beforeloadadd", this, r, options, o);
25250         
25251         if(!options || options.add !== true){
25252             if(this.pruneModifiedRecords){
25253                 this.modified = [];
25254             }
25255             for(var i = 0, len = r.length; i < len; i++){
25256                 r[i].join(this);
25257             }
25258             if(this.snapshot){
25259                 this.data = this.snapshot;
25260                 delete this.snapshot;
25261             }
25262             this.data.clear();
25263             this.data.addAll(r);
25264             this.totalLength = t;
25265             this.applySort();
25266             this.fireEvent("datachanged", this);
25267         }else{
25268             this.totalLength = Math.max(t, this.data.length+r.length);
25269             this.add(r);
25270         }
25271         
25272         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
25273                 
25274             var e = new Roo.data.Record({});
25275
25276             e.set(this.parent.displayField, this.parent.emptyTitle);
25277             e.set(this.parent.valueField, '');
25278
25279             this.insert(0, e);
25280         }
25281             
25282         this.fireEvent("load", this, r, options, o);
25283         if(options.callback){
25284             options.callback.call(options.scope || this, r, options, true);
25285         }
25286     },
25287
25288
25289     /**
25290      * Loads data from a passed data block. A Reader which understands the format of the data
25291      * must have been configured in the constructor.
25292      * @param {Object} data The data block from which to read the Records.  The format of the data expected
25293      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
25294      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
25295      */
25296     loadData : function(o, append){
25297         var r = this.reader.readRecords(o);
25298         this.loadRecords(r, {add: append}, true);
25299     },
25300     
25301      /**
25302      * using 'cn' the nested child reader read the child array into it's child stores.
25303      * @param {Object} rec The record with a 'children array
25304      */
25305     loadDataFromChildren : function(rec)
25306     {
25307         this.loadData(this.reader.toLoadData(rec));
25308     },
25309     
25310
25311     /**
25312      * Gets the number of cached records.
25313      * <p>
25314      * <em>If using paging, this may not be the total size of the dataset. If the data object
25315      * used by the Reader contains the dataset size, then the getTotalCount() function returns
25316      * the data set size</em>
25317      */
25318     getCount : function(){
25319         return this.data.length || 0;
25320     },
25321
25322     /**
25323      * Gets the total number of records in the dataset as returned by the server.
25324      * <p>
25325      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
25326      * the dataset size</em>
25327      */
25328     getTotalCount : function(){
25329         return this.totalLength || 0;
25330     },
25331
25332     /**
25333      * Returns the sort state of the Store as an object with two properties:
25334      * <pre><code>
25335  field {String} The name of the field by which the Records are sorted
25336  direction {String} The sort order, "ASC" or "DESC"
25337      * </code></pre>
25338      */
25339     getSortState : function(){
25340         return this.sortInfo;
25341     },
25342
25343     // private
25344     applySort : function(){
25345         if(this.sortInfo && !this.remoteSort){
25346             var s = this.sortInfo, f = s.field;
25347             var st = this.fields.get(f).sortType;
25348             var fn = function(r1, r2){
25349                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
25350                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
25351             };
25352             this.data.sort(s.direction, fn);
25353             if(this.snapshot && this.snapshot != this.data){
25354                 this.snapshot.sort(s.direction, fn);
25355             }
25356         }
25357     },
25358
25359     /**
25360      * Sets the default sort column and order to be used by the next load operation.
25361      * @param {String} fieldName The name of the field to sort by.
25362      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
25363      */
25364     setDefaultSort : function(field, dir){
25365         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
25366     },
25367
25368     /**
25369      * Sort the Records.
25370      * If remote sorting is used, the sort is performed on the server, and the cache is
25371      * reloaded. If local sorting is used, the cache is sorted internally.
25372      * @param {String} fieldName The name of the field to sort by.
25373      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
25374      */
25375     sort : function(fieldName, dir){
25376         var f = this.fields.get(fieldName);
25377         if(!dir){
25378             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
25379             
25380             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
25381                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
25382             }else{
25383                 dir = f.sortDir;
25384             }
25385         }
25386         this.sortToggle[f.name] = dir;
25387         this.sortInfo = {field: f.name, direction: dir};
25388         if(!this.remoteSort){
25389             this.applySort();
25390             this.fireEvent("datachanged", this);
25391         }else{
25392             this.load(this.lastOptions);
25393         }
25394     },
25395
25396     /**
25397      * Calls the specified function for each of the Records in the cache.
25398      * @param {Function} fn The function to call. The Record is passed as the first parameter.
25399      * Returning <em>false</em> aborts and exits the iteration.
25400      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
25401      */
25402     each : function(fn, scope){
25403         this.data.each(fn, scope);
25404     },
25405
25406     /**
25407      * Gets all records modified since the last commit.  Modified records are persisted across load operations
25408      * (e.g., during paging).
25409      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
25410      */
25411     getModifiedRecords : function(){
25412         return this.modified;
25413     },
25414
25415     // private
25416     createFilterFn : function(property, value, anyMatch){
25417         if(!value.exec){ // not a regex
25418             value = String(value);
25419             if(value.length == 0){
25420                 return false;
25421             }
25422             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
25423         }
25424         return function(r){
25425             return value.test(r.data[property]);
25426         };
25427     },
25428
25429     /**
25430      * Sums the value of <i>property</i> for each record between start and end and returns the result.
25431      * @param {String} property A field on your records
25432      * @param {Number} start The record index to start at (defaults to 0)
25433      * @param {Number} end The last record index to include (defaults to length - 1)
25434      * @return {Number} The sum
25435      */
25436     sum : function(property, start, end){
25437         var rs = this.data.items, v = 0;
25438         start = start || 0;
25439         end = (end || end === 0) ? end : rs.length-1;
25440
25441         for(var i = start; i <= end; i++){
25442             v += (rs[i].data[property] || 0);
25443         }
25444         return v;
25445     },
25446
25447     /**
25448      * Filter the records by a specified property.
25449      * @param {String} field A field on your records
25450      * @param {String/RegExp} value Either a string that the field
25451      * should start with or a RegExp to test against the field
25452      * @param {Boolean} anyMatch True to match any part not just the beginning
25453      */
25454     filter : function(property, value, anyMatch){
25455         var fn = this.createFilterFn(property, value, anyMatch);
25456         return fn ? this.filterBy(fn) : this.clearFilter();
25457     },
25458
25459     /**
25460      * Filter by a function. The specified function will be called with each
25461      * record in this data source. If the function returns true the record is included,
25462      * otherwise it is filtered.
25463      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
25464      * @param {Object} scope (optional) The scope of the function (defaults to this)
25465      */
25466     filterBy : function(fn, scope){
25467         this.snapshot = this.snapshot || this.data;
25468         this.data = this.queryBy(fn, scope||this);
25469         this.fireEvent("datachanged", this);
25470     },
25471
25472     /**
25473      * Query the records by a specified property.
25474      * @param {String} field A field on your records
25475      * @param {String/RegExp} value Either a string that the field
25476      * should start with or a RegExp to test against the field
25477      * @param {Boolean} anyMatch True to match any part not just the beginning
25478      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
25479      */
25480     query : function(property, value, anyMatch){
25481         var fn = this.createFilterFn(property, value, anyMatch);
25482         return fn ? this.queryBy(fn) : this.data.clone();
25483     },
25484
25485     /**
25486      * Query by a function. The specified function will be called with each
25487      * record in this data source. If the function returns true the record is included
25488      * in the results.
25489      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
25490      * @param {Object} scope (optional) The scope of the function (defaults to this)
25491       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
25492      **/
25493     queryBy : function(fn, scope){
25494         var data = this.snapshot || this.data;
25495         return data.filterBy(fn, scope||this);
25496     },
25497
25498     /**
25499      * Collects unique values for a particular dataIndex from this store.
25500      * @param {String} dataIndex The property to collect
25501      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
25502      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
25503      * @return {Array} An array of the unique values
25504      **/
25505     collect : function(dataIndex, allowNull, bypassFilter){
25506         var d = (bypassFilter === true && this.snapshot) ?
25507                 this.snapshot.items : this.data.items;
25508         var v, sv, r = [], l = {};
25509         for(var i = 0, len = d.length; i < len; i++){
25510             v = d[i].data[dataIndex];
25511             sv = String(v);
25512             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
25513                 l[sv] = true;
25514                 r[r.length] = v;
25515             }
25516         }
25517         return r;
25518     },
25519
25520     /**
25521      * Revert to a view of the Record cache with no filtering applied.
25522      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
25523      */
25524     clearFilter : function(suppressEvent){
25525         if(this.snapshot && this.snapshot != this.data){
25526             this.data = this.snapshot;
25527             delete this.snapshot;
25528             if(suppressEvent !== true){
25529                 this.fireEvent("datachanged", this);
25530             }
25531         }
25532     },
25533
25534     // private
25535     afterEdit : function(record){
25536         if(this.modified.indexOf(record) == -1){
25537             this.modified.push(record);
25538         }
25539         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
25540     },
25541     
25542     // private
25543     afterReject : function(record){
25544         this.modified.remove(record);
25545         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
25546     },
25547
25548     // private
25549     afterCommit : function(record){
25550         this.modified.remove(record);
25551         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
25552     },
25553
25554     /**
25555      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
25556      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
25557      */
25558     commitChanges : function(){
25559         var m = this.modified.slice(0);
25560         this.modified = [];
25561         for(var i = 0, len = m.length; i < len; i++){
25562             m[i].commit();
25563         }
25564     },
25565
25566     /**
25567      * Cancel outstanding changes on all changed records.
25568      */
25569     rejectChanges : function(){
25570         var m = this.modified.slice(0);
25571         this.modified = [];
25572         for(var i = 0, len = m.length; i < len; i++){
25573             m[i].reject();
25574         }
25575     },
25576
25577     onMetaChange : function(meta, rtype, o){
25578         this.recordType = rtype;
25579         this.fields = rtype.prototype.fields;
25580         delete this.snapshot;
25581         this.sortInfo = meta.sortInfo || this.sortInfo;
25582         this.modified = [];
25583         this.fireEvent('metachange', this, this.reader.meta);
25584     },
25585     
25586     moveIndex : function(data, type)
25587     {
25588         var index = this.indexOf(data);
25589         
25590         var newIndex = index + type;
25591         
25592         this.remove(data);
25593         
25594         this.insert(newIndex, data);
25595         
25596     }
25597 });/*
25598  * Based on:
25599  * Ext JS Library 1.1.1
25600  * Copyright(c) 2006-2007, Ext JS, LLC.
25601  *
25602  * Originally Released Under LGPL - original licence link has changed is not relivant.
25603  *
25604  * Fork - LGPL
25605  * <script type="text/javascript">
25606  */
25607
25608 /**
25609  * @class Roo.data.SimpleStore
25610  * @extends Roo.data.Store
25611  * Small helper class to make creating Stores from Array data easier.
25612  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
25613  * @cfg {Array} fields An array of field definition objects, or field name strings.
25614  * @cfg {Object} an existing reader (eg. copied from another store)
25615  * @cfg {Array} data The multi-dimensional array of data
25616  * @cfg {Roo.data.DataProxy} proxy [not-required]  
25617  * @cfg {Roo.data.Reader} reader  [not-required] 
25618  * @constructor
25619  * @param {Object} config
25620  */
25621 Roo.data.SimpleStore = function(config)
25622 {
25623     Roo.data.SimpleStore.superclass.constructor.call(this, {
25624         isLocal : true,
25625         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
25626                 id: config.id
25627             },
25628             Roo.data.Record.create(config.fields)
25629         ),
25630         proxy : new Roo.data.MemoryProxy(config.data)
25631     });
25632     this.load();
25633 };
25634 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
25635  * Based on:
25636  * Ext JS Library 1.1.1
25637  * Copyright(c) 2006-2007, Ext JS, LLC.
25638  *
25639  * Originally Released Under LGPL - original licence link has changed is not relivant.
25640  *
25641  * Fork - LGPL
25642  * <script type="text/javascript">
25643  */
25644
25645 /**
25646 /**
25647  * @extends Roo.data.Store
25648  * @class Roo.data.JsonStore
25649  * Small helper class to make creating Stores for JSON data easier. <br/>
25650 <pre><code>
25651 var store = new Roo.data.JsonStore({
25652     url: 'get-images.php',
25653     root: 'images',
25654     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
25655 });
25656 </code></pre>
25657  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
25658  * JsonReader and HttpProxy (unless inline data is provided).</b>
25659  * @cfg {Array} fields An array of field definition objects, or field name strings.
25660  * @constructor
25661  * @param {Object} config
25662  */
25663 Roo.data.JsonStore = function(c){
25664     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
25665         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
25666         reader: new Roo.data.JsonReader(c, c.fields)
25667     }));
25668 };
25669 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
25670  * Based on:
25671  * Ext JS Library 1.1.1
25672  * Copyright(c) 2006-2007, Ext JS, LLC.
25673  *
25674  * Originally Released Under LGPL - original licence link has changed is not relivant.
25675  *
25676  * Fork - LGPL
25677  * <script type="text/javascript">
25678  */
25679
25680  
25681 Roo.data.Field = function(config){
25682     if(typeof config == "string"){
25683         config = {name: config};
25684     }
25685     Roo.apply(this, config);
25686     
25687     if(!this.type){
25688         this.type = "auto";
25689     }
25690     
25691     var st = Roo.data.SortTypes;
25692     // named sortTypes are supported, here we look them up
25693     if(typeof this.sortType == "string"){
25694         this.sortType = st[this.sortType];
25695     }
25696     
25697     // set default sortType for strings and dates
25698     if(!this.sortType){
25699         switch(this.type){
25700             case "string":
25701                 this.sortType = st.asUCString;
25702                 break;
25703             case "date":
25704                 this.sortType = st.asDate;
25705                 break;
25706             default:
25707                 this.sortType = st.none;
25708         }
25709     }
25710
25711     // define once
25712     var stripRe = /[\$,%]/g;
25713
25714     // prebuilt conversion function for this field, instead of
25715     // switching every time we're reading a value
25716     if(!this.convert){
25717         var cv, dateFormat = this.dateFormat;
25718         switch(this.type){
25719             case "":
25720             case "auto":
25721             case undefined:
25722                 cv = function(v){ return v; };
25723                 break;
25724             case "string":
25725                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
25726                 break;
25727             case "int":
25728                 cv = function(v){
25729                     return v !== undefined && v !== null && v !== '' ?
25730                            parseInt(String(v).replace(stripRe, ""), 10) : '';
25731                     };
25732                 break;
25733             case "float":
25734                 cv = function(v){
25735                     return v !== undefined && v !== null && v !== '' ?
25736                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
25737                     };
25738                 break;
25739             case "bool":
25740             case "boolean":
25741                 cv = function(v){ return v === true || v === "true" || v == 1; };
25742                 break;
25743             case "date":
25744                 cv = function(v){
25745                     if(!v){
25746                         return '';
25747                     }
25748                     if(v instanceof Date){
25749                         return v;
25750                     }
25751                     if(dateFormat){
25752                         if(dateFormat == "timestamp"){
25753                             return new Date(v*1000);
25754                         }
25755                         return Date.parseDate(v, dateFormat);
25756                     }
25757                     var parsed = Date.parse(v);
25758                     return parsed ? new Date(parsed) : null;
25759                 };
25760              break;
25761             
25762         }
25763         this.convert = cv;
25764     }
25765 };
25766
25767 Roo.data.Field.prototype = {
25768     dateFormat: null,
25769     defaultValue: "",
25770     mapping: null,
25771     sortType : null,
25772     sortDir : "ASC"
25773 };/*
25774  * Based on:
25775  * Ext JS Library 1.1.1
25776  * Copyright(c) 2006-2007, Ext JS, LLC.
25777  *
25778  * Originally Released Under LGPL - original licence link has changed is not relivant.
25779  *
25780  * Fork - LGPL
25781  * <script type="text/javascript">
25782  */
25783  
25784 // Base class for reading structured data from a data source.  This class is intended to be
25785 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
25786
25787 /**
25788  * @class Roo.data.DataReader
25789  * @abstract
25790  * Base class for reading structured data from a data source.  This class is intended to be
25791  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
25792  */
25793
25794 Roo.data.DataReader = function(meta, recordType){
25795     
25796     this.meta = meta;
25797     
25798     this.recordType = recordType instanceof Array ? 
25799         Roo.data.Record.create(recordType) : recordType;
25800 };
25801
25802 Roo.data.DataReader.prototype = {
25803     
25804     
25805     readerType : 'Data',
25806      /**
25807      * Create an empty record
25808      * @param {Object} data (optional) - overlay some values
25809      * @return {Roo.data.Record} record created.
25810      */
25811     newRow :  function(d) {
25812         var da =  {};
25813         this.recordType.prototype.fields.each(function(c) {
25814             switch( c.type) {
25815                 case 'int' : da[c.name] = 0; break;
25816                 case 'date' : da[c.name] = new Date(); break;
25817                 case 'float' : da[c.name] = 0.0; break;
25818                 case 'boolean' : da[c.name] = false; break;
25819                 default : da[c.name] = ""; break;
25820             }
25821             
25822         });
25823         return new this.recordType(Roo.apply(da, d));
25824     }
25825     
25826     
25827 };/*
25828  * Based on:
25829  * Ext JS Library 1.1.1
25830  * Copyright(c) 2006-2007, Ext JS, LLC.
25831  *
25832  * Originally Released Under LGPL - original licence link has changed is not relivant.
25833  *
25834  * Fork - LGPL
25835  * <script type="text/javascript">
25836  */
25837
25838 /**
25839  * @class Roo.data.DataProxy
25840  * @extends Roo.util.Observable
25841  * @abstract
25842  * This class is an abstract base class for implementations which provide retrieval of
25843  * unformatted data objects.<br>
25844  * <p>
25845  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
25846  * (of the appropriate type which knows how to parse the data object) to provide a block of
25847  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
25848  * <p>
25849  * Custom implementations must implement the load method as described in
25850  * {@link Roo.data.HttpProxy#load}.
25851  */
25852 Roo.data.DataProxy = function(){
25853     this.addEvents({
25854         /**
25855          * @event beforeload
25856          * Fires before a network request is made to retrieve a data object.
25857          * @param {Object} This DataProxy object.
25858          * @param {Object} params The params parameter to the load function.
25859          */
25860         beforeload : true,
25861         /**
25862          * @event load
25863          * Fires before the load method's callback is called.
25864          * @param {Object} This DataProxy object.
25865          * @param {Object} o The data object.
25866          * @param {Object} arg The callback argument object passed to the load function.
25867          */
25868         load : true,
25869         /**
25870          * @event loadexception
25871          * Fires if an Exception occurs during data retrieval.
25872          * @param {Object} This DataProxy object.
25873          * @param {Object} o The data object.
25874          * @param {Object} arg The callback argument object passed to the load function.
25875          * @param {Object} e The Exception.
25876          */
25877         loadexception : true
25878     });
25879     Roo.data.DataProxy.superclass.constructor.call(this);
25880 };
25881
25882 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
25883
25884     /**
25885      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
25886      */
25887 /*
25888  * Based on:
25889  * Ext JS Library 1.1.1
25890  * Copyright(c) 2006-2007, Ext JS, LLC.
25891  *
25892  * Originally Released Under LGPL - original licence link has changed is not relivant.
25893  *
25894  * Fork - LGPL
25895  * <script type="text/javascript">
25896  */
25897 /**
25898  * @class Roo.data.MemoryProxy
25899  * @extends Roo.data.DataProxy
25900  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
25901  * to the Reader when its load method is called.
25902  * @constructor
25903  * @param {Object} config  A config object containing the objects needed for the Store to access data,
25904  */
25905 Roo.data.MemoryProxy = function(config){
25906     var data = config;
25907     if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
25908         data = config.data;
25909     }
25910     Roo.data.MemoryProxy.superclass.constructor.call(this);
25911     this.data = data;
25912 };
25913
25914 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
25915     
25916     /**
25917      *  @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
25918      */
25919     /**
25920      * Load data from the requested source (in this case an in-memory
25921      * data object passed to the constructor), read the data object into
25922      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25923      * process that block using the passed callback.
25924      * @param {Object} params This parameter is not used by the MemoryProxy class.
25925      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25926      * object into a block of Roo.data.Records.
25927      * @param {Function} callback The function into which to pass the block of Roo.data.records.
25928      * The function must be passed <ul>
25929      * <li>The Record block object</li>
25930      * <li>The "arg" argument from the load function</li>
25931      * <li>A boolean success indicator</li>
25932      * </ul>
25933      * @param {Object} scope The scope in which to call the callback
25934      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25935      */
25936     load : function(params, reader, callback, scope, arg){
25937         params = params || {};
25938         var result;
25939         try {
25940             result = reader.readRecords(params.data ? params.data :this.data);
25941         }catch(e){
25942             this.fireEvent("loadexception", this, arg, null, e);
25943             callback.call(scope, null, arg, false);
25944             return;
25945         }
25946         callback.call(scope, result, arg, true);
25947     },
25948     
25949     // private
25950     update : function(params, records){
25951         
25952     }
25953 });/*
25954  * Based on:
25955  * Ext JS Library 1.1.1
25956  * Copyright(c) 2006-2007, Ext JS, LLC.
25957  *
25958  * Originally Released Under LGPL - original licence link has changed is not relivant.
25959  *
25960  * Fork - LGPL
25961  * <script type="text/javascript">
25962  */
25963 /**
25964  * @class Roo.data.HttpProxy
25965  * @extends Roo.data.DataProxy
25966  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
25967  * configured to reference a certain URL.<br><br>
25968  * <p>
25969  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
25970  * from which the running page was served.<br><br>
25971  * <p>
25972  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
25973  * <p>
25974  * Be aware that to enable the browser to parse an XML document, the server must set
25975  * the Content-Type header in the HTTP response to "text/xml".
25976  * @constructor
25977  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
25978  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
25979  * will be used to make the request.
25980  */
25981 Roo.data.HttpProxy = function(conn){
25982     Roo.data.HttpProxy.superclass.constructor.call(this);
25983     // is conn a conn config or a real conn?
25984     this.conn = conn;
25985     this.useAjax = !conn || !conn.events;
25986   
25987 };
25988
25989 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
25990     // thse are take from connection...
25991     
25992     /**
25993      * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
25994      */
25995     /**
25996      * @cfg {Object} extraParams  An object containing properties which are used as
25997      * extra parameters to each request made by this object. (defaults to undefined)
25998      */
25999     /**
26000      * @cfg {Object} defaultHeaders   An object containing request headers which are added
26001      *  to each request made by this object. (defaults to undefined)
26002      */
26003     /**
26004      * @cfg {String} method (GET|POST)  The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
26005      */
26006     /**
26007      * @cfg {Number} timeout The timeout in milliseconds to be used for requests. (defaults to 30000)
26008      */
26009      /**
26010      * @cfg {Boolean} autoAbort Whether this request should abort any pending requests. (defaults to false)
26011      * @type Boolean
26012      */
26013   
26014
26015     /**
26016      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
26017      * @type Boolean
26018      */
26019     /**
26020      * Return the {@link Roo.data.Connection} object being used by this Proxy.
26021      * @return {Connection} The Connection object. This object may be used to subscribe to events on
26022      * a finer-grained basis than the DataProxy events.
26023      */
26024     getConnection : function(){
26025         return this.useAjax ? Roo.Ajax : this.conn;
26026     },
26027
26028     /**
26029      * Load data from the configured {@link Roo.data.Connection}, read the data object into
26030      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
26031      * process that block using the passed callback.
26032      * @param {Object} params An object containing properties which are to be used as HTTP parameters
26033      * for the request to the remote server.
26034      * @param {Roo.data.DataReader} reader The Reader object which converts the data
26035      * object into a block of Roo.data.Records.
26036      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
26037      * The function must be passed <ul>
26038      * <li>The Record block object</li>
26039      * <li>The "arg" argument from the load function</li>
26040      * <li>A boolean success indicator</li>
26041      * </ul>
26042      * @param {Object} scope The scope in which to call the callback
26043      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
26044      */
26045     load : function(params, reader, callback, scope, arg){
26046         if(this.fireEvent("beforeload", this, params) !== false){
26047             var  o = {
26048                 params : params || {},
26049                 request: {
26050                     callback : callback,
26051                     scope : scope,
26052                     arg : arg
26053                 },
26054                 reader: reader,
26055                 callback : this.loadResponse,
26056                 scope: this
26057             };
26058             if(this.useAjax){
26059                 Roo.applyIf(o, this.conn);
26060                 if(this.activeRequest){
26061                     Roo.Ajax.abort(this.activeRequest);
26062                 }
26063                 this.activeRequest = Roo.Ajax.request(o);
26064             }else{
26065                 this.conn.request(o);
26066             }
26067         }else{
26068             callback.call(scope||this, null, arg, false);
26069         }
26070     },
26071
26072     // private
26073     loadResponse : function(o, success, response){
26074         delete this.activeRequest;
26075         if(!success){
26076             this.fireEvent("loadexception", this, o, response);
26077             o.request.callback.call(o.request.scope, null, o.request.arg, false);
26078             return;
26079         }
26080         var result;
26081         try {
26082             result = o.reader.read(response);
26083         }catch(e){
26084             o.success = false;
26085             o.raw = { errorMsg : response.responseText };
26086             this.fireEvent("loadexception", this, o, response, e);
26087             o.request.callback.call(o.request.scope, o, o.request.arg, false);
26088             return;
26089         }
26090         
26091         this.fireEvent("load", this, o, o.request.arg);
26092         o.request.callback.call(o.request.scope, result, o.request.arg, true);
26093     },
26094
26095     // private
26096     update : function(dataSet){
26097
26098     },
26099
26100     // private
26101     updateResponse : function(dataSet){
26102
26103     }
26104 });/*
26105  * Based on:
26106  * Ext JS Library 1.1.1
26107  * Copyright(c) 2006-2007, Ext JS, LLC.
26108  *
26109  * Originally Released Under LGPL - original licence link has changed is not relivant.
26110  *
26111  * Fork - LGPL
26112  * <script type="text/javascript">
26113  */
26114
26115 /**
26116  * @class Roo.data.ScriptTagProxy
26117  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
26118  * other than the originating domain of the running page.<br><br>
26119  * <p>
26120  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
26121  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
26122  * <p>
26123  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
26124  * source code that is used as the source inside a &lt;script> tag.<br><br>
26125  * <p>
26126  * In order for the browser to process the returned data, the server must wrap the data object
26127  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
26128  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
26129  * depending on whether the callback name was passed:
26130  * <p>
26131  * <pre><code>
26132 boolean scriptTag = false;
26133 String cb = request.getParameter("callback");
26134 if (cb != null) {
26135     scriptTag = true;
26136     response.setContentType("text/javascript");
26137 } else {
26138     response.setContentType("application/x-json");
26139 }
26140 Writer out = response.getWriter();
26141 if (scriptTag) {
26142     out.write(cb + "(");
26143 }
26144 out.print(dataBlock.toJsonString());
26145 if (scriptTag) {
26146     out.write(");");
26147 }
26148 </pre></code>
26149  *
26150  * @constructor
26151  * @param {Object} config A configuration object.
26152  */
26153 Roo.data.ScriptTagProxy = function(config){
26154     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
26155     Roo.apply(this, config);
26156     this.head = document.getElementsByTagName("head")[0];
26157 };
26158
26159 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
26160
26161 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
26162     /**
26163      * @cfg {String} url The URL from which to request the data object.
26164      */
26165     /**
26166      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
26167      */
26168     timeout : 30000,
26169     /**
26170      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
26171      * the server the name of the callback function set up by the load call to process the returned data object.
26172      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
26173      * javascript output which calls this named function passing the data object as its only parameter.
26174      */
26175     callbackParam : "callback",
26176     /**
26177      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
26178      * name to the request.
26179      */
26180     nocache : true,
26181
26182     /**
26183      * Load data from the configured URL, read the data object into
26184      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
26185      * process that block using the passed callback.
26186      * @param {Object} params An object containing properties which are to be used as HTTP parameters
26187      * for the request to the remote server.
26188      * @param {Roo.data.DataReader} reader The Reader object which converts the data
26189      * object into a block of Roo.data.Records.
26190      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
26191      * The function must be passed <ul>
26192      * <li>The Record block object</li>
26193      * <li>The "arg" argument from the load function</li>
26194      * <li>A boolean success indicator</li>
26195      * </ul>
26196      * @param {Object} scope The scope in which to call the callback
26197      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
26198      */
26199     load : function(params, reader, callback, scope, arg){
26200         if(this.fireEvent("beforeload", this, params) !== false){
26201
26202             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
26203
26204             var url = this.url;
26205             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
26206             if(this.nocache){
26207                 url += "&_dc=" + (new Date().getTime());
26208             }
26209             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
26210             var trans = {
26211                 id : transId,
26212                 cb : "stcCallback"+transId,
26213                 scriptId : "stcScript"+transId,
26214                 params : params,
26215                 arg : arg,
26216                 url : url,
26217                 callback : callback,
26218                 scope : scope,
26219                 reader : reader
26220             };
26221             var conn = this;
26222
26223             window[trans.cb] = function(o){
26224                 conn.handleResponse(o, trans);
26225             };
26226
26227             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
26228
26229             if(this.autoAbort !== false){
26230                 this.abort();
26231             }
26232
26233             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
26234
26235             var script = document.createElement("script");
26236             script.setAttribute("src", url);
26237             script.setAttribute("type", "text/javascript");
26238             script.setAttribute("id", trans.scriptId);
26239             this.head.appendChild(script);
26240
26241             this.trans = trans;
26242         }else{
26243             callback.call(scope||this, null, arg, false);
26244         }
26245     },
26246
26247     // private
26248     isLoading : function(){
26249         return this.trans ? true : false;
26250     },
26251
26252     /**
26253      * Abort the current server request.
26254      */
26255     abort : function(){
26256         if(this.isLoading()){
26257             this.destroyTrans(this.trans);
26258         }
26259     },
26260
26261     // private
26262     destroyTrans : function(trans, isLoaded){
26263         this.head.removeChild(document.getElementById(trans.scriptId));
26264         clearTimeout(trans.timeoutId);
26265         if(isLoaded){
26266             window[trans.cb] = undefined;
26267             try{
26268                 delete window[trans.cb];
26269             }catch(e){}
26270         }else{
26271             // if hasn't been loaded, wait for load to remove it to prevent script error
26272             window[trans.cb] = function(){
26273                 window[trans.cb] = undefined;
26274                 try{
26275                     delete window[trans.cb];
26276                 }catch(e){}
26277             };
26278         }
26279     },
26280
26281     // private
26282     handleResponse : function(o, trans){
26283         this.trans = false;
26284         this.destroyTrans(trans, true);
26285         var result;
26286         try {
26287             result = trans.reader.readRecords(o);
26288         }catch(e){
26289             this.fireEvent("loadexception", this, o, trans.arg, e);
26290             trans.callback.call(trans.scope||window, null, trans.arg, false);
26291             return;
26292         }
26293         this.fireEvent("load", this, o, trans.arg);
26294         trans.callback.call(trans.scope||window, result, trans.arg, true);
26295     },
26296
26297     // private
26298     handleFailure : function(trans){
26299         this.trans = false;
26300         this.destroyTrans(trans, false);
26301         this.fireEvent("loadexception", this, null, trans.arg);
26302         trans.callback.call(trans.scope||window, null, trans.arg, false);
26303     }
26304 });/*
26305  * Based on:
26306  * Ext JS Library 1.1.1
26307  * Copyright(c) 2006-2007, Ext JS, LLC.
26308  *
26309  * Originally Released Under LGPL - original licence link has changed is not relivant.
26310  *
26311  * Fork - LGPL
26312  * <script type="text/javascript">
26313  */
26314
26315 /**
26316  * @class Roo.data.JsonReader
26317  * @extends Roo.data.DataReader
26318  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
26319  * based on mappings in a provided Roo.data.Record constructor.
26320  * 
26321  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
26322  * in the reply previously. 
26323  * 
26324  * <p>
26325  * Example code:
26326  * <pre><code>
26327 var RecordDef = Roo.data.Record.create([
26328     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
26329     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
26330 ]);
26331 var myReader = new Roo.data.JsonReader({
26332     totalProperty: "results",    // The property which contains the total dataset size (optional)
26333     root: "rows",                // The property which contains an Array of row objects
26334     id: "id"                     // The property within each row object that provides an ID for the record (optional)
26335 }, RecordDef);
26336 </code></pre>
26337  * <p>
26338  * This would consume a JSON file like this:
26339  * <pre><code>
26340 { 'results': 2, 'rows': [
26341     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
26342     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
26343 }
26344 </code></pre>
26345  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
26346  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
26347  * paged from the remote server.
26348  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
26349  * @cfg {String} root name of the property which contains the Array of row objects.
26350  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26351  * @cfg {Array} fields Array of field definition objects
26352  * @constructor
26353  * Create a new JsonReader
26354  * @param {Object} meta Metadata configuration options
26355  * @param {Object} recordType Either an Array of field definition objects,
26356  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
26357  */
26358 Roo.data.JsonReader = function(meta, recordType){
26359     
26360     meta = meta || {};
26361     // set some defaults:
26362     Roo.applyIf(meta, {
26363         totalProperty: 'total',
26364         successProperty : 'success',
26365         root : 'data',
26366         id : 'id'
26367     });
26368     
26369     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26370 };
26371 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
26372     
26373     readerType : 'Json',
26374     
26375     /**
26376      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
26377      * Used by Store query builder to append _requestMeta to params.
26378      * 
26379      */
26380     metaFromRemote : false,
26381     /**
26382      * This method is only used by a DataProxy which has retrieved data from a remote server.
26383      * @param {Object} response The XHR object which contains the JSON data in its responseText.
26384      * @return {Object} data A data block which is used by an Roo.data.Store object as
26385      * a cache of Roo.data.Records.
26386      */
26387     read : function(response){
26388         var json = response.responseText;
26389        
26390         var o = /* eval:var:o */ eval("("+json+")");
26391         if(!o) {
26392             throw {message: "JsonReader.read: Json object not found"};
26393         }
26394         
26395         if(o.metaData){
26396             
26397             delete this.ef;
26398             this.metaFromRemote = true;
26399             this.meta = o.metaData;
26400             this.recordType = Roo.data.Record.create(o.metaData.fields);
26401             this.onMetaChange(this.meta, this.recordType, o);
26402         }
26403         return this.readRecords(o);
26404     },
26405
26406     // private function a store will implement
26407     onMetaChange : function(meta, recordType, o){
26408
26409     },
26410
26411     /**
26412          * @ignore
26413          */
26414     simpleAccess: function(obj, subsc) {
26415         return obj[subsc];
26416     },
26417
26418         /**
26419          * @ignore
26420          */
26421     getJsonAccessor: function(){
26422         var re = /[\[\.]/;
26423         return function(expr) {
26424             try {
26425                 return(re.test(expr))
26426                     ? new Function("obj", "return obj." + expr)
26427                     : function(obj){
26428                         return obj[expr];
26429                     };
26430             } catch(e){}
26431             return Roo.emptyFn;
26432         };
26433     }(),
26434
26435     /**
26436      * Create a data block containing Roo.data.Records from an XML document.
26437      * @param {Object} o An object which contains an Array of row objects in the property specified
26438      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
26439      * which contains the total size of the dataset.
26440      * @return {Object} data A data block which is used by an Roo.data.Store object as
26441      * a cache of Roo.data.Records.
26442      */
26443     readRecords : function(o){
26444         /**
26445          * After any data loads, the raw JSON data is available for further custom processing.
26446          * @type Object
26447          */
26448         this.o = o;
26449         var s = this.meta, Record = this.recordType,
26450             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
26451
26452 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
26453         if (!this.ef) {
26454             if(s.totalProperty) {
26455                     this.getTotal = this.getJsonAccessor(s.totalProperty);
26456                 }
26457                 if(s.successProperty) {
26458                     this.getSuccess = this.getJsonAccessor(s.successProperty);
26459                 }
26460                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
26461                 if (s.id) {
26462                         var g = this.getJsonAccessor(s.id);
26463                         this.getId = function(rec) {
26464                                 var r = g(rec);  
26465                                 return (r === undefined || r === "") ? null : r;
26466                         };
26467                 } else {
26468                         this.getId = function(){return null;};
26469                 }
26470             this.ef = [];
26471             for(var jj = 0; jj < fl; jj++){
26472                 f = fi[jj];
26473                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
26474                 this.ef[jj] = this.getJsonAccessor(map);
26475             }
26476         }
26477
26478         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
26479         if(s.totalProperty){
26480             var vt = parseInt(this.getTotal(o), 10);
26481             if(!isNaN(vt)){
26482                 totalRecords = vt;
26483             }
26484         }
26485         if(s.successProperty){
26486             var vs = this.getSuccess(o);
26487             if(vs === false || vs === 'false'){
26488                 success = false;
26489             }
26490         }
26491         var records = [];
26492         for(var i = 0; i < c; i++){
26493             var n = root[i];
26494             var values = {};
26495             var id = this.getId(n);
26496             for(var j = 0; j < fl; j++){
26497                 f = fi[j];
26498                                 var v = this.ef[j](n);
26499                                 if (!f.convert) {
26500                                         Roo.log('missing convert for ' + f.name);
26501                                         Roo.log(f);
26502                                         continue;
26503                                 }
26504                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
26505             }
26506                         if (!Record) {
26507                                 return {
26508                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
26509                                         success : false,
26510                                         records : [],
26511                                         totalRecords : 0
26512                                 };
26513                         }
26514             var record = new Record(values, id);
26515             record.json = n;
26516             records[i] = record;
26517         }
26518         return {
26519             raw : o,
26520             success : success,
26521             records : records,
26522             totalRecords : totalRecords
26523         };
26524     },
26525     // used when loading children.. @see loadDataFromChildren
26526     toLoadData: function(rec)
26527     {
26528         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26529         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26530         return { data : data, total : data.length };
26531         
26532     }
26533 });/*
26534  * Based on:
26535  * Ext JS Library 1.1.1
26536  * Copyright(c) 2006-2007, Ext JS, LLC.
26537  *
26538  * Originally Released Under LGPL - original licence link has changed is not relivant.
26539  *
26540  * Fork - LGPL
26541  * <script type="text/javascript">
26542  */
26543
26544 /**
26545  * @class Roo.data.XmlReader
26546  * @extends Roo.data.DataReader
26547  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
26548  * based on mappings in a provided Roo.data.Record constructor.<br><br>
26549  * <p>
26550  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
26551  * header in the HTTP response must be set to "text/xml".</em>
26552  * <p>
26553  * Example code:
26554  * <pre><code>
26555 var RecordDef = Roo.data.Record.create([
26556    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
26557    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
26558 ]);
26559 var myReader = new Roo.data.XmlReader({
26560    totalRecords: "results", // The element which contains the total dataset size (optional)
26561    record: "row",           // The repeated element which contains row information
26562    id: "id"                 // The element within the row that provides an ID for the record (optional)
26563 }, RecordDef);
26564 </code></pre>
26565  * <p>
26566  * This would consume an XML file like this:
26567  * <pre><code>
26568 &lt;?xml?>
26569 &lt;dataset>
26570  &lt;results>2&lt;/results>
26571  &lt;row>
26572    &lt;id>1&lt;/id>
26573    &lt;name>Bill&lt;/name>
26574    &lt;occupation>Gardener&lt;/occupation>
26575  &lt;/row>
26576  &lt;row>
26577    &lt;id>2&lt;/id>
26578    &lt;name>Ben&lt;/name>
26579    &lt;occupation>Horticulturalist&lt;/occupation>
26580  &lt;/row>
26581 &lt;/dataset>
26582 </code></pre>
26583  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
26584  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
26585  * paged from the remote server.
26586  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
26587  * @cfg {String} success The DomQuery path to the success attribute used by forms.
26588  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
26589  * a record identifier value.
26590  * @constructor
26591  * Create a new XmlReader
26592  * @param {Object} meta Metadata configuration options
26593  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
26594  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
26595  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
26596  */
26597 Roo.data.XmlReader = function(meta, recordType){
26598     meta = meta || {};
26599     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26600 };
26601 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
26602     
26603     readerType : 'Xml',
26604     
26605     /**
26606      * This method is only used by a DataProxy which has retrieved data from a remote server.
26607          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
26608          * to contain a method called 'responseXML' that returns an XML document object.
26609      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26610      * a cache of Roo.data.Records.
26611      */
26612     read : function(response){
26613         var doc = response.responseXML;
26614         if(!doc) {
26615             throw {message: "XmlReader.read: XML Document not available"};
26616         }
26617         return this.readRecords(doc);
26618     },
26619
26620     /**
26621      * Create a data block containing Roo.data.Records from an XML document.
26622          * @param {Object} doc A parsed XML document.
26623      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26624      * a cache of Roo.data.Records.
26625      */
26626     readRecords : function(doc){
26627         /**
26628          * After any data loads/reads, the raw XML Document is available for further custom processing.
26629          * @type XMLDocument
26630          */
26631         this.xmlData = doc;
26632         var root = doc.documentElement || doc;
26633         var q = Roo.DomQuery;
26634         var recordType = this.recordType, fields = recordType.prototype.fields;
26635         var sid = this.meta.id;
26636         var totalRecords = 0, success = true;
26637         if(this.meta.totalRecords){
26638             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
26639         }
26640         
26641         if(this.meta.success){
26642             var sv = q.selectValue(this.meta.success, root, true);
26643             success = sv !== false && sv !== 'false';
26644         }
26645         var records = [];
26646         var ns = q.select(this.meta.record, root);
26647         for(var i = 0, len = ns.length; i < len; i++) {
26648                 var n = ns[i];
26649                 var values = {};
26650                 var id = sid ? q.selectValue(sid, n) : undefined;
26651                 for(var j = 0, jlen = fields.length; j < jlen; j++){
26652                     var f = fields.items[j];
26653                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
26654                     v = f.convert(v);
26655                     values[f.name] = v;
26656                 }
26657                 var record = new recordType(values, id);
26658                 record.node = n;
26659                 records[records.length] = record;
26660             }
26661
26662             return {
26663                 success : success,
26664                 records : records,
26665                 totalRecords : totalRecords || records.length
26666             };
26667     }
26668 });/*
26669  * Based on:
26670  * Ext JS Library 1.1.1
26671  * Copyright(c) 2006-2007, Ext JS, LLC.
26672  *
26673  * Originally Released Under LGPL - original licence link has changed is not relivant.
26674  *
26675  * Fork - LGPL
26676  * <script type="text/javascript">
26677  */
26678
26679 /**
26680  * @class Roo.data.ArrayReader
26681  * @extends Roo.data.DataReader
26682  * Data reader class to create an Array of Roo.data.Record objects from an Array.
26683  * Each element of that Array represents a row of data fields. The
26684  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
26685  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
26686  * <p>
26687  * Example code:.
26688  * <pre><code>
26689 var RecordDef = Roo.data.Record.create([
26690     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
26691     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
26692 ]);
26693 var myReader = new Roo.data.ArrayReader({
26694     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
26695 }, RecordDef);
26696 </code></pre>
26697  * <p>
26698  * This would consume an Array like this:
26699  * <pre><code>
26700 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
26701   </code></pre>
26702  
26703  * @constructor
26704  * Create a new JsonReader
26705  * @param {Object} meta Metadata configuration options.
26706  * @param {Object|Array} recordType Either an Array of field definition objects
26707  * 
26708  * @cfg {Array} fields Array of field definition objects
26709  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26710  * as specified to {@link Roo.data.Record#create},
26711  * or an {@link Roo.data.Record} object
26712  *
26713  * 
26714  * created using {@link Roo.data.Record#create}.
26715  */
26716 Roo.data.ArrayReader = function(meta, recordType)
26717 {    
26718     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26719 };
26720
26721 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
26722     
26723       /**
26724      * Create a data block containing Roo.data.Records from an XML document.
26725      * @param {Object} o An Array of row objects which represents the dataset.
26726      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
26727      * a cache of Roo.data.Records.
26728      */
26729     readRecords : function(o)
26730     {
26731         var sid = this.meta ? this.meta.id : null;
26732         var recordType = this.recordType, fields = recordType.prototype.fields;
26733         var records = [];
26734         var root = o;
26735         for(var i = 0; i < root.length; i++){
26736             var n = root[i];
26737             var values = {};
26738             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
26739             for(var j = 0, jlen = fields.length; j < jlen; j++){
26740                 var f = fields.items[j];
26741                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
26742                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
26743                 v = f.convert(v);
26744                 values[f.name] = v;
26745             }
26746             var record = new recordType(values, id);
26747             record.json = n;
26748             records[records.length] = record;
26749         }
26750         return {
26751             records : records,
26752             totalRecords : records.length
26753         };
26754     },
26755     // used when loading children.. @see loadDataFromChildren
26756     toLoadData: function(rec)
26757     {
26758         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26759         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26760         
26761     }
26762     
26763     
26764 });/*
26765  * Based on:
26766  * Ext JS Library 1.1.1
26767  * Copyright(c) 2006-2007, Ext JS, LLC.
26768  *
26769  * Originally Released Under LGPL - original licence link has changed is not relivant.
26770  *
26771  * Fork - LGPL
26772  * <script type="text/javascript">
26773  */
26774
26775
26776 /**
26777  * @class Roo.data.Tree
26778  * @extends Roo.util.Observable
26779  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
26780  * in the tree have most standard DOM functionality.
26781  * @constructor
26782  * @param {Node} root (optional) The root node
26783  */
26784 Roo.data.Tree = function(root){
26785    this.nodeHash = {};
26786    /**
26787     * The root node for this tree
26788     * @type Node
26789     */
26790    this.root = null;
26791    if(root){
26792        this.setRootNode(root);
26793    }
26794    this.addEvents({
26795        /**
26796         * @event append
26797         * Fires when a new child node is appended to a node in this tree.
26798         * @param {Tree} tree The owner tree
26799         * @param {Node} parent The parent node
26800         * @param {Node} node The newly appended node
26801         * @param {Number} index The index of the newly appended node
26802         */
26803        "append" : true,
26804        /**
26805         * @event remove
26806         * Fires when a child node is removed from a node in this tree.
26807         * @param {Tree} tree The owner tree
26808         * @param {Node} parent The parent node
26809         * @param {Node} node The child node removed
26810         */
26811        "remove" : true,
26812        /**
26813         * @event move
26814         * Fires when a node is moved to a new location in the tree
26815         * @param {Tree} tree The owner tree
26816         * @param {Node} node The node moved
26817         * @param {Node} oldParent The old parent of this node
26818         * @param {Node} newParent The new parent of this node
26819         * @param {Number} index The index it was moved to
26820         */
26821        "move" : true,
26822        /**
26823         * @event insert
26824         * Fires when a new child node is inserted in a node in this tree.
26825         * @param {Tree} tree The owner tree
26826         * @param {Node} parent The parent node
26827         * @param {Node} node The child node inserted
26828         * @param {Node} refNode The child node the node was inserted before
26829         */
26830        "insert" : true,
26831        /**
26832         * @event beforeappend
26833         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
26834         * @param {Tree} tree The owner tree
26835         * @param {Node} parent The parent node
26836         * @param {Node} node The child node to be appended
26837         */
26838        "beforeappend" : true,
26839        /**
26840         * @event beforeremove
26841         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
26842         * @param {Tree} tree The owner tree
26843         * @param {Node} parent The parent node
26844         * @param {Node} node The child node to be removed
26845         */
26846        "beforeremove" : true,
26847        /**
26848         * @event beforemove
26849         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
26850         * @param {Tree} tree The owner tree
26851         * @param {Node} node The node being moved
26852         * @param {Node} oldParent The parent of the node
26853         * @param {Node} newParent The new parent the node is moving to
26854         * @param {Number} index The index it is being moved to
26855         */
26856        "beforemove" : true,
26857        /**
26858         * @event beforeinsert
26859         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
26860         * @param {Tree} tree The owner tree
26861         * @param {Node} parent The parent node
26862         * @param {Node} node The child node to be inserted
26863         * @param {Node} refNode The child node the node is being inserted before
26864         */
26865        "beforeinsert" : true
26866    });
26867
26868     Roo.data.Tree.superclass.constructor.call(this);
26869 };
26870
26871 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
26872     pathSeparator: "/",
26873
26874     proxyNodeEvent : function(){
26875         return this.fireEvent.apply(this, arguments);
26876     },
26877
26878     /**
26879      * Returns the root node for this tree.
26880      * @return {Node}
26881      */
26882     getRootNode : function(){
26883         return this.root;
26884     },
26885
26886     /**
26887      * Sets the root node for this tree.
26888      * @param {Node} node
26889      * @return {Node}
26890      */
26891     setRootNode : function(node){
26892         this.root = node;
26893         node.ownerTree = this;
26894         node.isRoot = true;
26895         this.registerNode(node);
26896         return node;
26897     },
26898
26899     /**
26900      * Gets a node in this tree by its id.
26901      * @param {String} id
26902      * @return {Node}
26903      */
26904     getNodeById : function(id){
26905         return this.nodeHash[id];
26906     },
26907
26908     registerNode : function(node){
26909         this.nodeHash[node.id] = node;
26910     },
26911
26912     unregisterNode : function(node){
26913         delete this.nodeHash[node.id];
26914     },
26915
26916     toString : function(){
26917         return "[Tree"+(this.id?" "+this.id:"")+"]";
26918     }
26919 });
26920
26921 /**
26922  * @class Roo.data.Node
26923  * @extends Roo.util.Observable
26924  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
26925  * @cfg {String} id The id for this node. If one is not specified, one is generated.
26926  * @constructor
26927  * @param {Object} attributes The attributes/config for the node
26928  */
26929 Roo.data.Node = function(attributes){
26930     /**
26931      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
26932      * @type {Object}
26933      */
26934     this.attributes = attributes || {};
26935     this.leaf = this.attributes.leaf;
26936     /**
26937      * The node id. @type String
26938      */
26939     this.id = this.attributes.id;
26940     if(!this.id){
26941         this.id = Roo.id(null, "ynode-");
26942         this.attributes.id = this.id;
26943     }
26944      
26945     
26946     /**
26947      * All child nodes of this node. @type Array
26948      */
26949     this.childNodes = [];
26950     if(!this.childNodes.indexOf){ // indexOf is a must
26951         this.childNodes.indexOf = function(o){
26952             for(var i = 0, len = this.length; i < len; i++){
26953                 if(this[i] == o) {
26954                     return i;
26955                 }
26956             }
26957             return -1;
26958         };
26959     }
26960     /**
26961      * The parent node for this node. @type Node
26962      */
26963     this.parentNode = null;
26964     /**
26965      * The first direct child node of this node, or null if this node has no child nodes. @type Node
26966      */
26967     this.firstChild = null;
26968     /**
26969      * The last direct child node of this node, or null if this node has no child nodes. @type Node
26970      */
26971     this.lastChild = null;
26972     /**
26973      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
26974      */
26975     this.previousSibling = null;
26976     /**
26977      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
26978      */
26979     this.nextSibling = null;
26980
26981     this.addEvents({
26982        /**
26983         * @event append
26984         * Fires when a new child node is appended
26985         * @param {Tree} tree The owner tree
26986         * @param {Node} this This node
26987         * @param {Node} node The newly appended node
26988         * @param {Number} index The index of the newly appended node
26989         */
26990        "append" : true,
26991        /**
26992         * @event remove
26993         * Fires when a child node is removed
26994         * @param {Tree} tree The owner tree
26995         * @param {Node} this This node
26996         * @param {Node} node The removed node
26997         */
26998        "remove" : true,
26999        /**
27000         * @event move
27001         * Fires when this node is moved to a new location in the tree
27002         * @param {Tree} tree The owner tree
27003         * @param {Node} this This node
27004         * @param {Node} oldParent The old parent of this node
27005         * @param {Node} newParent The new parent of this node
27006         * @param {Number} index The index it was moved to
27007         */
27008        "move" : true,
27009        /**
27010         * @event insert
27011         * Fires when a new child node is inserted.
27012         * @param {Tree} tree The owner tree
27013         * @param {Node} this This node
27014         * @param {Node} node The child node inserted
27015         * @param {Node} refNode The child node the node was inserted before
27016         */
27017        "insert" : true,
27018        /**
27019         * @event beforeappend
27020         * Fires before a new child is appended, return false to cancel the append.
27021         * @param {Tree} tree The owner tree
27022         * @param {Node} this This node
27023         * @param {Node} node The child node to be appended
27024         */
27025        "beforeappend" : true,
27026        /**
27027         * @event beforeremove
27028         * Fires before a child is removed, return false to cancel the remove.
27029         * @param {Tree} tree The owner tree
27030         * @param {Node} this This node
27031         * @param {Node} node The child node to be removed
27032         */
27033        "beforeremove" : true,
27034        /**
27035         * @event beforemove
27036         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
27037         * @param {Tree} tree The owner tree
27038         * @param {Node} this This node
27039         * @param {Node} oldParent The parent of this node
27040         * @param {Node} newParent The new parent this node is moving to
27041         * @param {Number} index The index it is being moved to
27042         */
27043        "beforemove" : true,
27044        /**
27045         * @event beforeinsert
27046         * Fires before a new child is inserted, return false to cancel the insert.
27047         * @param {Tree} tree The owner tree
27048         * @param {Node} this This node
27049         * @param {Node} node The child node to be inserted
27050         * @param {Node} refNode The child node the node is being inserted before
27051         */
27052        "beforeinsert" : true
27053    });
27054     this.listeners = this.attributes.listeners;
27055     Roo.data.Node.superclass.constructor.call(this);
27056 };
27057
27058 Roo.extend(Roo.data.Node, Roo.util.Observable, {
27059     fireEvent : function(evtName){
27060         // first do standard event for this node
27061         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
27062             return false;
27063         }
27064         // then bubble it up to the tree if the event wasn't cancelled
27065         var ot = this.getOwnerTree();
27066         if(ot){
27067             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
27068                 return false;
27069             }
27070         }
27071         return true;
27072     },
27073
27074     /**
27075      * Returns true if this node is a leaf
27076      * @return {Boolean}
27077      */
27078     isLeaf : function(){
27079         return this.leaf === true;
27080     },
27081
27082     // private
27083     setFirstChild : function(node){
27084         this.firstChild = node;
27085     },
27086
27087     //private
27088     setLastChild : function(node){
27089         this.lastChild = node;
27090     },
27091
27092
27093     /**
27094      * Returns true if this node is the last child of its parent
27095      * @return {Boolean}
27096      */
27097     isLast : function(){
27098        return (!this.parentNode ? true : this.parentNode.lastChild == this);
27099     },
27100
27101     /**
27102      * Returns true if this node is the first child of its parent
27103      * @return {Boolean}
27104      */
27105     isFirst : function(){
27106        return (!this.parentNode ? true : this.parentNode.firstChild == this);
27107     },
27108
27109     hasChildNodes : function(){
27110         return !this.isLeaf() && this.childNodes.length > 0;
27111     },
27112
27113     /**
27114      * Insert node(s) as the last child node of this node.
27115      * @param {Node/Array} node The node or Array of nodes to append
27116      * @return {Node} The appended node if single append, or null if an array was passed
27117      */
27118     appendChild : function(node){
27119         var multi = false;
27120         if(node instanceof Array){
27121             multi = node;
27122         }else if(arguments.length > 1){
27123             multi = arguments;
27124         }
27125         
27126         // if passed an array or multiple args do them one by one
27127         if(multi){
27128             for(var i = 0, len = multi.length; i < len; i++) {
27129                 this.appendChild(multi[i]);
27130             }
27131         }else{
27132             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
27133                 return false;
27134             }
27135             var index = this.childNodes.length;
27136             var oldParent = node.parentNode;
27137             // it's a move, make sure we move it cleanly
27138             if(oldParent){
27139                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
27140                     return false;
27141                 }
27142                 oldParent.removeChild(node);
27143             }
27144             
27145             index = this.childNodes.length;
27146             if(index == 0){
27147                 this.setFirstChild(node);
27148             }
27149             this.childNodes.push(node);
27150             node.parentNode = this;
27151             var ps = this.childNodes[index-1];
27152             if(ps){
27153                 node.previousSibling = ps;
27154                 ps.nextSibling = node;
27155             }else{
27156                 node.previousSibling = null;
27157             }
27158             node.nextSibling = null;
27159             this.setLastChild(node);
27160             node.setOwnerTree(this.getOwnerTree());
27161             this.fireEvent("append", this.ownerTree, this, node, index);
27162             if(this.ownerTree) {
27163                 this.ownerTree.fireEvent("appendnode", this, node, index);
27164             }
27165             if(oldParent){
27166                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
27167             }
27168             return node;
27169         }
27170     },
27171
27172     /**
27173      * Removes a child node from this node.
27174      * @param {Node} node The node to remove
27175      * @return {Node} The removed node
27176      */
27177     removeChild : function(node){
27178         var index = this.childNodes.indexOf(node);
27179         if(index == -1){
27180             return false;
27181         }
27182         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
27183             return false;
27184         }
27185
27186         // remove it from childNodes collection
27187         this.childNodes.splice(index, 1);
27188
27189         // update siblings
27190         if(node.previousSibling){
27191             node.previousSibling.nextSibling = node.nextSibling;
27192         }
27193         if(node.nextSibling){
27194             node.nextSibling.previousSibling = node.previousSibling;
27195         }
27196
27197         // update child refs
27198         if(this.firstChild == node){
27199             this.setFirstChild(node.nextSibling);
27200         }
27201         if(this.lastChild == node){
27202             this.setLastChild(node.previousSibling);
27203         }
27204
27205         node.setOwnerTree(null);
27206         // clear any references from the node
27207         node.parentNode = null;
27208         node.previousSibling = null;
27209         node.nextSibling = null;
27210         this.fireEvent("remove", this.ownerTree, this, node);
27211         return node;
27212     },
27213
27214     /**
27215      * Inserts the first node before the second node in this nodes childNodes collection.
27216      * @param {Node} node The node to insert
27217      * @param {Node} refNode The node to insert before (if null the node is appended)
27218      * @return {Node} The inserted node
27219      */
27220     insertBefore : function(node, refNode){
27221         if(!refNode){ // like standard Dom, refNode can be null for append
27222             return this.appendChild(node);
27223         }
27224         // nothing to do
27225         if(node == refNode){
27226             return false;
27227         }
27228
27229         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
27230             return false;
27231         }
27232         var index = this.childNodes.indexOf(refNode);
27233         var oldParent = node.parentNode;
27234         var refIndex = index;
27235
27236         // when moving internally, indexes will change after remove
27237         if(oldParent == this && this.childNodes.indexOf(node) < index){
27238             refIndex--;
27239         }
27240
27241         // it's a move, make sure we move it cleanly
27242         if(oldParent){
27243             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
27244                 return false;
27245             }
27246             oldParent.removeChild(node);
27247         }
27248         if(refIndex == 0){
27249             this.setFirstChild(node);
27250         }
27251         this.childNodes.splice(refIndex, 0, node);
27252         node.parentNode = this;
27253         var ps = this.childNodes[refIndex-1];
27254         if(ps){
27255             node.previousSibling = ps;
27256             ps.nextSibling = node;
27257         }else{
27258             node.previousSibling = null;
27259         }
27260         node.nextSibling = refNode;
27261         refNode.previousSibling = node;
27262         node.setOwnerTree(this.getOwnerTree());
27263         this.fireEvent("insert", this.ownerTree, this, node, refNode);
27264         if(oldParent){
27265             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
27266         }
27267         return node;
27268     },
27269
27270     /**
27271      * Returns the child node at the specified index.
27272      * @param {Number} index
27273      * @return {Node}
27274      */
27275     item : function(index){
27276         return this.childNodes[index];
27277     },
27278
27279     /**
27280      * Replaces one child node in this node with another.
27281      * @param {Node} newChild The replacement node
27282      * @param {Node} oldChild The node to replace
27283      * @return {Node} The replaced node
27284      */
27285     replaceChild : function(newChild, oldChild){
27286         this.insertBefore(newChild, oldChild);
27287         this.removeChild(oldChild);
27288         return oldChild;
27289     },
27290
27291     /**
27292      * Returns the index of a child node
27293      * @param {Node} node
27294      * @return {Number} The index of the node or -1 if it was not found
27295      */
27296     indexOf : function(child){
27297         return this.childNodes.indexOf(child);
27298     },
27299
27300     /**
27301      * Returns the tree this node is in.
27302      * @return {Tree}
27303      */
27304     getOwnerTree : function(){
27305         // if it doesn't have one, look for one
27306         if(!this.ownerTree){
27307             var p = this;
27308             while(p){
27309                 if(p.ownerTree){
27310                     this.ownerTree = p.ownerTree;
27311                     break;
27312                 }
27313                 p = p.parentNode;
27314             }
27315         }
27316         return this.ownerTree;
27317     },
27318
27319     /**
27320      * Returns depth of this node (the root node has a depth of 0)
27321      * @return {Number}
27322      */
27323     getDepth : function(){
27324         var depth = 0;
27325         var p = this;
27326         while(p.parentNode){
27327             ++depth;
27328             p = p.parentNode;
27329         }
27330         return depth;
27331     },
27332
27333     // private
27334     setOwnerTree : function(tree){
27335         // if it's move, we need to update everyone
27336         if(tree != this.ownerTree){
27337             if(this.ownerTree){
27338                 this.ownerTree.unregisterNode(this);
27339             }
27340             this.ownerTree = tree;
27341             var cs = this.childNodes;
27342             for(var i = 0, len = cs.length; i < len; i++) {
27343                 cs[i].setOwnerTree(tree);
27344             }
27345             if(tree){
27346                 tree.registerNode(this);
27347             }
27348         }
27349     },
27350
27351     /**
27352      * Returns the path for this node. The path can be used to expand or select this node programmatically.
27353      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
27354      * @return {String} The path
27355      */
27356     getPath : function(attr){
27357         attr = attr || "id";
27358         var p = this.parentNode;
27359         var b = [this.attributes[attr]];
27360         while(p){
27361             b.unshift(p.attributes[attr]);
27362             p = p.parentNode;
27363         }
27364         var sep = this.getOwnerTree().pathSeparator;
27365         return sep + b.join(sep);
27366     },
27367
27368     /**
27369      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
27370      * function call will be the scope provided or the current node. The arguments to the function
27371      * will be the args provided or the current node. If the function returns false at any point,
27372      * the bubble is stopped.
27373      * @param {Function} fn The function to call
27374      * @param {Object} scope (optional) The scope of the function (defaults to current node)
27375      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27376      */
27377     bubble : function(fn, scope, args){
27378         var p = this;
27379         while(p){
27380             if(fn.call(scope || p, args || p) === false){
27381                 break;
27382             }
27383             p = p.parentNode;
27384         }
27385     },
27386
27387     /**
27388      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
27389      * function call will be the scope provided or the current node. The arguments to the function
27390      * will be the args provided or the current node. If the function returns false at any point,
27391      * the cascade is stopped on that branch.
27392      * @param {Function} fn The function to call
27393      * @param {Object} scope (optional) The scope of the function (defaults to current node)
27394      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27395      */
27396     cascade : function(fn, scope, args){
27397         if(fn.call(scope || this, args || this) !== false){
27398             var cs = this.childNodes;
27399             for(var i = 0, len = cs.length; i < len; i++) {
27400                 cs[i].cascade(fn, scope, args);
27401             }
27402         }
27403     },
27404
27405     /**
27406      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
27407      * function call will be the scope provided or the current node. The arguments to the function
27408      * will be the args provided or the current node. If the function returns false at any point,
27409      * the iteration stops.
27410      * @param {Function} fn The function to call
27411      * @param {Object} scope (optional) The scope of the function (defaults to current node)
27412      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27413      */
27414     eachChild : function(fn, scope, args){
27415         var cs = this.childNodes;
27416         for(var i = 0, len = cs.length; i < len; i++) {
27417                 if(fn.call(scope || this, args || cs[i]) === false){
27418                     break;
27419                 }
27420         }
27421     },
27422
27423     /**
27424      * Finds the first child that has the attribute with the specified value.
27425      * @param {String} attribute The attribute name
27426      * @param {Mixed} value The value to search for
27427      * @return {Node} The found child or null if none was found
27428      */
27429     findChild : function(attribute, value){
27430         var cs = this.childNodes;
27431         for(var i = 0, len = cs.length; i < len; i++) {
27432                 if(cs[i].attributes[attribute] == value){
27433                     return cs[i];
27434                 }
27435         }
27436         return null;
27437     },
27438
27439     /**
27440      * Finds the first child by a custom function. The child matches if the function passed
27441      * returns true.
27442      * @param {Function} fn
27443      * @param {Object} scope (optional)
27444      * @return {Node} The found child or null if none was found
27445      */
27446     findChildBy : function(fn, scope){
27447         var cs = this.childNodes;
27448         for(var i = 0, len = cs.length; i < len; i++) {
27449                 if(fn.call(scope||cs[i], cs[i]) === true){
27450                     return cs[i];
27451                 }
27452         }
27453         return null;
27454     },
27455
27456     /**
27457      * Sorts this nodes children using the supplied sort function
27458      * @param {Function} fn
27459      * @param {Object} scope (optional)
27460      */
27461     sort : function(fn, scope){
27462         var cs = this.childNodes;
27463         var len = cs.length;
27464         if(len > 0){
27465             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
27466             cs.sort(sortFn);
27467             for(var i = 0; i < len; i++){
27468                 var n = cs[i];
27469                 n.previousSibling = cs[i-1];
27470                 n.nextSibling = cs[i+1];
27471                 if(i == 0){
27472                     this.setFirstChild(n);
27473                 }
27474                 if(i == len-1){
27475                     this.setLastChild(n);
27476                 }
27477             }
27478         }
27479     },
27480
27481     /**
27482      * Returns true if this node is an ancestor (at any point) of the passed node.
27483      * @param {Node} node
27484      * @return {Boolean}
27485      */
27486     contains : function(node){
27487         return node.isAncestor(this);
27488     },
27489
27490     /**
27491      * Returns true if the passed node is an ancestor (at any point) of this node.
27492      * @param {Node} node
27493      * @return {Boolean}
27494      */
27495     isAncestor : function(node){
27496         var p = this.parentNode;
27497         while(p){
27498             if(p == node){
27499                 return true;
27500             }
27501             p = p.parentNode;
27502         }
27503         return false;
27504     },
27505
27506     toString : function(){
27507         return "[Node"+(this.id?" "+this.id:"")+"]";
27508     }
27509 });/*
27510  * Based on:
27511  * Ext JS Library 1.1.1
27512  * Copyright(c) 2006-2007, Ext JS, LLC.
27513  *
27514  * Originally Released Under LGPL - original licence link has changed is not relivant.
27515  *
27516  * Fork - LGPL
27517  * <script type="text/javascript">
27518  */
27519
27520
27521 /**
27522  * @class Roo.Shadow
27523  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
27524  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
27525  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
27526  * @constructor
27527  * Create a new Shadow
27528  * @param {Object} config The config object
27529  */
27530 Roo.Shadow = function(config){
27531     Roo.apply(this, config);
27532     if(typeof this.mode != "string"){
27533         this.mode = this.defaultMode;
27534     }
27535     var o = this.offset, a = {h: 0};
27536     var rad = Math.floor(this.offset/2);
27537     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
27538         case "drop":
27539             a.w = 0;
27540             a.l = a.t = o;
27541             a.t -= 1;
27542             if(Roo.isIE){
27543                 a.l -= this.offset + rad;
27544                 a.t -= this.offset + rad;
27545                 a.w -= rad;
27546                 a.h -= rad;
27547                 a.t += 1;
27548             }
27549         break;
27550         case "sides":
27551             a.w = (o*2);
27552             a.l = -o;
27553             a.t = o-1;
27554             if(Roo.isIE){
27555                 a.l -= (this.offset - rad);
27556                 a.t -= this.offset + rad;
27557                 a.l += 1;
27558                 a.w -= (this.offset - rad)*2;
27559                 a.w -= rad + 1;
27560                 a.h -= 1;
27561             }
27562         break;
27563         case "frame":
27564             a.w = a.h = (o*2);
27565             a.l = a.t = -o;
27566             a.t += 1;
27567             a.h -= 2;
27568             if(Roo.isIE){
27569                 a.l -= (this.offset - rad);
27570                 a.t -= (this.offset - rad);
27571                 a.l += 1;
27572                 a.w -= (this.offset + rad + 1);
27573                 a.h -= (this.offset + rad);
27574                 a.h += 1;
27575             }
27576         break;
27577     };
27578
27579     this.adjusts = a;
27580 };
27581
27582 Roo.Shadow.prototype = {
27583     /**
27584      * @cfg {String} mode
27585      * The shadow display mode.  Supports the following options:<br />
27586      * sides: Shadow displays on both sides and bottom only<br />
27587      * frame: Shadow displays equally on all four sides<br />
27588      * drop: Traditional bottom-right drop shadow (default)
27589      */
27590     mode: false,
27591     /**
27592      * @cfg {String} offset
27593      * The number of pixels to offset the shadow from the element (defaults to 4)
27594      */
27595     offset: 4,
27596
27597     // private
27598     defaultMode: "drop",
27599
27600     /**
27601      * Displays the shadow under the target element
27602      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
27603      */
27604     show : function(target){
27605         target = Roo.get(target);
27606         if(!this.el){
27607             this.el = Roo.Shadow.Pool.pull();
27608             if(this.el.dom.nextSibling != target.dom){
27609                 this.el.insertBefore(target);
27610             }
27611         }
27612         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
27613         if(Roo.isIE){
27614             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
27615         }
27616         this.realign(
27617             target.getLeft(true),
27618             target.getTop(true),
27619             target.getWidth(),
27620             target.getHeight()
27621         );
27622         this.el.dom.style.display = "block";
27623     },
27624
27625     /**
27626      * Returns true if the shadow is visible, else false
27627      */
27628     isVisible : function(){
27629         return this.el ? true : false;  
27630     },
27631
27632     /**
27633      * Direct alignment when values are already available. Show must be called at least once before
27634      * calling this method to ensure it is initialized.
27635      * @param {Number} left The target element left position
27636      * @param {Number} top The target element top position
27637      * @param {Number} width The target element width
27638      * @param {Number} height The target element height
27639      */
27640     realign : function(l, t, w, h){
27641         if(!this.el){
27642             return;
27643         }
27644         var a = this.adjusts, d = this.el.dom, s = d.style;
27645         var iea = 0;
27646         s.left = (l+a.l)+"px";
27647         s.top = (t+a.t)+"px";
27648         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
27649  
27650         if(s.width != sws || s.height != shs){
27651             s.width = sws;
27652             s.height = shs;
27653             if(!Roo.isIE){
27654                 var cn = d.childNodes;
27655                 var sww = Math.max(0, (sw-12))+"px";
27656                 cn[0].childNodes[1].style.width = sww;
27657                 cn[1].childNodes[1].style.width = sww;
27658                 cn[2].childNodes[1].style.width = sww;
27659                 cn[1].style.height = Math.max(0, (sh-12))+"px";
27660             }
27661         }
27662     },
27663
27664     /**
27665      * Hides this shadow
27666      */
27667     hide : function(){
27668         if(this.el){
27669             this.el.dom.style.display = "none";
27670             Roo.Shadow.Pool.push(this.el);
27671             delete this.el;
27672         }
27673     },
27674
27675     /**
27676      * Adjust the z-index of this shadow
27677      * @param {Number} zindex The new z-index
27678      */
27679     setZIndex : function(z){
27680         this.zIndex = z;
27681         if(this.el){
27682             this.el.setStyle("z-index", z);
27683         }
27684     }
27685 };
27686
27687 // Private utility class that manages the internal Shadow cache
27688 Roo.Shadow.Pool = function(){
27689     var p = [];
27690     var markup = Roo.isIE ?
27691                  '<div class="x-ie-shadow"></div>' :
27692                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
27693     return {
27694         pull : function(){
27695             var sh = p.shift();
27696             if(!sh){
27697                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
27698                 sh.autoBoxAdjust = false;
27699             }
27700             return sh;
27701         },
27702
27703         push : function(sh){
27704             p.push(sh);
27705         }
27706     };
27707 }();/*
27708  * Based on:
27709  * Ext JS Library 1.1.1
27710  * Copyright(c) 2006-2007, Ext JS, LLC.
27711  *
27712  * Originally Released Under LGPL - original licence link has changed is not relivant.
27713  *
27714  * Fork - LGPL
27715  * <script type="text/javascript">
27716  */
27717
27718
27719 /**
27720  * @class Roo.SplitBar
27721  * @extends Roo.util.Observable
27722  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
27723  * <br><br>
27724  * Usage:
27725  * <pre><code>
27726 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
27727                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
27728 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
27729 split.minSize = 100;
27730 split.maxSize = 600;
27731 split.animate = true;
27732 split.on('moved', splitterMoved);
27733 </code></pre>
27734  * @constructor
27735  * Create a new SplitBar
27736  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
27737  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
27738  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27739  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
27740                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
27741                         position of the SplitBar).
27742  */
27743 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
27744     
27745     /** @private */
27746     this.el = Roo.get(dragElement, true);
27747     this.el.dom.unselectable = "on";
27748     /** @private */
27749     this.resizingEl = Roo.get(resizingElement, true);
27750
27751     /**
27752      * @private
27753      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27754      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
27755      * @type Number
27756      */
27757     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
27758     
27759     /**
27760      * The minimum size of the resizing element. (Defaults to 0)
27761      * @type Number
27762      */
27763     this.minSize = 0;
27764     
27765     /**
27766      * The maximum size of the resizing element. (Defaults to 2000)
27767      * @type Number
27768      */
27769     this.maxSize = 2000;
27770     
27771     /**
27772      * Whether to animate the transition to the new size
27773      * @type Boolean
27774      */
27775     this.animate = false;
27776     
27777     /**
27778      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
27779      * @type Boolean
27780      */
27781     this.useShim = false;
27782     
27783     /** @private */
27784     this.shim = null;
27785     
27786     if(!existingProxy){
27787         /** @private */
27788         this.proxy = Roo.SplitBar.createProxy(this.orientation);
27789     }else{
27790         this.proxy = Roo.get(existingProxy).dom;
27791     }
27792     /** @private */
27793     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
27794     
27795     /** @private */
27796     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
27797     
27798     /** @private */
27799     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
27800     
27801     /** @private */
27802     this.dragSpecs = {};
27803     
27804     /**
27805      * @private The adapter to use to positon and resize elements
27806      */
27807     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
27808     this.adapter.init(this);
27809     
27810     if(this.orientation == Roo.SplitBar.HORIZONTAL){
27811         /** @private */
27812         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
27813         this.el.addClass("x-splitbar-h");
27814     }else{
27815         /** @private */
27816         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
27817         this.el.addClass("x-splitbar-v");
27818     }
27819     
27820     this.addEvents({
27821         /**
27822          * @event resize
27823          * Fires when the splitter is moved (alias for {@link #event-moved})
27824          * @param {Roo.SplitBar} this
27825          * @param {Number} newSize the new width or height
27826          */
27827         "resize" : true,
27828         /**
27829          * @event moved
27830          * Fires when the splitter is moved
27831          * @param {Roo.SplitBar} this
27832          * @param {Number} newSize the new width or height
27833          */
27834         "moved" : true,
27835         /**
27836          * @event beforeresize
27837          * Fires before the splitter is dragged
27838          * @param {Roo.SplitBar} this
27839          */
27840         "beforeresize" : true,
27841
27842         "beforeapply" : true
27843     });
27844
27845     Roo.util.Observable.call(this);
27846 };
27847
27848 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
27849     onStartProxyDrag : function(x, y){
27850         this.fireEvent("beforeresize", this);
27851         if(!this.overlay){
27852             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
27853             o.unselectable();
27854             o.enableDisplayMode("block");
27855             // all splitbars share the same overlay
27856             Roo.SplitBar.prototype.overlay = o;
27857         }
27858         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27859         this.overlay.show();
27860         Roo.get(this.proxy).setDisplayed("block");
27861         var size = this.adapter.getElementSize(this);
27862         this.activeMinSize = this.getMinimumSize();;
27863         this.activeMaxSize = this.getMaximumSize();;
27864         var c1 = size - this.activeMinSize;
27865         var c2 = Math.max(this.activeMaxSize - size, 0);
27866         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27867             this.dd.resetConstraints();
27868             this.dd.setXConstraint(
27869                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
27870                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
27871             );
27872             this.dd.setYConstraint(0, 0);
27873         }else{
27874             this.dd.resetConstraints();
27875             this.dd.setXConstraint(0, 0);
27876             this.dd.setYConstraint(
27877                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
27878                 this.placement == Roo.SplitBar.TOP ? c2 : c1
27879             );
27880          }
27881         this.dragSpecs.startSize = size;
27882         this.dragSpecs.startPoint = [x, y];
27883         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
27884     },
27885     
27886     /** 
27887      * @private Called after the drag operation by the DDProxy
27888      */
27889     onEndProxyDrag : function(e){
27890         Roo.get(this.proxy).setDisplayed(false);
27891         var endPoint = Roo.lib.Event.getXY(e);
27892         if(this.overlay){
27893             this.overlay.hide();
27894         }
27895         var newSize;
27896         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27897             newSize = this.dragSpecs.startSize + 
27898                 (this.placement == Roo.SplitBar.LEFT ?
27899                     endPoint[0] - this.dragSpecs.startPoint[0] :
27900                     this.dragSpecs.startPoint[0] - endPoint[0]
27901                 );
27902         }else{
27903             newSize = this.dragSpecs.startSize + 
27904                 (this.placement == Roo.SplitBar.TOP ?
27905                     endPoint[1] - this.dragSpecs.startPoint[1] :
27906                     this.dragSpecs.startPoint[1] - endPoint[1]
27907                 );
27908         }
27909         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
27910         if(newSize != this.dragSpecs.startSize){
27911             if(this.fireEvent('beforeapply', this, newSize) !== false){
27912                 this.adapter.setElementSize(this, newSize);
27913                 this.fireEvent("moved", this, newSize);
27914                 this.fireEvent("resize", this, newSize);
27915             }
27916         }
27917     },
27918     
27919     /**
27920      * Get the adapter this SplitBar uses
27921      * @return The adapter object
27922      */
27923     getAdapter : function(){
27924         return this.adapter;
27925     },
27926     
27927     /**
27928      * Set the adapter this SplitBar uses
27929      * @param {Object} adapter A SplitBar adapter object
27930      */
27931     setAdapter : function(adapter){
27932         this.adapter = adapter;
27933         this.adapter.init(this);
27934     },
27935     
27936     /**
27937      * Gets the minimum size for the resizing element
27938      * @return {Number} The minimum size
27939      */
27940     getMinimumSize : function(){
27941         return this.minSize;
27942     },
27943     
27944     /**
27945      * Sets the minimum size for the resizing element
27946      * @param {Number} minSize The minimum size
27947      */
27948     setMinimumSize : function(minSize){
27949         this.minSize = minSize;
27950     },
27951     
27952     /**
27953      * Gets the maximum size for the resizing element
27954      * @return {Number} The maximum size
27955      */
27956     getMaximumSize : function(){
27957         return this.maxSize;
27958     },
27959     
27960     /**
27961      * Sets the maximum size for the resizing element
27962      * @param {Number} maxSize The maximum size
27963      */
27964     setMaximumSize : function(maxSize){
27965         this.maxSize = maxSize;
27966     },
27967     
27968     /**
27969      * Sets the initialize size for the resizing element
27970      * @param {Number} size The initial size
27971      */
27972     setCurrentSize : function(size){
27973         var oldAnimate = this.animate;
27974         this.animate = false;
27975         this.adapter.setElementSize(this, size);
27976         this.animate = oldAnimate;
27977     },
27978     
27979     /**
27980      * Destroy this splitbar. 
27981      * @param {Boolean} removeEl True to remove the element
27982      */
27983     destroy : function(removeEl){
27984         if(this.shim){
27985             this.shim.remove();
27986         }
27987         this.dd.unreg();
27988         this.proxy.parentNode.removeChild(this.proxy);
27989         if(removeEl){
27990             this.el.remove();
27991         }
27992     }
27993 });
27994
27995 /**
27996  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
27997  */
27998 Roo.SplitBar.createProxy = function(dir){
27999     var proxy = new Roo.Element(document.createElement("div"));
28000     proxy.unselectable();
28001     var cls = 'x-splitbar-proxy';
28002     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
28003     document.body.appendChild(proxy.dom);
28004     return proxy.dom;
28005 };
28006
28007 /** 
28008  * @class Roo.SplitBar.BasicLayoutAdapter
28009  * Default Adapter. It assumes the splitter and resizing element are not positioned
28010  * elements and only gets/sets the width of the element. Generally used for table based layouts.
28011  */
28012 Roo.SplitBar.BasicLayoutAdapter = function(){
28013 };
28014
28015 Roo.SplitBar.BasicLayoutAdapter.prototype = {
28016     // do nothing for now
28017     init : function(s){
28018     
28019     },
28020     /**
28021      * Called before drag operations to get the current size of the resizing element. 
28022      * @param {Roo.SplitBar} s The SplitBar using this adapter
28023      */
28024      getElementSize : function(s){
28025         if(s.orientation == Roo.SplitBar.HORIZONTAL){
28026             return s.resizingEl.getWidth();
28027         }else{
28028             return s.resizingEl.getHeight();
28029         }
28030     },
28031     
28032     /**
28033      * Called after drag operations to set the size of the resizing element.
28034      * @param {Roo.SplitBar} s The SplitBar using this adapter
28035      * @param {Number} newSize The new size to set
28036      * @param {Function} onComplete A function to be invoked when resizing is complete
28037      */
28038     setElementSize : function(s, newSize, onComplete){
28039         if(s.orientation == Roo.SplitBar.HORIZONTAL){
28040             if(!s.animate){
28041                 s.resizingEl.setWidth(newSize);
28042                 if(onComplete){
28043                     onComplete(s, newSize);
28044                 }
28045             }else{
28046                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
28047             }
28048         }else{
28049             
28050             if(!s.animate){
28051                 s.resizingEl.setHeight(newSize);
28052                 if(onComplete){
28053                     onComplete(s, newSize);
28054                 }
28055             }else{
28056                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
28057             }
28058         }
28059     }
28060 };
28061
28062 /** 
28063  *@class Roo.SplitBar.AbsoluteLayoutAdapter
28064  * @extends Roo.SplitBar.BasicLayoutAdapter
28065  * Adapter that  moves the splitter element to align with the resized sizing element. 
28066  * Used with an absolute positioned SplitBar.
28067  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
28068  * document.body, make sure you assign an id to the body element.
28069  */
28070 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
28071     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
28072     this.container = Roo.get(container);
28073 };
28074
28075 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
28076     init : function(s){
28077         this.basic.init(s);
28078     },
28079     
28080     getElementSize : function(s){
28081         return this.basic.getElementSize(s);
28082     },
28083     
28084     setElementSize : function(s, newSize, onComplete){
28085         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
28086     },
28087     
28088     moveSplitter : function(s){
28089         var yes = Roo.SplitBar;
28090         switch(s.placement){
28091             case yes.LEFT:
28092                 s.el.setX(s.resizingEl.getRight());
28093                 break;
28094             case yes.RIGHT:
28095                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
28096                 break;
28097             case yes.TOP:
28098                 s.el.setY(s.resizingEl.getBottom());
28099                 break;
28100             case yes.BOTTOM:
28101                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
28102                 break;
28103         }
28104     }
28105 };
28106
28107 /**
28108  * Orientation constant - Create a vertical SplitBar
28109  * @static
28110  * @type Number
28111  */
28112 Roo.SplitBar.VERTICAL = 1;
28113
28114 /**
28115  * Orientation constant - Create a horizontal SplitBar
28116  * @static
28117  * @type Number
28118  */
28119 Roo.SplitBar.HORIZONTAL = 2;
28120
28121 /**
28122  * Placement constant - The resizing element is to the left of the splitter element
28123  * @static
28124  * @type Number
28125  */
28126 Roo.SplitBar.LEFT = 1;
28127
28128 /**
28129  * Placement constant - The resizing element is to the right of the splitter element
28130  * @static
28131  * @type Number
28132  */
28133 Roo.SplitBar.RIGHT = 2;
28134
28135 /**
28136  * Placement constant - The resizing element is positioned above the splitter element
28137  * @static
28138  * @type Number
28139  */
28140 Roo.SplitBar.TOP = 3;
28141
28142 /**
28143  * Placement constant - The resizing element is positioned under splitter element
28144  * @static
28145  * @type Number
28146  */
28147 Roo.SplitBar.BOTTOM = 4;
28148 /*
28149  * Based on:
28150  * Ext JS Library 1.1.1
28151  * Copyright(c) 2006-2007, Ext JS, LLC.
28152  *
28153  * Originally Released Under LGPL - original licence link has changed is not relivant.
28154  *
28155  * Fork - LGPL
28156  * <script type="text/javascript">
28157  */
28158
28159 /**
28160  * @class Roo.View
28161  * @extends Roo.util.Observable
28162  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
28163  * This class also supports single and multi selection modes. <br>
28164  * Create a data model bound view:
28165  <pre><code>
28166  var store = new Roo.data.Store(...);
28167
28168  var view = new Roo.View({
28169     el : "my-element",
28170     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
28171  
28172     singleSelect: true,
28173     selectedClass: "ydataview-selected",
28174     store: store
28175  });
28176
28177  // listen for node click?
28178  view.on("click", function(vw, index, node, e){
28179  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
28180  });
28181
28182  // load XML data
28183  dataModel.load("foobar.xml");
28184  </code></pre>
28185  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
28186  * <br><br>
28187  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
28188  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
28189  * 
28190  * Note: old style constructor is still suported (container, template, config)
28191  * 
28192  * @constructor
28193  * Create a new View
28194  * @param {Object} config The config object
28195  * 
28196  */
28197 Roo.View = function(config, depreciated_tpl, depreciated_config){
28198     
28199     this.parent = false;
28200     
28201     if (typeof(depreciated_tpl) == 'undefined') {
28202         // new way.. - universal constructor.
28203         Roo.apply(this, config);
28204         this.el  = Roo.get(this.el);
28205     } else {
28206         // old format..
28207         this.el  = Roo.get(config);
28208         this.tpl = depreciated_tpl;
28209         Roo.apply(this, depreciated_config);
28210     }
28211     this.wrapEl  = this.el.wrap().wrap();
28212     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
28213     
28214     
28215     if(typeof(this.tpl) == "string"){
28216         this.tpl = new Roo.Template(this.tpl);
28217     } else {
28218         // support xtype ctors..
28219         this.tpl = new Roo.factory(this.tpl, Roo);
28220     }
28221     
28222     
28223     this.tpl.compile();
28224     
28225     /** @private */
28226     this.addEvents({
28227         /**
28228          * @event beforeclick
28229          * Fires before a click is processed. Returns false to cancel the default action.
28230          * @param {Roo.View} this
28231          * @param {Number} index The index of the target node
28232          * @param {HTMLElement} node The target node
28233          * @param {Roo.EventObject} e The raw event object
28234          */
28235             "beforeclick" : true,
28236         /**
28237          * @event click
28238          * Fires when a template node is clicked.
28239          * @param {Roo.View} this
28240          * @param {Number} index The index of the target node
28241          * @param {HTMLElement} node The target node
28242          * @param {Roo.EventObject} e The raw event object
28243          */
28244             "click" : true,
28245         /**
28246          * @event dblclick
28247          * Fires when a template node is double clicked.
28248          * @param {Roo.View} this
28249          * @param {Number} index The index of the target node
28250          * @param {HTMLElement} node The target node
28251          * @param {Roo.EventObject} e The raw event object
28252          */
28253             "dblclick" : true,
28254         /**
28255          * @event contextmenu
28256          * Fires when a template node is right clicked.
28257          * @param {Roo.View} this
28258          * @param {Number} index The index of the target node
28259          * @param {HTMLElement} node The target node
28260          * @param {Roo.EventObject} e The raw event object
28261          */
28262             "contextmenu" : true,
28263         /**
28264          * @event selectionchange
28265          * Fires when the selected nodes change.
28266          * @param {Roo.View} this
28267          * @param {Array} selections Array of the selected nodes
28268          */
28269             "selectionchange" : true,
28270     
28271         /**
28272          * @event beforeselect
28273          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
28274          * @param {Roo.View} this
28275          * @param {HTMLElement} node The node to be selected
28276          * @param {Array} selections Array of currently selected nodes
28277          */
28278             "beforeselect" : true,
28279         /**
28280          * @event preparedata
28281          * Fires on every row to render, to allow you to change the data.
28282          * @param {Roo.View} this
28283          * @param {Object} data to be rendered (change this)
28284          */
28285           "preparedata" : true
28286           
28287           
28288         });
28289
28290
28291
28292     this.el.on({
28293         "click": this.onClick,
28294         "dblclick": this.onDblClick,
28295         "contextmenu": this.onContextMenu,
28296         scope:this
28297     });
28298
28299     this.selections = [];
28300     this.nodes = [];
28301     this.cmp = new Roo.CompositeElementLite([]);
28302     if(this.store){
28303         this.store = Roo.factory(this.store, Roo.data);
28304         this.setStore(this.store, true);
28305     }
28306     
28307     if ( this.footer && this.footer.xtype) {
28308            
28309          var fctr = this.wrapEl.appendChild(document.createElement("div"));
28310         
28311         this.footer.dataSource = this.store;
28312         this.footer.container = fctr;
28313         this.footer = Roo.factory(this.footer, Roo);
28314         fctr.insertFirst(this.el);
28315         
28316         // this is a bit insane - as the paging toolbar seems to detach the el..
28317 //        dom.parentNode.parentNode.parentNode
28318          // they get detached?
28319     }
28320     
28321     
28322     Roo.View.superclass.constructor.call(this);
28323     
28324     
28325 };
28326
28327 Roo.extend(Roo.View, Roo.util.Observable, {
28328     
28329      /**
28330      * @cfg {Roo.data.Store} store Data store to load data from.
28331      */
28332     store : false,
28333     
28334     /**
28335      * @cfg {String|Roo.Element} el The container element.
28336      */
28337     el : '',
28338     
28339     /**
28340      * @cfg {String|Roo.Template} tpl The template used by this View 
28341      */
28342     tpl : false,
28343     /**
28344      * @cfg {String} dataName the named area of the template to use as the data area
28345      *                          Works with domtemplates roo-name="name"
28346      */
28347     dataName: false,
28348     /**
28349      * @cfg {String} selectedClass The css class to add to selected nodes
28350      */
28351     selectedClass : "x-view-selected",
28352      /**
28353      * @cfg {String} emptyText The empty text to show when nothing is loaded.
28354      */
28355     emptyText : "",
28356     
28357     /**
28358      * @cfg {String} text to display on mask (default Loading)
28359      */
28360     mask : false,
28361     /**
28362      * @cfg {Boolean} multiSelect Allow multiple selection
28363      */
28364     multiSelect : false,
28365     /**
28366      * @cfg {Boolean} singleSelect Allow single selection
28367      */
28368     singleSelect:  false,
28369     
28370     /**
28371      * @cfg {Boolean} toggleSelect - selecting 
28372      */
28373     toggleSelect : false,
28374     
28375     /**
28376      * @cfg {Boolean} tickable - selecting 
28377      */
28378     tickable : false,
28379     
28380     /**
28381      * Returns the element this view is bound to.
28382      * @return {Roo.Element}
28383      */
28384     getEl : function(){
28385         return this.wrapEl;
28386     },
28387     
28388     
28389
28390     /**
28391      * Refreshes the view. - called by datachanged on the store. - do not call directly.
28392      */
28393     refresh : function(){
28394         //Roo.log('refresh');
28395         var t = this.tpl;
28396         
28397         // if we are using something like 'domtemplate', then
28398         // the what gets used is:
28399         // t.applySubtemplate(NAME, data, wrapping data..)
28400         // the outer template then get' applied with
28401         //     the store 'extra data'
28402         // and the body get's added to the
28403         //      roo-name="data" node?
28404         //      <span class='roo-tpl-{name}'></span> ?????
28405         
28406         
28407         
28408         this.clearSelections();
28409         this.el.update("");
28410         var html = [];
28411         var records = this.store.getRange();
28412         if(records.length < 1) {
28413             
28414             // is this valid??  = should it render a template??
28415             
28416             this.el.update(this.emptyText);
28417             return;
28418         }
28419         var el = this.el;
28420         if (this.dataName) {
28421             this.el.update(t.apply(this.store.meta)); //????
28422             el = this.el.child('.roo-tpl-' + this.dataName);
28423         }
28424         
28425         for(var i = 0, len = records.length; i < len; i++){
28426             var data = this.prepareData(records[i].data, i, records[i]);
28427             this.fireEvent("preparedata", this, data, i, records[i]);
28428             
28429             var d = Roo.apply({}, data);
28430             
28431             if(this.tickable){
28432                 Roo.apply(d, {'roo-id' : Roo.id()});
28433                 
28434                 var _this = this;
28435             
28436                 Roo.each(this.parent.item, function(item){
28437                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
28438                         return;
28439                     }
28440                     Roo.apply(d, {'roo-data-checked' : 'checked'});
28441                 });
28442             }
28443             
28444             html[html.length] = Roo.util.Format.trim(
28445                 this.dataName ?
28446                     t.applySubtemplate(this.dataName, d, this.store.meta) :
28447                     t.apply(d)
28448             );
28449         }
28450         
28451         
28452         
28453         el.update(html.join(""));
28454         this.nodes = el.dom.childNodes;
28455         this.updateIndexes(0);
28456     },
28457     
28458
28459     /**
28460      * Function to override to reformat the data that is sent to
28461      * the template for each node.
28462      * DEPRICATED - use the preparedata event handler.
28463      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
28464      * a JSON object for an UpdateManager bound view).
28465      */
28466     prepareData : function(data, index, record)
28467     {
28468         this.fireEvent("preparedata", this, data, index, record);
28469         return data;
28470     },
28471
28472     onUpdate : function(ds, record){
28473         // Roo.log('on update');   
28474         this.clearSelections();
28475         var index = this.store.indexOf(record);
28476         var n = this.nodes[index];
28477         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
28478         n.parentNode.removeChild(n);
28479         this.updateIndexes(index, index);
28480     },
28481
28482     
28483     
28484 // --------- FIXME     
28485     onAdd : function(ds, records, index)
28486     {
28487         //Roo.log(['on Add', ds, records, index] );        
28488         this.clearSelections();
28489         if(this.nodes.length == 0){
28490             this.refresh();
28491             return;
28492         }
28493         var n = this.nodes[index];
28494         for(var i = 0, len = records.length; i < len; i++){
28495             var d = this.prepareData(records[i].data, i, records[i]);
28496             if(n){
28497                 this.tpl.insertBefore(n, d);
28498             }else{
28499                 
28500                 this.tpl.append(this.el, d);
28501             }
28502         }
28503         this.updateIndexes(index);
28504     },
28505
28506     onRemove : function(ds, record, index){
28507        // Roo.log('onRemove');
28508         this.clearSelections();
28509         var el = this.dataName  ?
28510             this.el.child('.roo-tpl-' + this.dataName) :
28511             this.el; 
28512         
28513         el.dom.removeChild(this.nodes[index]);
28514         this.updateIndexes(index);
28515     },
28516
28517     /**
28518      * Refresh an individual node.
28519      * @param {Number} index
28520      */
28521     refreshNode : function(index){
28522         this.onUpdate(this.store, this.store.getAt(index));
28523     },
28524
28525     updateIndexes : function(startIndex, endIndex){
28526         var ns = this.nodes;
28527         startIndex = startIndex || 0;
28528         endIndex = endIndex || ns.length - 1;
28529         for(var i = startIndex; i <= endIndex; i++){
28530             ns[i].nodeIndex = i;
28531         }
28532     },
28533
28534     /**
28535      * Changes the data store this view uses and refresh the view.
28536      * @param {Store} store
28537      */
28538     setStore : function(store, initial){
28539         if(!initial && this.store){
28540             this.store.un("datachanged", this.refresh);
28541             this.store.un("add", this.onAdd);
28542             this.store.un("remove", this.onRemove);
28543             this.store.un("update", this.onUpdate);
28544             this.store.un("clear", this.refresh);
28545             this.store.un("beforeload", this.onBeforeLoad);
28546             this.store.un("load", this.onLoad);
28547             this.store.un("loadexception", this.onLoad);
28548         }
28549         if(store){
28550           
28551             store.on("datachanged", this.refresh, this);
28552             store.on("add", this.onAdd, this);
28553             store.on("remove", this.onRemove, this);
28554             store.on("update", this.onUpdate, this);
28555             store.on("clear", this.refresh, this);
28556             store.on("beforeload", this.onBeforeLoad, this);
28557             store.on("load", this.onLoad, this);
28558             store.on("loadexception", this.onLoad, this);
28559         }
28560         
28561         if(store){
28562             this.refresh();
28563         }
28564     },
28565     /**
28566      * onbeforeLoad - masks the loading area.
28567      *
28568      */
28569     onBeforeLoad : function(store,opts)
28570     {
28571          //Roo.log('onBeforeLoad');   
28572         if (!opts.add) {
28573             this.el.update("");
28574         }
28575         this.el.mask(this.mask ? this.mask : "Loading" ); 
28576     },
28577     onLoad : function ()
28578     {
28579         this.el.unmask();
28580     },
28581     
28582
28583     /**
28584      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
28585      * @param {HTMLElement} node
28586      * @return {HTMLElement} The template node
28587      */
28588     findItemFromChild : function(node){
28589         var el = this.dataName  ?
28590             this.el.child('.roo-tpl-' + this.dataName,true) :
28591             this.el.dom; 
28592         
28593         if(!node || node.parentNode == el){
28594                     return node;
28595             }
28596             var p = node.parentNode;
28597             while(p && p != el){
28598             if(p.parentNode == el){
28599                 return p;
28600             }
28601             p = p.parentNode;
28602         }
28603             return null;
28604     },
28605
28606     /** @ignore */
28607     onClick : function(e){
28608         var item = this.findItemFromChild(e.getTarget());
28609         if(item){
28610             var index = this.indexOf(item);
28611             if(this.onItemClick(item, index, e) !== false){
28612                 this.fireEvent("click", this, index, item, e);
28613             }
28614         }else{
28615             this.clearSelections();
28616         }
28617     },
28618
28619     /** @ignore */
28620     onContextMenu : function(e){
28621         var item = this.findItemFromChild(e.getTarget());
28622         if(item){
28623             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
28624         }
28625     },
28626
28627     /** @ignore */
28628     onDblClick : function(e){
28629         var item = this.findItemFromChild(e.getTarget());
28630         if(item){
28631             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
28632         }
28633     },
28634
28635     onItemClick : function(item, index, e)
28636     {
28637         if(this.fireEvent("beforeclick", this, index, item, e) === false){
28638             return false;
28639         }
28640         if (this.toggleSelect) {
28641             var m = this.isSelected(item) ? 'unselect' : 'select';
28642             //Roo.log(m);
28643             var _t = this;
28644             _t[m](item, true, false);
28645             return true;
28646         }
28647         if(this.multiSelect || this.singleSelect){
28648             if(this.multiSelect && e.shiftKey && this.lastSelection){
28649                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
28650             }else{
28651                 this.select(item, this.multiSelect && e.ctrlKey);
28652                 this.lastSelection = item;
28653             }
28654             
28655             if(!this.tickable){
28656                 e.preventDefault();
28657             }
28658             
28659         }
28660         return true;
28661     },
28662
28663     /**
28664      * Get the number of selected nodes.
28665      * @return {Number}
28666      */
28667     getSelectionCount : function(){
28668         return this.selections.length;
28669     },
28670
28671     /**
28672      * Get the currently selected nodes.
28673      * @return {Array} An array of HTMLElements
28674      */
28675     getSelectedNodes : function(){
28676         return this.selections;
28677     },
28678
28679     /**
28680      * Get the indexes of the selected nodes.
28681      * @return {Array}
28682      */
28683     getSelectedIndexes : function(){
28684         var indexes = [], s = this.selections;
28685         for(var i = 0, len = s.length; i < len; i++){
28686             indexes.push(s[i].nodeIndex);
28687         }
28688         return indexes;
28689     },
28690
28691     /**
28692      * Clear all selections
28693      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
28694      */
28695     clearSelections : function(suppressEvent){
28696         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
28697             this.cmp.elements = this.selections;
28698             this.cmp.removeClass(this.selectedClass);
28699             this.selections = [];
28700             if(!suppressEvent){
28701                 this.fireEvent("selectionchange", this, this.selections);
28702             }
28703         }
28704     },
28705
28706     /**
28707      * Returns true if the passed node is selected
28708      * @param {HTMLElement/Number} node The node or node index
28709      * @return {Boolean}
28710      */
28711     isSelected : function(node){
28712         var s = this.selections;
28713         if(s.length < 1){
28714             return false;
28715         }
28716         node = this.getNode(node);
28717         return s.indexOf(node) !== -1;
28718     },
28719
28720     /**
28721      * Selects nodes.
28722      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
28723      * @param {Boolean} keepExisting (optional) true to keep existing selections
28724      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28725      */
28726     select : function(nodeInfo, keepExisting, suppressEvent){
28727         if(nodeInfo instanceof Array){
28728             if(!keepExisting){
28729                 this.clearSelections(true);
28730             }
28731             for(var i = 0, len = nodeInfo.length; i < len; i++){
28732                 this.select(nodeInfo[i], true, true);
28733             }
28734             return;
28735         } 
28736         var node = this.getNode(nodeInfo);
28737         if(!node || this.isSelected(node)){
28738             return; // already selected.
28739         }
28740         if(!keepExisting){
28741             this.clearSelections(true);
28742         }
28743         
28744         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28745             Roo.fly(node).addClass(this.selectedClass);
28746             this.selections.push(node);
28747             if(!suppressEvent){
28748                 this.fireEvent("selectionchange", this, this.selections);
28749             }
28750         }
28751         
28752         
28753     },
28754       /**
28755      * Unselects nodes.
28756      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
28757      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
28758      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28759      */
28760     unselect : function(nodeInfo, keepExisting, suppressEvent)
28761     {
28762         if(nodeInfo instanceof Array){
28763             Roo.each(this.selections, function(s) {
28764                 this.unselect(s, nodeInfo);
28765             }, this);
28766             return;
28767         }
28768         var node = this.getNode(nodeInfo);
28769         if(!node || !this.isSelected(node)){
28770             //Roo.log("not selected");
28771             return; // not selected.
28772         }
28773         // fireevent???
28774         var ns = [];
28775         Roo.each(this.selections, function(s) {
28776             if (s == node ) {
28777                 Roo.fly(node).removeClass(this.selectedClass);
28778
28779                 return;
28780             }
28781             ns.push(s);
28782         },this);
28783         
28784         this.selections= ns;
28785         this.fireEvent("selectionchange", this, this.selections);
28786     },
28787
28788     /**
28789      * Gets a template node.
28790      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28791      * @return {HTMLElement} The node or null if it wasn't found
28792      */
28793     getNode : function(nodeInfo){
28794         if(typeof nodeInfo == "string"){
28795             return document.getElementById(nodeInfo);
28796         }else if(typeof nodeInfo == "number"){
28797             return this.nodes[nodeInfo];
28798         }
28799         return nodeInfo;
28800     },
28801
28802     /**
28803      * Gets a range template nodes.
28804      * @param {Number} startIndex
28805      * @param {Number} endIndex
28806      * @return {Array} An array of nodes
28807      */
28808     getNodes : function(start, end){
28809         var ns = this.nodes;
28810         start = start || 0;
28811         end = typeof end == "undefined" ? ns.length - 1 : end;
28812         var nodes = [];
28813         if(start <= end){
28814             for(var i = start; i <= end; i++){
28815                 nodes.push(ns[i]);
28816             }
28817         } else{
28818             for(var i = start; i >= end; i--){
28819                 nodes.push(ns[i]);
28820             }
28821         }
28822         return nodes;
28823     },
28824
28825     /**
28826      * Finds the index of the passed node
28827      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28828      * @return {Number} The index of the node or -1
28829      */
28830     indexOf : function(node){
28831         node = this.getNode(node);
28832         if(typeof node.nodeIndex == "number"){
28833             return node.nodeIndex;
28834         }
28835         var ns = this.nodes;
28836         for(var i = 0, len = ns.length; i < len; i++){
28837             if(ns[i] == node){
28838                 return i;
28839             }
28840         }
28841         return -1;
28842     }
28843 });
28844 /*
28845  * Based on:
28846  * Ext JS Library 1.1.1
28847  * Copyright(c) 2006-2007, Ext JS, LLC.
28848  *
28849  * Originally Released Under LGPL - original licence link has changed is not relivant.
28850  *
28851  * Fork - LGPL
28852  * <script type="text/javascript">
28853  */
28854
28855 /**
28856  * @class Roo.JsonView
28857  * @extends Roo.View
28858  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
28859 <pre><code>
28860 var view = new Roo.JsonView({
28861     container: "my-element",
28862     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
28863     multiSelect: true, 
28864     jsonRoot: "data" 
28865 });
28866
28867 // listen for node click?
28868 view.on("click", function(vw, index, node, e){
28869     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
28870 });
28871
28872 // direct load of JSON data
28873 view.load("foobar.php");
28874
28875 // Example from my blog list
28876 var tpl = new Roo.Template(
28877     '&lt;div class="entry"&gt;' +
28878     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
28879     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
28880     "&lt;/div&gt;&lt;hr /&gt;"
28881 );
28882
28883 var moreView = new Roo.JsonView({
28884     container :  "entry-list", 
28885     template : tpl,
28886     jsonRoot: "posts"
28887 });
28888 moreView.on("beforerender", this.sortEntries, this);
28889 moreView.load({
28890     url: "/blog/get-posts.php",
28891     params: "allposts=true",
28892     text: "Loading Blog Entries..."
28893 });
28894 </code></pre>
28895
28896 * Note: old code is supported with arguments : (container, template, config)
28897
28898
28899  * @constructor
28900  * Create a new JsonView
28901  * 
28902  * @param {Object} config The config object
28903  * 
28904  */
28905 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
28906     
28907     
28908     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
28909
28910     var um = this.el.getUpdateManager();
28911     um.setRenderer(this);
28912     um.on("update", this.onLoad, this);
28913     um.on("failure", this.onLoadException, this);
28914
28915     /**
28916      * @event beforerender
28917      * Fires before rendering of the downloaded JSON data.
28918      * @param {Roo.JsonView} this
28919      * @param {Object} data The JSON data loaded
28920      */
28921     /**
28922      * @event load
28923      * Fires when data is loaded.
28924      * @param {Roo.JsonView} this
28925      * @param {Object} data The JSON data loaded
28926      * @param {Object} response The raw Connect response object
28927      */
28928     /**
28929      * @event loadexception
28930      * Fires when loading fails.
28931      * @param {Roo.JsonView} this
28932      * @param {Object} response The raw Connect response object
28933      */
28934     this.addEvents({
28935         'beforerender' : true,
28936         'load' : true,
28937         'loadexception' : true
28938     });
28939 };
28940 Roo.extend(Roo.JsonView, Roo.View, {
28941     /**
28942      * @type {String} The root property in the loaded JSON object that contains the data
28943      */
28944     jsonRoot : "",
28945
28946     /**
28947      * Refreshes the view.
28948      */
28949     refresh : function(){
28950         this.clearSelections();
28951         this.el.update("");
28952         var html = [];
28953         var o = this.jsonData;
28954         if(o && o.length > 0){
28955             for(var i = 0, len = o.length; i < len; i++){
28956                 var data = this.prepareData(o[i], i, o);
28957                 html[html.length] = this.tpl.apply(data);
28958             }
28959         }else{
28960             html.push(this.emptyText);
28961         }
28962         this.el.update(html.join(""));
28963         this.nodes = this.el.dom.childNodes;
28964         this.updateIndexes(0);
28965     },
28966
28967     /**
28968      * Performs an async HTTP request, and loads the JSON from the response. If <i>params</i> are specified it uses POST, otherwise it uses GET.
28969      * @param {Object/String/Function} url The URL for this request, or a function to call to get the URL, or a config object containing any of the following options:
28970      <pre><code>
28971      view.load({
28972          url: "your-url.php",
28973          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
28974          callback: yourFunction,
28975          scope: yourObject, //(optional scope)
28976          discardUrl: false,
28977          nocache: false,
28978          text: "Loading...",
28979          timeout: 30,
28980          scripts: false
28981      });
28982      </code></pre>
28983      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
28984      * are respectively shorthand for <i>disableCaching</i>, <i>indicatorText</i>, and <i>loadScripts</i> and are used to set their associated property on this UpdateManager instance.
28985      * @param {String/Object} params (optional) The parameters to pass, as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
28986      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
28987      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
28988      */
28989     load : function(){
28990         var um = this.el.getUpdateManager();
28991         um.update.apply(um, arguments);
28992     },
28993
28994     // note - render is a standard framework call...
28995     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
28996     render : function(el, response){
28997         
28998         this.clearSelections();
28999         this.el.update("");
29000         var o;
29001         try{
29002             if (response != '') {
29003                 o = Roo.util.JSON.decode(response.responseText);
29004                 if(this.jsonRoot){
29005                     
29006                     o = o[this.jsonRoot];
29007                 }
29008             }
29009         } catch(e){
29010         }
29011         /**
29012          * The current JSON data or null
29013          */
29014         this.jsonData = o;
29015         this.beforeRender();
29016         this.refresh();
29017     },
29018
29019 /**
29020  * Get the number of records in the current JSON dataset
29021  * @return {Number}
29022  */
29023     getCount : function(){
29024         return this.jsonData ? this.jsonData.length : 0;
29025     },
29026
29027 /**
29028  * Returns the JSON object for the specified node(s)
29029  * @param {HTMLElement/Array} node The node or an array of nodes
29030  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
29031  * you get the JSON object for the node
29032  */
29033     getNodeData : function(node){
29034         if(node instanceof Array){
29035             var data = [];
29036             for(var i = 0, len = node.length; i < len; i++){
29037                 data.push(this.getNodeData(node[i]));
29038             }
29039             return data;
29040         }
29041         return this.jsonData[this.indexOf(node)] || null;
29042     },
29043
29044     beforeRender : function(){
29045         this.snapshot = this.jsonData;
29046         if(this.sortInfo){
29047             this.sort.apply(this, this.sortInfo);
29048         }
29049         this.fireEvent("beforerender", this, this.jsonData);
29050     },
29051
29052     onLoad : function(el, o){
29053         this.fireEvent("load", this, this.jsonData, o);
29054     },
29055
29056     onLoadException : function(el, o){
29057         this.fireEvent("loadexception", this, o);
29058     },
29059
29060 /**
29061  * Filter the data by a specific property.
29062  * @param {String} property A property on your JSON objects
29063  * @param {String/RegExp} value Either string that the property values
29064  * should start with, or a RegExp to test against the property
29065  */
29066     filter : function(property, value){
29067         if(this.jsonData){
29068             var data = [];
29069             var ss = this.snapshot;
29070             if(typeof value == "string"){
29071                 var vlen = value.length;
29072                 if(vlen == 0){
29073                     this.clearFilter();
29074                     return;
29075                 }
29076                 value = value.toLowerCase();
29077                 for(var i = 0, len = ss.length; i < len; i++){
29078                     var o = ss[i];
29079                     if(o[property].substr(0, vlen).toLowerCase() == value){
29080                         data.push(o);
29081                     }
29082                 }
29083             } else if(value.exec){ // regex?
29084                 for(var i = 0, len = ss.length; i < len; i++){
29085                     var o = ss[i];
29086                     if(value.test(o[property])){
29087                         data.push(o);
29088                     }
29089                 }
29090             } else{
29091                 return;
29092             }
29093             this.jsonData = data;
29094             this.refresh();
29095         }
29096     },
29097
29098 /**
29099  * Filter by a function. The passed function will be called with each
29100  * object in the current dataset. If the function returns true the value is kept,
29101  * otherwise it is filtered.
29102  * @param {Function} fn
29103  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
29104  */
29105     filterBy : function(fn, scope){
29106         if(this.jsonData){
29107             var data = [];
29108             var ss = this.snapshot;
29109             for(var i = 0, len = ss.length; i < len; i++){
29110                 var o = ss[i];
29111                 if(fn.call(scope || this, o)){
29112                     data.push(o);
29113                 }
29114             }
29115             this.jsonData = data;
29116             this.refresh();
29117         }
29118     },
29119
29120 /**
29121  * Clears the current filter.
29122  */
29123     clearFilter : function(){
29124         if(this.snapshot && this.jsonData != this.snapshot){
29125             this.jsonData = this.snapshot;
29126             this.refresh();
29127         }
29128     },
29129
29130
29131 /**
29132  * Sorts the data for this view and refreshes it.
29133  * @param {String} property A property on your JSON objects to sort on
29134  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
29135  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
29136  */
29137     sort : function(property, dir, sortType){
29138         this.sortInfo = Array.prototype.slice.call(arguments, 0);
29139         if(this.jsonData){
29140             var p = property;
29141             var dsc = dir && dir.toLowerCase() == "desc";
29142             var f = function(o1, o2){
29143                 var v1 = sortType ? sortType(o1[p]) : o1[p];
29144                 var v2 = sortType ? sortType(o2[p]) : o2[p];
29145                 ;
29146                 if(v1 < v2){
29147                     return dsc ? +1 : -1;
29148                 } else if(v1 > v2){
29149                     return dsc ? -1 : +1;
29150                 } else{
29151                     return 0;
29152                 }
29153             };
29154             this.jsonData.sort(f);
29155             this.refresh();
29156             if(this.jsonData != this.snapshot){
29157                 this.snapshot.sort(f);
29158             }
29159         }
29160     }
29161 });/*
29162  * Based on:
29163  * Ext JS Library 1.1.1
29164  * Copyright(c) 2006-2007, Ext JS, LLC.
29165  *
29166  * Originally Released Under LGPL - original licence link has changed is not relivant.
29167  *
29168  * Fork - LGPL
29169  * <script type="text/javascript">
29170  */
29171  
29172
29173 /**
29174  * @class Roo.ColorPalette
29175  * @extends Roo.Component
29176  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
29177  * Here's an example of typical usage:
29178  * <pre><code>
29179 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
29180 cp.render('my-div');
29181
29182 cp.on('select', function(palette, selColor){
29183     // do something with selColor
29184 });
29185 </code></pre>
29186  * @constructor
29187  * Create a new ColorPalette
29188  * @param {Object} config The config object
29189  */
29190 Roo.ColorPalette = function(config){
29191     Roo.ColorPalette.superclass.constructor.call(this, config);
29192     this.addEvents({
29193         /**
29194              * @event select
29195              * Fires when a color is selected
29196              * @param {ColorPalette} this
29197              * @param {String} color The 6-digit color hex code (without the # symbol)
29198              */
29199         select: true
29200     });
29201
29202     if(this.handler){
29203         this.on("select", this.handler, this.scope, true);
29204     }
29205 };
29206 Roo.extend(Roo.ColorPalette, Roo.Component, {
29207     /**
29208      * @cfg {String} itemCls
29209      * The CSS class to apply to the containing element (defaults to "x-color-palette")
29210      */
29211     itemCls : "x-color-palette",
29212     /**
29213      * @cfg {String} value
29214      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
29215      * the hex codes are case-sensitive.
29216      */
29217     value : null,
29218     clickEvent:'click',
29219     // private
29220     ctype: "Roo.ColorPalette",
29221
29222     /**
29223      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
29224      */
29225     allowReselect : false,
29226
29227     /**
29228      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
29229      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
29230      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
29231      * of colors with the width setting until the box is symmetrical.</p>
29232      * <p>You can override individual colors if needed:</p>
29233      * <pre><code>
29234 var cp = new Roo.ColorPalette();
29235 cp.colors[0] = "FF0000";  // change the first box to red
29236 </code></pre>
29237
29238 Or you can provide a custom array of your own for complete control:
29239 <pre><code>
29240 var cp = new Roo.ColorPalette();
29241 cp.colors = ["000000", "993300", "333300"];
29242 </code></pre>
29243      * @type Array
29244      */
29245     colors : [
29246         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
29247         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
29248         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
29249         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
29250         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
29251     ],
29252
29253     // private
29254     onRender : function(container, position){
29255         var t = new Roo.MasterTemplate(
29256             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
29257         );
29258         var c = this.colors;
29259         for(var i = 0, len = c.length; i < len; i++){
29260             t.add([c[i]]);
29261         }
29262         var el = document.createElement("div");
29263         el.className = this.itemCls;
29264         t.overwrite(el);
29265         container.dom.insertBefore(el, position);
29266         this.el = Roo.get(el);
29267         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
29268         if(this.clickEvent != 'click'){
29269             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
29270         }
29271     },
29272
29273     // private
29274     afterRender : function(){
29275         Roo.ColorPalette.superclass.afterRender.call(this);
29276         if(this.value){
29277             var s = this.value;
29278             this.value = null;
29279             this.select(s);
29280         }
29281     },
29282
29283     // private
29284     handleClick : function(e, t){
29285         e.preventDefault();
29286         if(!this.disabled){
29287             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
29288             this.select(c.toUpperCase());
29289         }
29290     },
29291
29292     /**
29293      * Selects the specified color in the palette (fires the select event)
29294      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
29295      */
29296     select : function(color){
29297         color = color.replace("#", "");
29298         if(color != this.value || this.allowReselect){
29299             var el = this.el;
29300             if(this.value){
29301                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
29302             }
29303             el.child("a.color-"+color).addClass("x-color-palette-sel");
29304             this.value = color;
29305             this.fireEvent("select", this, color);
29306         }
29307     }
29308 });/*
29309  * Based on:
29310  * Ext JS Library 1.1.1
29311  * Copyright(c) 2006-2007, Ext JS, LLC.
29312  *
29313  * Originally Released Under LGPL - original licence link has changed is not relivant.
29314  *
29315  * Fork - LGPL
29316  * <script type="text/javascript">
29317  */
29318  
29319 /**
29320  * @class Roo.DatePicker
29321  * @extends Roo.Component
29322  * Simple date picker class.
29323  * @constructor
29324  * Create a new DatePicker
29325  * @param {Object} config The config object
29326  */
29327 Roo.DatePicker = function(config){
29328     Roo.DatePicker.superclass.constructor.call(this, config);
29329
29330     this.value = config && config.value ?
29331                  config.value.clearTime() : new Date().clearTime();
29332
29333     this.addEvents({
29334         /**
29335              * @event select
29336              * Fires when a date is selected
29337              * @param {DatePicker} this
29338              * @param {Date} date The selected date
29339              */
29340         'select': true,
29341         /**
29342              * @event monthchange
29343              * Fires when the displayed month changes 
29344              * @param {DatePicker} this
29345              * @param {Date} date The selected month
29346              */
29347         'monthchange': true
29348     });
29349
29350     if(this.handler){
29351         this.on("select", this.handler,  this.scope || this);
29352     }
29353     // build the disabledDatesRE
29354     if(!this.disabledDatesRE && this.disabledDates){
29355         var dd = this.disabledDates;
29356         var re = "(?:";
29357         for(var i = 0; i < dd.length; i++){
29358             re += dd[i];
29359             if(i != dd.length-1) {
29360                 re += "|";
29361             }
29362         }
29363         this.disabledDatesRE = new RegExp(re + ")");
29364     }
29365 };
29366
29367 Roo.extend(Roo.DatePicker, Roo.Component, {
29368     /**
29369      * @cfg {String} todayText
29370      * The text to display on the button that selects the current date (defaults to "Today")
29371      */
29372     todayText : "Today",
29373     /**
29374      * @cfg {String} okText
29375      * The text to display on the ok button
29376      */
29377     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
29378     /**
29379      * @cfg {String} cancelText
29380      * The text to display on the cancel button
29381      */
29382     cancelText : "Cancel",
29383     /**
29384      * @cfg {String} todayTip
29385      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
29386      */
29387     todayTip : "{0} (Spacebar)",
29388     /**
29389      * @cfg {Date} minDate
29390      * Minimum allowable date (JavaScript date object, defaults to null)
29391      */
29392     minDate : null,
29393     /**
29394      * @cfg {Date} maxDate
29395      * Maximum allowable date (JavaScript date object, defaults to null)
29396      */
29397     maxDate : null,
29398     /**
29399      * @cfg {String} minText
29400      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
29401      */
29402     minText : "This date is before the minimum date",
29403     /**
29404      * @cfg {String} maxText
29405      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
29406      */
29407     maxText : "This date is after the maximum date",
29408     /**
29409      * @cfg {String} format
29410      * The default date format string which can be overriden for localization support.  The format must be
29411      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
29412      */
29413     format : "m/d/y",
29414     /**
29415      * @cfg {Array} disabledDays
29416      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
29417      */
29418     disabledDays : null,
29419     /**
29420      * @cfg {String} disabledDaysText
29421      * The tooltip to display when the date falls on a disabled day (defaults to "")
29422      */
29423     disabledDaysText : "",
29424     /**
29425      * @cfg {RegExp} disabledDatesRE
29426      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
29427      */
29428     disabledDatesRE : null,
29429     /**
29430      * @cfg {String} disabledDatesText
29431      * The tooltip text to display when the date falls on a disabled date (defaults to "")
29432      */
29433     disabledDatesText : "",
29434     /**
29435      * @cfg {Boolean} constrainToViewport
29436      * True to constrain the date picker to the viewport (defaults to true)
29437      */
29438     constrainToViewport : true,
29439     /**
29440      * @cfg {Array} monthNames
29441      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
29442      */
29443     monthNames : Date.monthNames,
29444     /**
29445      * @cfg {Array} dayNames
29446      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
29447      */
29448     dayNames : Date.dayNames,
29449     /**
29450      * @cfg {String} nextText
29451      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
29452      */
29453     nextText: 'Next Month (Control+Right)',
29454     /**
29455      * @cfg {String} prevText
29456      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
29457      */
29458     prevText: 'Previous Month (Control+Left)',
29459     /**
29460      * @cfg {String} monthYearText
29461      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
29462      */
29463     monthYearText: 'Choose a month (Control+Up/Down to move years)',
29464     /**
29465      * @cfg {Number} startDay
29466      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
29467      */
29468     startDay : 0,
29469     /**
29470      * @cfg {Bool} showClear
29471      * Show a clear button (usefull for date form elements that can be blank.)
29472      */
29473     
29474     showClear: false,
29475     
29476     /**
29477      * Sets the value of the date field
29478      * @param {Date} value The date to set
29479      */
29480     setValue : function(value){
29481         var old = this.value;
29482         
29483         if (typeof(value) == 'string') {
29484          
29485             value = Date.parseDate(value, this.format);
29486         }
29487         if (!value) {
29488             value = new Date();
29489         }
29490         
29491         this.value = value.clearTime(true);
29492         if(this.el){
29493             this.update(this.value);
29494         }
29495     },
29496
29497     /**
29498      * Gets the current selected value of the date field
29499      * @return {Date} The selected date
29500      */
29501     getValue : function(){
29502         return this.value;
29503     },
29504
29505     // private
29506     focus : function(){
29507         if(this.el){
29508             this.update(this.activeDate);
29509         }
29510     },
29511
29512     // privateval
29513     onRender : function(container, position){
29514         
29515         var m = [
29516              '<table cellspacing="0">',
29517                 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'">&#160;</a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'">&#160;</a></td></tr>',
29518                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
29519         var dn = this.dayNames;
29520         for(var i = 0; i < 7; i++){
29521             var d = this.startDay+i;
29522             if(d > 6){
29523                 d = d-7;
29524             }
29525             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
29526         }
29527         m[m.length] = "</tr></thead><tbody><tr>";
29528         for(var i = 0; i < 42; i++) {
29529             if(i % 7 == 0 && i != 0){
29530                 m[m.length] = "</tr><tr>";
29531             }
29532             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
29533         }
29534         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
29535             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
29536
29537         var el = document.createElement("div");
29538         el.className = "x-date-picker";
29539         el.innerHTML = m.join("");
29540
29541         container.dom.insertBefore(el, position);
29542
29543         this.el = Roo.get(el);
29544         this.eventEl = Roo.get(el.firstChild);
29545
29546         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
29547             handler: this.showPrevMonth,
29548             scope: this,
29549             preventDefault:true,
29550             stopDefault:true
29551         });
29552
29553         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
29554             handler: this.showNextMonth,
29555             scope: this,
29556             preventDefault:true,
29557             stopDefault:true
29558         });
29559
29560         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
29561
29562         this.monthPicker = this.el.down('div.x-date-mp');
29563         this.monthPicker.enableDisplayMode('block');
29564         
29565         var kn = new Roo.KeyNav(this.eventEl, {
29566             "left" : function(e){
29567                 e.ctrlKey ?
29568                     this.showPrevMonth() :
29569                     this.update(this.activeDate.add("d", -1));
29570             },
29571
29572             "right" : function(e){
29573                 e.ctrlKey ?
29574                     this.showNextMonth() :
29575                     this.update(this.activeDate.add("d", 1));
29576             },
29577
29578             "up" : function(e){
29579                 e.ctrlKey ?
29580                     this.showNextYear() :
29581                     this.update(this.activeDate.add("d", -7));
29582             },
29583
29584             "down" : function(e){
29585                 e.ctrlKey ?
29586                     this.showPrevYear() :
29587                     this.update(this.activeDate.add("d", 7));
29588             },
29589
29590             "pageUp" : function(e){
29591                 this.showNextMonth();
29592             },
29593
29594             "pageDown" : function(e){
29595                 this.showPrevMonth();
29596             },
29597
29598             "enter" : function(e){
29599                 e.stopPropagation();
29600                 return true;
29601             },
29602
29603             scope : this
29604         });
29605
29606         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
29607
29608         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
29609
29610         this.el.unselectable();
29611         
29612         this.cells = this.el.select("table.x-date-inner tbody td");
29613         this.textNodes = this.el.query("table.x-date-inner tbody span");
29614
29615         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
29616             text: "&#160;",
29617             tooltip: this.monthYearText
29618         });
29619
29620         this.mbtn.on('click', this.showMonthPicker, this);
29621         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
29622
29623
29624         var today = (new Date()).dateFormat(this.format);
29625         
29626         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
29627         if (this.showClear) {
29628             baseTb.add( new Roo.Toolbar.Fill());
29629         }
29630         baseTb.add({
29631             text: String.format(this.todayText, today),
29632             tooltip: String.format(this.todayTip, today),
29633             handler: this.selectToday,
29634             scope: this
29635         });
29636         
29637         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
29638             
29639         //});
29640         if (this.showClear) {
29641             
29642             baseTb.add( new Roo.Toolbar.Fill());
29643             baseTb.add({
29644                 text: '&#160;',
29645                 cls: 'x-btn-icon x-btn-clear',
29646                 handler: function() {
29647                     //this.value = '';
29648                     this.fireEvent("select", this, '');
29649                 },
29650                 scope: this
29651             });
29652         }
29653         
29654         
29655         if(Roo.isIE){
29656             this.el.repaint();
29657         }
29658         this.update(this.value);
29659     },
29660
29661     createMonthPicker : function(){
29662         if(!this.monthPicker.dom.firstChild){
29663             var buf = ['<table border="0" cellspacing="0">'];
29664             for(var i = 0; i < 6; i++){
29665                 buf.push(
29666                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
29667                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
29668                     i == 0 ?
29669                     '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :
29670                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
29671                 );
29672             }
29673             buf.push(
29674                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
29675                     this.okText,
29676                     '</button><button type="button" class="x-date-mp-cancel">',
29677                     this.cancelText,
29678                     '</button></td></tr>',
29679                 '</table>'
29680             );
29681             this.monthPicker.update(buf.join(''));
29682             this.monthPicker.on('click', this.onMonthClick, this);
29683             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
29684
29685             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
29686             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
29687
29688             this.mpMonths.each(function(m, a, i){
29689                 i += 1;
29690                 if((i%2) == 0){
29691                     m.dom.xmonth = 5 + Math.round(i * .5);
29692                 }else{
29693                     m.dom.xmonth = Math.round((i-1) * .5);
29694                 }
29695             });
29696         }
29697     },
29698
29699     showMonthPicker : function(){
29700         this.createMonthPicker();
29701         var size = this.el.getSize();
29702         this.monthPicker.setSize(size);
29703         this.monthPicker.child('table').setSize(size);
29704
29705         this.mpSelMonth = (this.activeDate || this.value).getMonth();
29706         this.updateMPMonth(this.mpSelMonth);
29707         this.mpSelYear = (this.activeDate || this.value).getFullYear();
29708         this.updateMPYear(this.mpSelYear);
29709
29710         this.monthPicker.slideIn('t', {duration:.2});
29711     },
29712
29713     updateMPYear : function(y){
29714         this.mpyear = y;
29715         var ys = this.mpYears.elements;
29716         for(var i = 1; i <= 10; i++){
29717             var td = ys[i-1], y2;
29718             if((i%2) == 0){
29719                 y2 = y + Math.round(i * .5);
29720                 td.firstChild.innerHTML = y2;
29721                 td.xyear = y2;
29722             }else{
29723                 y2 = y - (5-Math.round(i * .5));
29724                 td.firstChild.innerHTML = y2;
29725                 td.xyear = y2;
29726             }
29727             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
29728         }
29729     },
29730
29731     updateMPMonth : function(sm){
29732         this.mpMonths.each(function(m, a, i){
29733             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
29734         });
29735     },
29736
29737     selectMPMonth: function(m){
29738         
29739     },
29740
29741     onMonthClick : function(e, t){
29742         e.stopEvent();
29743         var el = new Roo.Element(t), pn;
29744         if(el.is('button.x-date-mp-cancel')){
29745             this.hideMonthPicker();
29746         }
29747         else if(el.is('button.x-date-mp-ok')){
29748             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29749             this.hideMonthPicker();
29750         }
29751         else if(pn = el.up('td.x-date-mp-month', 2)){
29752             this.mpMonths.removeClass('x-date-mp-sel');
29753             pn.addClass('x-date-mp-sel');
29754             this.mpSelMonth = pn.dom.xmonth;
29755         }
29756         else if(pn = el.up('td.x-date-mp-year', 2)){
29757             this.mpYears.removeClass('x-date-mp-sel');
29758             pn.addClass('x-date-mp-sel');
29759             this.mpSelYear = pn.dom.xyear;
29760         }
29761         else if(el.is('a.x-date-mp-prev')){
29762             this.updateMPYear(this.mpyear-10);
29763         }
29764         else if(el.is('a.x-date-mp-next')){
29765             this.updateMPYear(this.mpyear+10);
29766         }
29767     },
29768
29769     onMonthDblClick : function(e, t){
29770         e.stopEvent();
29771         var el = new Roo.Element(t), pn;
29772         if(pn = el.up('td.x-date-mp-month', 2)){
29773             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
29774             this.hideMonthPicker();
29775         }
29776         else if(pn = el.up('td.x-date-mp-year', 2)){
29777             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29778             this.hideMonthPicker();
29779         }
29780     },
29781
29782     hideMonthPicker : function(disableAnim){
29783         if(this.monthPicker){
29784             if(disableAnim === true){
29785                 this.monthPicker.hide();
29786             }else{
29787                 this.monthPicker.slideOut('t', {duration:.2});
29788             }
29789         }
29790     },
29791
29792     // private
29793     showPrevMonth : function(e){
29794         this.update(this.activeDate.add("mo", -1));
29795     },
29796
29797     // private
29798     showNextMonth : function(e){
29799         this.update(this.activeDate.add("mo", 1));
29800     },
29801
29802     // private
29803     showPrevYear : function(){
29804         this.update(this.activeDate.add("y", -1));
29805     },
29806
29807     // private
29808     showNextYear : function(){
29809         this.update(this.activeDate.add("y", 1));
29810     },
29811
29812     // private
29813     handleMouseWheel : function(e){
29814         var delta = e.getWheelDelta();
29815         if(delta > 0){
29816             this.showPrevMonth();
29817             e.stopEvent();
29818         } else if(delta < 0){
29819             this.showNextMonth();
29820             e.stopEvent();
29821         }
29822     },
29823
29824     // private
29825     handleDateClick : function(e, t){
29826         e.stopEvent();
29827         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
29828             this.setValue(new Date(t.dateValue));
29829             this.fireEvent("select", this, this.value);
29830         }
29831     },
29832
29833     // private
29834     selectToday : function(){
29835         this.setValue(new Date().clearTime());
29836         this.fireEvent("select", this, this.value);
29837     },
29838
29839     // private
29840     update : function(date)
29841     {
29842         var vd = this.activeDate;
29843         this.activeDate = date;
29844         if(vd && this.el){
29845             var t = date.getTime();
29846             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
29847                 this.cells.removeClass("x-date-selected");
29848                 this.cells.each(function(c){
29849                    if(c.dom.firstChild.dateValue == t){
29850                        c.addClass("x-date-selected");
29851                        setTimeout(function(){
29852                             try{c.dom.firstChild.focus();}catch(e){}
29853                        }, 50);
29854                        return false;
29855                    }
29856                 });
29857                 return;
29858             }
29859         }
29860         
29861         var days = date.getDaysInMonth();
29862         var firstOfMonth = date.getFirstDateOfMonth();
29863         var startingPos = firstOfMonth.getDay()-this.startDay;
29864
29865         if(startingPos <= this.startDay){
29866             startingPos += 7;
29867         }
29868
29869         var pm = date.add("mo", -1);
29870         var prevStart = pm.getDaysInMonth()-startingPos;
29871
29872         var cells = this.cells.elements;
29873         var textEls = this.textNodes;
29874         days += startingPos;
29875
29876         // convert everything to numbers so it's fast
29877         var day = 86400000;
29878         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
29879         var today = new Date().clearTime().getTime();
29880         var sel = date.clearTime().getTime();
29881         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
29882         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
29883         var ddMatch = this.disabledDatesRE;
29884         var ddText = this.disabledDatesText;
29885         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
29886         var ddaysText = this.disabledDaysText;
29887         var format = this.format;
29888
29889         var setCellClass = function(cal, cell){
29890             cell.title = "";
29891             var t = d.getTime();
29892             cell.firstChild.dateValue = t;
29893             if(t == today){
29894                 cell.className += " x-date-today";
29895                 cell.title = cal.todayText;
29896             }
29897             if(t == sel){
29898                 cell.className += " x-date-selected";
29899                 setTimeout(function(){
29900                     try{cell.firstChild.focus();}catch(e){}
29901                 }, 50);
29902             }
29903             // disabling
29904             if(t < min) {
29905                 cell.className = " x-date-disabled";
29906                 cell.title = cal.minText;
29907                 return;
29908             }
29909             if(t > max) {
29910                 cell.className = " x-date-disabled";
29911                 cell.title = cal.maxText;
29912                 return;
29913             }
29914             if(ddays){
29915                 if(ddays.indexOf(d.getDay()) != -1){
29916                     cell.title = ddaysText;
29917                     cell.className = " x-date-disabled";
29918                 }
29919             }
29920             if(ddMatch && format){
29921                 var fvalue = d.dateFormat(format);
29922                 if(ddMatch.test(fvalue)){
29923                     cell.title = ddText.replace("%0", fvalue);
29924                     cell.className = " x-date-disabled";
29925                 }
29926             }
29927         };
29928
29929         var i = 0;
29930         for(; i < startingPos; i++) {
29931             textEls[i].innerHTML = (++prevStart);
29932             d.setDate(d.getDate()+1);
29933             cells[i].className = "x-date-prevday";
29934             setCellClass(this, cells[i]);
29935         }
29936         for(; i < days; i++){
29937             intDay = i - startingPos + 1;
29938             textEls[i].innerHTML = (intDay);
29939             d.setDate(d.getDate()+1);
29940             cells[i].className = "x-date-active";
29941             setCellClass(this, cells[i]);
29942         }
29943         var extraDays = 0;
29944         for(; i < 42; i++) {
29945              textEls[i].innerHTML = (++extraDays);
29946              d.setDate(d.getDate()+1);
29947              cells[i].className = "x-date-nextday";
29948              setCellClass(this, cells[i]);
29949         }
29950
29951         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
29952         this.fireEvent('monthchange', this, date);
29953         
29954         if(!this.internalRender){
29955             var main = this.el.dom.firstChild;
29956             var w = main.offsetWidth;
29957             this.el.setWidth(w + this.el.getBorderWidth("lr"));
29958             Roo.fly(main).setWidth(w);
29959             this.internalRender = true;
29960             // opera does not respect the auto grow header center column
29961             // then, after it gets a width opera refuses to recalculate
29962             // without a second pass
29963             if(Roo.isOpera && !this.secondPass){
29964                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
29965                 this.secondPass = true;
29966                 this.update.defer(10, this, [date]);
29967             }
29968         }
29969         
29970         
29971     }
29972 });        /*
29973  * Based on:
29974  * Ext JS Library 1.1.1
29975  * Copyright(c) 2006-2007, Ext JS, LLC.
29976  *
29977  * Originally Released Under LGPL - original licence link has changed is not relivant.
29978  *
29979  * Fork - LGPL
29980  * <script type="text/javascript">
29981  */
29982 /**
29983  * @class Roo.TabPanel
29984  * @extends Roo.util.Observable
29985  * A lightweight tab container.
29986  * <br><br>
29987  * Usage:
29988  * <pre><code>
29989 // basic tabs 1, built from existing content
29990 var tabs = new Roo.TabPanel("tabs1");
29991 tabs.addTab("script", "View Script");
29992 tabs.addTab("markup", "View Markup");
29993 tabs.activate("script");
29994
29995 // more advanced tabs, built from javascript
29996 var jtabs = new Roo.TabPanel("jtabs");
29997 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
29998
29999 // set up the UpdateManager
30000 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
30001 var updater = tab2.getUpdateManager();
30002 updater.setDefaultUrl("ajax1.htm");
30003 tab2.on('activate', updater.refresh, updater, true);
30004
30005 // Use setUrl for Ajax loading
30006 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
30007 tab3.setUrl("ajax2.htm", null, true);
30008
30009 // Disabled tab
30010 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
30011 tab4.disable();
30012
30013 jtabs.activate("jtabs-1");
30014  * </code></pre>
30015  * @constructor
30016  * Create a new TabPanel.
30017  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
30018  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
30019  */
30020 Roo.TabPanel = function(container, config){
30021     /**
30022     * The container element for this TabPanel.
30023     * @type Roo.Element
30024     */
30025     this.el = Roo.get(container, true);
30026     if(config){
30027         if(typeof config == "boolean"){
30028             this.tabPosition = config ? "bottom" : "top";
30029         }else{
30030             Roo.apply(this, config);
30031         }
30032     }
30033     if(this.tabPosition == "bottom"){
30034         this.bodyEl = Roo.get(this.createBody(this.el.dom));
30035         this.el.addClass("x-tabs-bottom");
30036     }
30037     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
30038     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
30039     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
30040     if(Roo.isIE){
30041         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
30042     }
30043     if(this.tabPosition != "bottom"){
30044         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
30045          * @type Roo.Element
30046          */
30047         this.bodyEl = Roo.get(this.createBody(this.el.dom));
30048         this.el.addClass("x-tabs-top");
30049     }
30050     this.items = [];
30051
30052     this.bodyEl.setStyle("position", "relative");
30053
30054     this.active = null;
30055     this.activateDelegate = this.activate.createDelegate(this);
30056
30057     this.addEvents({
30058         /**
30059          * @event tabchange
30060          * Fires when the active tab changes
30061          * @param {Roo.TabPanel} this
30062          * @param {Roo.TabPanelItem} activePanel The new active tab
30063          */
30064         "tabchange": true,
30065         /**
30066          * @event beforetabchange
30067          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
30068          * @param {Roo.TabPanel} this
30069          * @param {Object} e Set cancel to true on this object to cancel the tab change
30070          * @param {Roo.TabPanelItem} tab The tab being changed to
30071          */
30072         "beforetabchange" : true
30073     });
30074
30075     Roo.EventManager.onWindowResize(this.onResize, this);
30076     this.cpad = this.el.getPadding("lr");
30077     this.hiddenCount = 0;
30078
30079
30080     // toolbar on the tabbar support...
30081     if (this.toolbar) {
30082         var tcfg = this.toolbar;
30083         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
30084         this.toolbar = new Roo.Toolbar(tcfg);
30085         if (Roo.isSafari) {
30086             var tbl = tcfg.container.child('table', true);
30087             tbl.setAttribute('width', '100%');
30088         }
30089         
30090     }
30091    
30092
30093
30094     Roo.TabPanel.superclass.constructor.call(this);
30095 };
30096
30097 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
30098     /*
30099      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
30100      */
30101     tabPosition : "top",
30102     /*
30103      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
30104      */
30105     currentTabWidth : 0,
30106     /*
30107      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
30108      */
30109     minTabWidth : 40,
30110     /*
30111      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
30112      */
30113     maxTabWidth : 250,
30114     /*
30115      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
30116      */
30117     preferredTabWidth : 175,
30118     /*
30119      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
30120      */
30121     resizeTabs : false,
30122     /*
30123      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
30124      */
30125     monitorResize : true,
30126     /*
30127      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
30128      */
30129     toolbar : false,
30130
30131     /**
30132      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
30133      * @param {String} id The id of the div to use <b>or create</b>
30134      * @param {String} text The text for the tab
30135      * @param {String} content (optional) Content to put in the TabPanelItem body
30136      * @param {Boolean} closable (optional) True to create a close icon on the tab
30137      * @return {Roo.TabPanelItem} The created TabPanelItem
30138      */
30139     addTab : function(id, text, content, closable){
30140         var item = new Roo.TabPanelItem(this, id, text, closable);
30141         this.addTabItem(item);
30142         if(content){
30143             item.setContent(content);
30144         }
30145         return item;
30146     },
30147
30148     /**
30149      * Returns the {@link Roo.TabPanelItem} with the specified id/index
30150      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
30151      * @return {Roo.TabPanelItem}
30152      */
30153     getTab : function(id){
30154         return this.items[id];
30155     },
30156
30157     /**
30158      * Hides the {@link Roo.TabPanelItem} with the specified id/index
30159      * @param {String/Number} id The id or index of the TabPanelItem to hide.
30160      */
30161     hideTab : function(id){
30162         var t = this.items[id];
30163         if(!t.isHidden()){
30164            t.setHidden(true);
30165            this.hiddenCount++;
30166            this.autoSizeTabs();
30167         }
30168     },
30169
30170     /**
30171      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
30172      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
30173      */
30174     unhideTab : function(id){
30175         var t = this.items[id];
30176         if(t.isHidden()){
30177            t.setHidden(false);
30178            this.hiddenCount--;
30179            this.autoSizeTabs();
30180         }
30181     },
30182
30183     /**
30184      * Adds an existing {@link Roo.TabPanelItem}.
30185      * @param {Roo.TabPanelItem} item The TabPanelItem to add
30186      */
30187     addTabItem : function(item){
30188         this.items[item.id] = item;
30189         this.items.push(item);
30190         if(this.resizeTabs){
30191            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
30192            this.autoSizeTabs();
30193         }else{
30194             item.autoSize();
30195         }
30196     },
30197
30198     /**
30199      * Removes a {@link Roo.TabPanelItem}.
30200      * @param {String/Number} id The id or index of the TabPanelItem to remove.
30201      */
30202     removeTab : function(id){
30203         var items = this.items;
30204         var tab = items[id];
30205         if(!tab) { return; }
30206         var index = items.indexOf(tab);
30207         if(this.active == tab && items.length > 1){
30208             var newTab = this.getNextAvailable(index);
30209             if(newTab) {
30210                 newTab.activate();
30211             }
30212         }
30213         this.stripEl.dom.removeChild(tab.pnode.dom);
30214         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
30215             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
30216         }
30217         items.splice(index, 1);
30218         delete this.items[tab.id];
30219         tab.fireEvent("close", tab);
30220         tab.purgeListeners();
30221         this.autoSizeTabs();
30222     },
30223
30224     getNextAvailable : function(start){
30225         var items = this.items;
30226         var index = start;
30227         // look for a next tab that will slide over to
30228         // replace the one being removed
30229         while(index < items.length){
30230             var item = items[++index];
30231             if(item && !item.isHidden()){
30232                 return item;
30233             }
30234         }
30235         // if one isn't found select the previous tab (on the left)
30236         index = start;
30237         while(index >= 0){
30238             var item = items[--index];
30239             if(item && !item.isHidden()){
30240                 return item;
30241             }
30242         }
30243         return null;
30244     },
30245
30246     /**
30247      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
30248      * @param {String/Number} id The id or index of the TabPanelItem to disable.
30249      */
30250     disableTab : function(id){
30251         var tab = this.items[id];
30252         if(tab && this.active != tab){
30253             tab.disable();
30254         }
30255     },
30256
30257     /**
30258      * Enables a {@link Roo.TabPanelItem} that is disabled.
30259      * @param {String/Number} id The id or index of the TabPanelItem to enable.
30260      */
30261     enableTab : function(id){
30262         var tab = this.items[id];
30263         tab.enable();
30264     },
30265
30266     /**
30267      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
30268      * @param {String/Number} id The id or index of the TabPanelItem to activate.
30269      * @return {Roo.TabPanelItem} The TabPanelItem.
30270      */
30271     activate : function(id){
30272         var tab = this.items[id];
30273         if(!tab){
30274             return null;
30275         }
30276         if(tab == this.active || tab.disabled){
30277             return tab;
30278         }
30279         var e = {};
30280         this.fireEvent("beforetabchange", this, e, tab);
30281         if(e.cancel !== true && !tab.disabled){
30282             if(this.active){
30283                 this.active.hide();
30284             }
30285             this.active = this.items[id];
30286             this.active.show();
30287             this.fireEvent("tabchange", this, this.active);
30288         }
30289         return tab;
30290     },
30291
30292     /**
30293      * Gets the active {@link Roo.TabPanelItem}.
30294      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
30295      */
30296     getActiveTab : function(){
30297         return this.active;
30298     },
30299
30300     /**
30301      * Updates the tab body element to fit the height of the container element
30302      * for overflow scrolling
30303      * @param {Number} targetHeight (optional) Override the starting height from the elements height
30304      */
30305     syncHeight : function(targetHeight){
30306         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
30307         var bm = this.bodyEl.getMargins();
30308         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
30309         this.bodyEl.setHeight(newHeight);
30310         return newHeight;
30311     },
30312
30313     onResize : function(){
30314         if(this.monitorResize){
30315             this.autoSizeTabs();
30316         }
30317     },
30318
30319     /**
30320      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
30321      */
30322     beginUpdate : function(){
30323         this.updating = true;
30324     },
30325
30326     /**
30327      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
30328      */
30329     endUpdate : function(){
30330         this.updating = false;
30331         this.autoSizeTabs();
30332     },
30333
30334     /**
30335      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
30336      */
30337     autoSizeTabs : function(){
30338         var count = this.items.length;
30339         var vcount = count - this.hiddenCount;
30340         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
30341             return;
30342         }
30343         var w = Math.max(this.el.getWidth() - this.cpad, 10);
30344         var availWidth = Math.floor(w / vcount);
30345         var b = this.stripBody;
30346         if(b.getWidth() > w){
30347             var tabs = this.items;
30348             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
30349             if(availWidth < this.minTabWidth){
30350                 /*if(!this.sleft){    // incomplete scrolling code
30351                     this.createScrollButtons();
30352                 }
30353                 this.showScroll();
30354                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
30355             }
30356         }else{
30357             if(this.currentTabWidth < this.preferredTabWidth){
30358                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
30359             }
30360         }
30361     },
30362
30363     /**
30364      * Returns the number of tabs in this TabPanel.
30365      * @return {Number}
30366      */
30367      getCount : function(){
30368          return this.items.length;
30369      },
30370
30371     /**
30372      * Resizes all the tabs to the passed width
30373      * @param {Number} The new width
30374      */
30375     setTabWidth : function(width){
30376         this.currentTabWidth = width;
30377         for(var i = 0, len = this.items.length; i < len; i++) {
30378                 if(!this.items[i].isHidden()) {
30379                 this.items[i].setWidth(width);
30380             }
30381         }
30382     },
30383
30384     /**
30385      * Destroys this TabPanel
30386      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
30387      */
30388     destroy : function(removeEl){
30389         Roo.EventManager.removeResizeListener(this.onResize, this);
30390         for(var i = 0, len = this.items.length; i < len; i++){
30391             this.items[i].purgeListeners();
30392         }
30393         if(removeEl === true){
30394             this.el.update("");
30395             this.el.remove();
30396         }
30397     }
30398 });
30399
30400 /**
30401  * @class Roo.TabPanelItem
30402  * @extends Roo.util.Observable
30403  * Represents an individual item (tab plus body) in a TabPanel.
30404  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
30405  * @param {String} id The id of this TabPanelItem
30406  * @param {String} text The text for the tab of this TabPanelItem
30407  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
30408  */
30409 Roo.TabPanelItem = function(tabPanel, id, text, closable){
30410     /**
30411      * The {@link Roo.TabPanel} this TabPanelItem belongs to
30412      * @type Roo.TabPanel
30413      */
30414     this.tabPanel = tabPanel;
30415     /**
30416      * The id for this TabPanelItem
30417      * @type String
30418      */
30419     this.id = id;
30420     /** @private */
30421     this.disabled = false;
30422     /** @private */
30423     this.text = text;
30424     /** @private */
30425     this.loaded = false;
30426     this.closable = closable;
30427
30428     /**
30429      * The body element for this TabPanelItem.
30430      * @type Roo.Element
30431      */
30432     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
30433     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
30434     this.bodyEl.setStyle("display", "block");
30435     this.bodyEl.setStyle("zoom", "1");
30436     this.hideAction();
30437
30438     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
30439     /** @private */
30440     this.el = Roo.get(els.el, true);
30441     this.inner = Roo.get(els.inner, true);
30442     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
30443     this.pnode = Roo.get(els.el.parentNode, true);
30444     this.el.on("mousedown", this.onTabMouseDown, this);
30445     this.el.on("click", this.onTabClick, this);
30446     /** @private */
30447     if(closable){
30448         var c = Roo.get(els.close, true);
30449         c.dom.title = this.closeText;
30450         c.addClassOnOver("close-over");
30451         c.on("click", this.closeClick, this);
30452      }
30453
30454     this.addEvents({
30455          /**
30456          * @event activate
30457          * Fires when this tab becomes the active tab.
30458          * @param {Roo.TabPanel} tabPanel The parent TabPanel
30459          * @param {Roo.TabPanelItem} this
30460          */
30461         "activate": true,
30462         /**
30463          * @event beforeclose
30464          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
30465          * @param {Roo.TabPanelItem} this
30466          * @param {Object} e Set cancel to true on this object to cancel the close.
30467          */
30468         "beforeclose": true,
30469         /**
30470          * @event close
30471          * Fires when this tab is closed.
30472          * @param {Roo.TabPanelItem} this
30473          */
30474          "close": true,
30475         /**
30476          * @event deactivate
30477          * Fires when this tab is no longer the active tab.
30478          * @param {Roo.TabPanel} tabPanel The parent TabPanel
30479          * @param {Roo.TabPanelItem} this
30480          */
30481          "deactivate" : true
30482     });
30483     this.hidden = false;
30484
30485     Roo.TabPanelItem.superclass.constructor.call(this);
30486 };
30487
30488 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
30489     purgeListeners : function(){
30490        Roo.util.Observable.prototype.purgeListeners.call(this);
30491        this.el.removeAllListeners();
30492     },
30493     /**
30494      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
30495      */
30496     show : function(){
30497         this.pnode.addClass("on");
30498         this.showAction();
30499         if(Roo.isOpera){
30500             this.tabPanel.stripWrap.repaint();
30501         }
30502         this.fireEvent("activate", this.tabPanel, this);
30503     },
30504
30505     /**
30506      * Returns true if this tab is the active tab.
30507      * @return {Boolean}
30508      */
30509     isActive : function(){
30510         return this.tabPanel.getActiveTab() == this;
30511     },
30512
30513     /**
30514      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
30515      */
30516     hide : function(){
30517         this.pnode.removeClass("on");
30518         this.hideAction();
30519         this.fireEvent("deactivate", this.tabPanel, this);
30520     },
30521
30522     hideAction : function(){
30523         this.bodyEl.hide();
30524         this.bodyEl.setStyle("position", "absolute");
30525         this.bodyEl.setLeft("-20000px");
30526         this.bodyEl.setTop("-20000px");
30527     },
30528
30529     showAction : function(){
30530         this.bodyEl.setStyle("position", "relative");
30531         this.bodyEl.setTop("");
30532         this.bodyEl.setLeft("");
30533         this.bodyEl.show();
30534     },
30535
30536     /**
30537      * Set the tooltip for the tab.
30538      * @param {String} tooltip The tab's tooltip
30539      */
30540     setTooltip : function(text){
30541         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
30542             this.textEl.dom.qtip = text;
30543             this.textEl.dom.removeAttribute('title');
30544         }else{
30545             this.textEl.dom.title = text;
30546         }
30547     },
30548
30549     onTabClick : function(e){
30550         e.preventDefault();
30551         this.tabPanel.activate(this.id);
30552     },
30553
30554     onTabMouseDown : function(e){
30555         e.preventDefault();
30556         this.tabPanel.activate(this.id);
30557     },
30558
30559     getWidth : function(){
30560         return this.inner.getWidth();
30561     },
30562
30563     setWidth : function(width){
30564         var iwidth = width - this.pnode.getPadding("lr");
30565         this.inner.setWidth(iwidth);
30566         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
30567         this.pnode.setWidth(width);
30568     },
30569
30570     /**
30571      * Show or hide the tab
30572      * @param {Boolean} hidden True to hide or false to show.
30573      */
30574     setHidden : function(hidden){
30575         this.hidden = hidden;
30576         this.pnode.setStyle("display", hidden ? "none" : "");
30577     },
30578
30579     /**
30580      * Returns true if this tab is "hidden"
30581      * @return {Boolean}
30582      */
30583     isHidden : function(){
30584         return this.hidden;
30585     },
30586
30587     /**
30588      * Returns the text for this tab
30589      * @return {String}
30590      */
30591     getText : function(){
30592         return this.text;
30593     },
30594
30595     autoSize : function(){
30596         //this.el.beginMeasure();
30597         this.textEl.setWidth(1);
30598         /*
30599          *  #2804 [new] Tabs in Roojs
30600          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
30601          */
30602         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
30603         //this.el.endMeasure();
30604     },
30605
30606     /**
30607      * Sets the text for the tab (Note: this also sets the tooltip text)
30608      * @param {String} text The tab's text and tooltip
30609      */
30610     setText : function(text){
30611         this.text = text;
30612         this.textEl.update(text);
30613         this.setTooltip(text);
30614         if(!this.tabPanel.resizeTabs){
30615             this.autoSize();
30616         }
30617     },
30618     /**
30619      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
30620      */
30621     activate : function(){
30622         this.tabPanel.activate(this.id);
30623     },
30624
30625     /**
30626      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
30627      */
30628     disable : function(){
30629         if(this.tabPanel.active != this){
30630             this.disabled = true;
30631             this.pnode.addClass("disabled");
30632         }
30633     },
30634
30635     /**
30636      * Enables this TabPanelItem if it was previously disabled.
30637      */
30638     enable : function(){
30639         this.disabled = false;
30640         this.pnode.removeClass("disabled");
30641     },
30642
30643     /**
30644      * Sets the content for this TabPanelItem.
30645      * @param {String} content The content
30646      * @param {Boolean} loadScripts true to look for and load scripts
30647      */
30648     setContent : function(content, loadScripts){
30649         this.bodyEl.update(content, loadScripts);
30650     },
30651
30652     /**
30653      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
30654      * @return {Roo.UpdateManager} The UpdateManager
30655      */
30656     getUpdateManager : function(){
30657         return this.bodyEl.getUpdateManager();
30658     },
30659
30660     /**
30661      * Set a URL to be used to load the content for this TabPanelItem.
30662      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
30663      * @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)
30664      * @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)
30665      * @return {Roo.UpdateManager} The UpdateManager
30666      */
30667     setUrl : function(url, params, loadOnce){
30668         if(this.refreshDelegate){
30669             this.un('activate', this.refreshDelegate);
30670         }
30671         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
30672         this.on("activate", this.refreshDelegate);
30673         return this.bodyEl.getUpdateManager();
30674     },
30675
30676     /** @private */
30677     _handleRefresh : function(url, params, loadOnce){
30678         if(!loadOnce || !this.loaded){
30679             var updater = this.bodyEl.getUpdateManager();
30680             updater.update(url, params, this._setLoaded.createDelegate(this));
30681         }
30682     },
30683
30684     /**
30685      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
30686      *   Will fail silently if the setUrl method has not been called.
30687      *   This does not activate the panel, just updates its content.
30688      */
30689     refresh : function(){
30690         if(this.refreshDelegate){
30691            this.loaded = false;
30692            this.refreshDelegate();
30693         }
30694     },
30695
30696     /** @private */
30697     _setLoaded : function(){
30698         this.loaded = true;
30699     },
30700
30701     /** @private */
30702     closeClick : function(e){
30703         var o = {};
30704         e.stopEvent();
30705         this.fireEvent("beforeclose", this, o);
30706         if(o.cancel !== true){
30707             this.tabPanel.removeTab(this.id);
30708         }
30709     },
30710     /**
30711      * The text displayed in the tooltip for the close icon.
30712      * @type String
30713      */
30714     closeText : "Close this tab"
30715 });
30716
30717 /** @private */
30718 Roo.TabPanel.prototype.createStrip = function(container){
30719     var strip = document.createElement("div");
30720     strip.className = "x-tabs-wrap";
30721     container.appendChild(strip);
30722     return strip;
30723 };
30724 /** @private */
30725 Roo.TabPanel.prototype.createStripList = function(strip){
30726     // div wrapper for retard IE
30727     // returns the "tr" element.
30728     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
30729         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
30730         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
30731     return strip.firstChild.firstChild.firstChild.firstChild;
30732 };
30733 /** @private */
30734 Roo.TabPanel.prototype.createBody = function(container){
30735     var body = document.createElement("div");
30736     Roo.id(body, "tab-body");
30737     Roo.fly(body).addClass("x-tabs-body");
30738     container.appendChild(body);
30739     return body;
30740 };
30741 /** @private */
30742 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
30743     var body = Roo.getDom(id);
30744     if(!body){
30745         body = document.createElement("div");
30746         body.id = id;
30747     }
30748     Roo.fly(body).addClass("x-tabs-item-body");
30749     bodyEl.insertBefore(body, bodyEl.firstChild);
30750     return body;
30751 };
30752 /** @private */
30753 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
30754     var td = document.createElement("td");
30755     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
30756     //stripEl.appendChild(td);
30757     if(closable){
30758         td.className = "x-tabs-closable";
30759         if(!this.closeTpl){
30760             this.closeTpl = new Roo.Template(
30761                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30762                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
30763                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
30764             );
30765         }
30766         var el = this.closeTpl.overwrite(td, {"text": text});
30767         var close = el.getElementsByTagName("div")[0];
30768         var inner = el.getElementsByTagName("em")[0];
30769         return {"el": el, "close": close, "inner": inner};
30770     } else {
30771         if(!this.tabTpl){
30772             this.tabTpl = new Roo.Template(
30773                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30774                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
30775             );
30776         }
30777         var el = this.tabTpl.overwrite(td, {"text": text});
30778         var inner = el.getElementsByTagName("em")[0];
30779         return {"el": el, "inner": inner};
30780     }
30781 };/*
30782  * Based on:
30783  * Ext JS Library 1.1.1
30784  * Copyright(c) 2006-2007, Ext JS, LLC.
30785  *
30786  * Originally Released Under LGPL - original licence link has changed is not relivant.
30787  *
30788  * Fork - LGPL
30789  * <script type="text/javascript">
30790  */
30791
30792 /**
30793  * @class Roo.Button
30794  * @extends Roo.util.Observable
30795  * Simple Button class
30796  * @cfg {String} text The button text
30797  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
30798  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
30799  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
30800  * @cfg {Object} scope The scope of the handler
30801  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
30802  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
30803  * @cfg {Boolean} hidden True to start hidden (defaults to false)
30804  * @cfg {Boolean} disabled True to start disabled (defaults to false)
30805  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
30806  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
30807    applies if enableToggle = true)
30808  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
30809  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
30810   an {@link Roo.util.ClickRepeater} config object (defaults to false).
30811  * @constructor
30812  * Create a new button
30813  * @param {Object} config The config object
30814  */
30815 Roo.Button = function(renderTo, config)
30816 {
30817     if (!config) {
30818         config = renderTo;
30819         renderTo = config.renderTo || false;
30820     }
30821     
30822     Roo.apply(this, config);
30823     this.addEvents({
30824         /**
30825              * @event click
30826              * Fires when this button is clicked
30827              * @param {Button} this
30828              * @param {EventObject} e The click event
30829              */
30830             "click" : true,
30831         /**
30832              * @event toggle
30833              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
30834              * @param {Button} this
30835              * @param {Boolean} pressed
30836              */
30837             "toggle" : true,
30838         /**
30839              * @event mouseover
30840              * Fires when the mouse hovers over the button
30841              * @param {Button} this
30842              * @param {Event} e The event object
30843              */
30844         'mouseover' : true,
30845         /**
30846              * @event mouseout
30847              * Fires when the mouse exits the button
30848              * @param {Button} this
30849              * @param {Event} e The event object
30850              */
30851         'mouseout': true,
30852          /**
30853              * @event render
30854              * Fires when the button is rendered
30855              * @param {Button} this
30856              */
30857         'render': true
30858     });
30859     if(this.menu){
30860         this.menu = Roo.menu.MenuMgr.get(this.menu);
30861     }
30862     // register listeners first!!  - so render can be captured..
30863     Roo.util.Observable.call(this);
30864     if(renderTo){
30865         this.render(renderTo);
30866     }
30867     
30868   
30869 };
30870
30871 Roo.extend(Roo.Button, Roo.util.Observable, {
30872     /**
30873      * 
30874      */
30875     
30876     /**
30877      * Read-only. True if this button is hidden
30878      * @type Boolean
30879      */
30880     hidden : false,
30881     /**
30882      * Read-only. True if this button is disabled
30883      * @type Boolean
30884      */
30885     disabled : false,
30886     /**
30887      * Read-only. True if this button is pressed (only if enableToggle = true)
30888      * @type Boolean
30889      */
30890     pressed : false,
30891
30892     /**
30893      * @cfg {Number} tabIndex 
30894      * The DOM tabIndex for this button (defaults to undefined)
30895      */
30896     tabIndex : undefined,
30897
30898     /**
30899      * @cfg {Boolean} enableToggle
30900      * True to enable pressed/not pressed toggling (defaults to false)
30901      */
30902     enableToggle: false,
30903     /**
30904      * @cfg {Roo.menu.Menu} menu
30905      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
30906      */
30907     menu : undefined,
30908     /**
30909      * @cfg {String} menuAlign
30910      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
30911      */
30912     menuAlign : "tl-bl?",
30913
30914     /**
30915      * @cfg {String} iconCls
30916      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
30917      */
30918     iconCls : undefined,
30919     /**
30920      * @cfg {String} type
30921      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
30922      */
30923     type : 'button',
30924
30925     // private
30926     menuClassTarget: 'tr',
30927
30928     /**
30929      * @cfg {String} clickEvent
30930      * The type of event to map to the button's event handler (defaults to 'click')
30931      */
30932     clickEvent : 'click',
30933
30934     /**
30935      * @cfg {Boolean} handleMouseEvents
30936      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
30937      */
30938     handleMouseEvents : true,
30939
30940     /**
30941      * @cfg {String} tooltipType
30942      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
30943      */
30944     tooltipType : 'qtip',
30945
30946     /**
30947      * @cfg {String} cls
30948      * A CSS class to apply to the button's main element.
30949      */
30950     
30951     /**
30952      * @cfg {Roo.Template} template (Optional)
30953      * An {@link Roo.Template} with which to create the Button's main element. This Template must
30954      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
30955      * require code modifications if required elements (e.g. a button) aren't present.
30956      */
30957
30958     // private
30959     render : function(renderTo){
30960         var btn;
30961         if(this.hideParent){
30962             this.parentEl = Roo.get(renderTo);
30963         }
30964         if(!this.dhconfig){
30965             if(!this.template){
30966                 if(!Roo.Button.buttonTemplate){
30967                     // hideous table template
30968                     Roo.Button.buttonTemplate = new Roo.Template(
30969                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
30970                         '<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>',
30971                         "</tr></tbody></table>");
30972                 }
30973                 this.template = Roo.Button.buttonTemplate;
30974             }
30975             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
30976             var btnEl = btn.child("button:first");
30977             btnEl.on('focus', this.onFocus, this);
30978             btnEl.on('blur', this.onBlur, this);
30979             if(this.cls){
30980                 btn.addClass(this.cls);
30981             }
30982             if(this.icon){
30983                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
30984             }
30985             if(this.iconCls){
30986                 btnEl.addClass(this.iconCls);
30987                 if(!this.cls){
30988                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
30989                 }
30990             }
30991             if(this.tabIndex !== undefined){
30992                 btnEl.dom.tabIndex = this.tabIndex;
30993             }
30994             if(this.tooltip){
30995                 if(typeof this.tooltip == 'object'){
30996                     Roo.QuickTips.tips(Roo.apply({
30997                           target: btnEl.id
30998                     }, this.tooltip));
30999                 } else {
31000                     btnEl.dom[this.tooltipType] = this.tooltip;
31001                 }
31002             }
31003         }else{
31004             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
31005         }
31006         this.el = btn;
31007         if(this.id){
31008             this.el.dom.id = this.el.id = this.id;
31009         }
31010         if(this.menu){
31011             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
31012             this.menu.on("show", this.onMenuShow, this);
31013             this.menu.on("hide", this.onMenuHide, this);
31014         }
31015         btn.addClass("x-btn");
31016         if(Roo.isIE && !Roo.isIE7){
31017             this.autoWidth.defer(1, this);
31018         }else{
31019             this.autoWidth();
31020         }
31021         if(this.handleMouseEvents){
31022             btn.on("mouseover", this.onMouseOver, this);
31023             btn.on("mouseout", this.onMouseOut, this);
31024             btn.on("mousedown", this.onMouseDown, this);
31025         }
31026         btn.on(this.clickEvent, this.onClick, this);
31027         //btn.on("mouseup", this.onMouseUp, this);
31028         if(this.hidden){
31029             this.hide();
31030         }
31031         if(this.disabled){
31032             this.disable();
31033         }
31034         Roo.ButtonToggleMgr.register(this);
31035         if(this.pressed){
31036             this.el.addClass("x-btn-pressed");
31037         }
31038         if(this.repeat){
31039             var repeater = new Roo.util.ClickRepeater(btn,
31040                 typeof this.repeat == "object" ? this.repeat : {}
31041             );
31042             repeater.on("click", this.onClick,  this);
31043         }
31044         
31045         this.fireEvent('render', this);
31046         
31047     },
31048     /**
31049      * Returns the button's underlying element
31050      * @return {Roo.Element} The element
31051      */
31052     getEl : function(){
31053         return this.el;  
31054     },
31055     
31056     /**
31057      * Destroys this Button and removes any listeners.
31058      */
31059     destroy : function(){
31060         Roo.ButtonToggleMgr.unregister(this);
31061         this.el.removeAllListeners();
31062         this.purgeListeners();
31063         this.el.remove();
31064     },
31065
31066     // private
31067     autoWidth : function(){
31068         if(this.el){
31069             this.el.setWidth("auto");
31070             if(Roo.isIE7 && Roo.isStrict){
31071                 var ib = this.el.child('button');
31072                 if(ib && ib.getWidth() > 20){
31073                     ib.clip();
31074                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
31075                 }
31076             }
31077             if(this.minWidth){
31078                 if(this.hidden){
31079                     this.el.beginMeasure();
31080                 }
31081                 if(this.el.getWidth() < this.minWidth){
31082                     this.el.setWidth(this.minWidth);
31083                 }
31084                 if(this.hidden){
31085                     this.el.endMeasure();
31086                 }
31087             }
31088         }
31089     },
31090
31091     /**
31092      * Assigns this button's click handler
31093      * @param {Function} handler The function to call when the button is clicked
31094      * @param {Object} scope (optional) Scope for the function passed in
31095      */
31096     setHandler : function(handler, scope){
31097         this.handler = handler;
31098         this.scope = scope;  
31099     },
31100     
31101     /**
31102      * Sets this button's text
31103      * @param {String} text The button text
31104      */
31105     setText : function(text){
31106         this.text = text;
31107         if(this.el){
31108             this.el.child("td.x-btn-center button.x-btn-text").update(text);
31109         }
31110         this.autoWidth();
31111     },
31112     
31113     /**
31114      * Gets the text for this button
31115      * @return {String} The button text
31116      */
31117     getText : function(){
31118         return this.text;  
31119     },
31120     
31121     /**
31122      * Show this button
31123      */
31124     show: function(){
31125         this.hidden = false;
31126         if(this.el){
31127             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
31128         }
31129     },
31130     
31131     /**
31132      * Hide this button
31133      */
31134     hide: function(){
31135         this.hidden = true;
31136         if(this.el){
31137             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
31138         }
31139     },
31140     
31141     /**
31142      * Convenience function for boolean show/hide
31143      * @param {Boolean} visible True to show, false to hide
31144      */
31145     setVisible: function(visible){
31146         if(visible) {
31147             this.show();
31148         }else{
31149             this.hide();
31150         }
31151     },
31152     /**
31153          * Similar to toggle, but does not trigger event.
31154          * @param {Boolean} state [required] Force a particular state
31155          */
31156         setPressed : function(state)
31157         {
31158             if(state != this.pressed){
31159             if(state){
31160                 this.el.addClass("x-btn-pressed");
31161                 this.pressed = true;
31162             }else{
31163                 this.el.removeClass("x-btn-pressed");
31164                 this.pressed = false;
31165             }
31166         }
31167         },
31168         
31169     /**
31170      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
31171      * @param {Boolean} state (optional) Force a particular state
31172      */
31173     toggle : function(state){
31174         state = state === undefined ? !this.pressed : state;
31175         if(state != this.pressed){
31176             if(state){
31177                 this.el.addClass("x-btn-pressed");
31178                 this.pressed = true;
31179                 this.fireEvent("toggle", this, true);
31180             }else{
31181                 this.el.removeClass("x-btn-pressed");
31182                 this.pressed = false;
31183                 this.fireEvent("toggle", this, false);
31184             }
31185             if(this.toggleHandler){
31186                 this.toggleHandler.call(this.scope || this, this, state);
31187             }
31188         }
31189     },
31190     
31191         
31192         
31193     /**
31194      * Focus the button
31195      */
31196     focus : function(){
31197         this.el.child('button:first').focus();
31198     },
31199     
31200     /**
31201      * Disable this button
31202      */
31203     disable : function(){
31204         if(this.el){
31205             this.el.addClass("x-btn-disabled");
31206         }
31207         this.disabled = true;
31208     },
31209     
31210     /**
31211      * Enable this button
31212      */
31213     enable : function(){
31214         if(this.el){
31215             this.el.removeClass("x-btn-disabled");
31216         }
31217         this.disabled = false;
31218     },
31219
31220     /**
31221      * Convenience function for boolean enable/disable
31222      * @param {Boolean} enabled True to enable, false to disable
31223      */
31224     setDisabled : function(v){
31225         this[v !== true ? "enable" : "disable"]();
31226     },
31227
31228     // private
31229     onClick : function(e)
31230     {
31231         if(e){
31232             e.preventDefault();
31233         }
31234         if(e.button != 0){
31235             return;
31236         }
31237         if(!this.disabled){
31238             if(this.enableToggle){
31239                 this.toggle();
31240             }
31241             if(this.menu && !this.menu.isVisible()){
31242                 this.menu.show(this.el, this.menuAlign);
31243             }
31244             this.fireEvent("click", this, e);
31245             if(this.handler){
31246                 this.el.removeClass("x-btn-over");
31247                 this.handler.call(this.scope || this, this, e);
31248             }
31249         }
31250     },
31251     // private
31252     onMouseOver : function(e){
31253         if(!this.disabled){
31254             this.el.addClass("x-btn-over");
31255             this.fireEvent('mouseover', this, e);
31256         }
31257     },
31258     // private
31259     onMouseOut : function(e){
31260         if(!e.within(this.el,  true)){
31261             this.el.removeClass("x-btn-over");
31262             this.fireEvent('mouseout', this, e);
31263         }
31264     },
31265     // private
31266     onFocus : function(e){
31267         if(!this.disabled){
31268             this.el.addClass("x-btn-focus");
31269         }
31270     },
31271     // private
31272     onBlur : function(e){
31273         this.el.removeClass("x-btn-focus");
31274     },
31275     // private
31276     onMouseDown : function(e){
31277         if(!this.disabled && e.button == 0){
31278             this.el.addClass("x-btn-click");
31279             Roo.get(document).on('mouseup', this.onMouseUp, this);
31280         }
31281     },
31282     // private
31283     onMouseUp : function(e){
31284         if(e.button == 0){
31285             this.el.removeClass("x-btn-click");
31286             Roo.get(document).un('mouseup', this.onMouseUp, this);
31287         }
31288     },
31289     // private
31290     onMenuShow : function(e){
31291         this.el.addClass("x-btn-menu-active");
31292     },
31293     // private
31294     onMenuHide : function(e){
31295         this.el.removeClass("x-btn-menu-active");
31296     }   
31297 });
31298
31299 // Private utility class used by Button
31300 Roo.ButtonToggleMgr = function(){
31301    var groups = {};
31302    
31303    function toggleGroup(btn, state){
31304        if(state){
31305            var g = groups[btn.toggleGroup];
31306            for(var i = 0, l = g.length; i < l; i++){
31307                if(g[i] != btn){
31308                    g[i].toggle(false);
31309                }
31310            }
31311        }
31312    }
31313    
31314    return {
31315        register : function(btn){
31316            if(!btn.toggleGroup){
31317                return;
31318            }
31319            var g = groups[btn.toggleGroup];
31320            if(!g){
31321                g = groups[btn.toggleGroup] = [];
31322            }
31323            g.push(btn);
31324            btn.on("toggle", toggleGroup);
31325        },
31326        
31327        unregister : function(btn){
31328            if(!btn.toggleGroup){
31329                return;
31330            }
31331            var g = groups[btn.toggleGroup];
31332            if(g){
31333                g.remove(btn);
31334                btn.un("toggle", toggleGroup);
31335            }
31336        }
31337    };
31338 }();/*
31339  * Based on:
31340  * Ext JS Library 1.1.1
31341  * Copyright(c) 2006-2007, Ext JS, LLC.
31342  *
31343  * Originally Released Under LGPL - original licence link has changed is not relivant.
31344  *
31345  * Fork - LGPL
31346  * <script type="text/javascript">
31347  */
31348  
31349 /**
31350  * @class Roo.SplitButton
31351  * @extends Roo.Button
31352  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
31353  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
31354  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
31355  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
31356  * @cfg {String} arrowTooltip The title attribute of the arrow
31357  * @constructor
31358  * Create a new menu button
31359  * @param {String/HTMLElement/Element} renderTo The element to append the button to
31360  * @param {Object} config The config object
31361  */
31362 Roo.SplitButton = function(renderTo, config){
31363     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
31364     /**
31365      * @event arrowclick
31366      * Fires when this button's arrow is clicked
31367      * @param {SplitButton} this
31368      * @param {EventObject} e The click event
31369      */
31370     this.addEvents({"arrowclick":true});
31371 };
31372
31373 Roo.extend(Roo.SplitButton, Roo.Button, {
31374     render : function(renderTo){
31375         // this is one sweet looking template!
31376         var tpl = new Roo.Template(
31377             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
31378             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
31379             '<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>',
31380             "</tbody></table></td><td>",
31381             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
31382             '<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>',
31383             "</tbody></table></td></tr></table>"
31384         );
31385         var btn = tpl.append(renderTo, [this.text, this.type], true);
31386         var btnEl = btn.child("button");
31387         if(this.cls){
31388             btn.addClass(this.cls);
31389         }
31390         if(this.icon){
31391             btnEl.setStyle('background-image', 'url(' +this.icon +')');
31392         }
31393         if(this.iconCls){
31394             btnEl.addClass(this.iconCls);
31395             if(!this.cls){
31396                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
31397             }
31398         }
31399         this.el = btn;
31400         if(this.handleMouseEvents){
31401             btn.on("mouseover", this.onMouseOver, this);
31402             btn.on("mouseout", this.onMouseOut, this);
31403             btn.on("mousedown", this.onMouseDown, this);
31404             btn.on("mouseup", this.onMouseUp, this);
31405         }
31406         btn.on(this.clickEvent, this.onClick, this);
31407         if(this.tooltip){
31408             if(typeof this.tooltip == 'object'){
31409                 Roo.QuickTips.tips(Roo.apply({
31410                       target: btnEl.id
31411                 }, this.tooltip));
31412             } else {
31413                 btnEl.dom[this.tooltipType] = this.tooltip;
31414             }
31415         }
31416         if(this.arrowTooltip){
31417             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
31418         }
31419         if(this.hidden){
31420             this.hide();
31421         }
31422         if(this.disabled){
31423             this.disable();
31424         }
31425         if(this.pressed){
31426             this.el.addClass("x-btn-pressed");
31427         }
31428         if(Roo.isIE && !Roo.isIE7){
31429             this.autoWidth.defer(1, this);
31430         }else{
31431             this.autoWidth();
31432         }
31433         if(this.menu){
31434             this.menu.on("show", this.onMenuShow, this);
31435             this.menu.on("hide", this.onMenuHide, this);
31436         }
31437         this.fireEvent('render', this);
31438     },
31439
31440     // private
31441     autoWidth : function(){
31442         if(this.el){
31443             var tbl = this.el.child("table:first");
31444             var tbl2 = this.el.child("table:last");
31445             this.el.setWidth("auto");
31446             tbl.setWidth("auto");
31447             if(Roo.isIE7 && Roo.isStrict){
31448                 var ib = this.el.child('button:first');
31449                 if(ib && ib.getWidth() > 20){
31450                     ib.clip();
31451                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
31452                 }
31453             }
31454             if(this.minWidth){
31455                 if(this.hidden){
31456                     this.el.beginMeasure();
31457                 }
31458                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
31459                     tbl.setWidth(this.minWidth-tbl2.getWidth());
31460                 }
31461                 if(this.hidden){
31462                     this.el.endMeasure();
31463                 }
31464             }
31465             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
31466         } 
31467     },
31468     /**
31469      * Sets this button's click handler
31470      * @param {Function} handler The function to call when the button is clicked
31471      * @param {Object} scope (optional) Scope for the function passed above
31472      */
31473     setHandler : function(handler, scope){
31474         this.handler = handler;
31475         this.scope = scope;  
31476     },
31477     
31478     /**
31479      * Sets this button's arrow click handler
31480      * @param {Function} handler The function to call when the arrow is clicked
31481      * @param {Object} scope (optional) Scope for the function passed above
31482      */
31483     setArrowHandler : function(handler, scope){
31484         this.arrowHandler = handler;
31485         this.scope = scope;  
31486     },
31487     
31488     /**
31489      * Focus the button
31490      */
31491     focus : function(){
31492         if(this.el){
31493             this.el.child("button:first").focus();
31494         }
31495     },
31496
31497     // private
31498     onClick : function(e){
31499         e.preventDefault();
31500         if(!this.disabled){
31501             if(e.getTarget(".x-btn-menu-arrow-wrap")){
31502                 if(this.menu && !this.menu.isVisible()){
31503                     this.menu.show(this.el, this.menuAlign);
31504                 }
31505                 this.fireEvent("arrowclick", this, e);
31506                 if(this.arrowHandler){
31507                     this.arrowHandler.call(this.scope || this, this, e);
31508                 }
31509             }else{
31510                 this.fireEvent("click", this, e);
31511                 if(this.handler){
31512                     this.handler.call(this.scope || this, this, e);
31513                 }
31514             }
31515         }
31516     },
31517     // private
31518     onMouseDown : function(e){
31519         if(!this.disabled){
31520             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
31521         }
31522     },
31523     // private
31524     onMouseUp : function(e){
31525         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
31526     }   
31527 });
31528
31529
31530 // backwards compat
31531 Roo.MenuButton = Roo.SplitButton;/*
31532  * Based on:
31533  * Ext JS Library 1.1.1
31534  * Copyright(c) 2006-2007, Ext JS, LLC.
31535  *
31536  * Originally Released Under LGPL - original licence link has changed is not relivant.
31537  *
31538  * Fork - LGPL
31539  * <script type="text/javascript">
31540  */
31541
31542 /**
31543  * @class Roo.Toolbar
31544  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field 
31545  * Basic Toolbar class.
31546  * @constructor
31547  * Creates a new Toolbar
31548  * @param {Object} container The config object
31549  */ 
31550 Roo.Toolbar = function(container, buttons, config)
31551 {
31552     /// old consturctor format still supported..
31553     if(container instanceof Array){ // omit the container for later rendering
31554         buttons = container;
31555         config = buttons;
31556         container = null;
31557     }
31558     if (typeof(container) == 'object' && container.xtype) {
31559         config = container;
31560         container = config.container;
31561         buttons = config.buttons || []; // not really - use items!!
31562     }
31563     var xitems = [];
31564     if (config && config.items) {
31565         xitems = config.items;
31566         delete config.items;
31567     }
31568     Roo.apply(this, config);
31569     this.buttons = buttons;
31570     
31571     if(container){
31572         this.render(container);
31573     }
31574     this.xitems = xitems;
31575     Roo.each(xitems, function(b) {
31576         this.add(b);
31577     }, this);
31578     
31579 };
31580
31581 Roo.Toolbar.prototype = {
31582     /**
31583      * @cfg {Array} items
31584      * array of button configs or elements to add (will be converted to a MixedCollection)
31585      */
31586     items: false,
31587     /**
31588      * @cfg {String/HTMLElement/Element} container
31589      * The id or element that will contain the toolbar
31590      */
31591     // private
31592     render : function(ct){
31593         this.el = Roo.get(ct);
31594         if(this.cls){
31595             this.el.addClass(this.cls);
31596         }
31597         // using a table allows for vertical alignment
31598         // 100% width is needed by Safari...
31599         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
31600         this.tr = this.el.child("tr", true);
31601         var autoId = 0;
31602         this.items = new Roo.util.MixedCollection(false, function(o){
31603             return o.id || ("item" + (++autoId));
31604         });
31605         if(this.buttons){
31606             this.add.apply(this, this.buttons);
31607             delete this.buttons;
31608         }
31609     },
31610
31611     /**
31612      * Adds element(s) to the toolbar -- this function takes a variable number of 
31613      * arguments of mixed type and adds them to the toolbar.
31614      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
31615      * <ul>
31616      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
31617      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
31618      * <li>Field: Any form field (equivalent to {@link #addField})</li>
31619      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
31620      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
31621      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
31622      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
31623      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
31624      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
31625      * </ul>
31626      * @param {Mixed} arg2
31627      * @param {Mixed} etc.
31628      */
31629     add : function(){
31630         var a = arguments, l = a.length;
31631         for(var i = 0; i < l; i++){
31632             this._add(a[i]);
31633         }
31634     },
31635     // private..
31636     _add : function(el) {
31637         
31638         if (el.xtype) {
31639             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
31640         }
31641         
31642         if (el.applyTo){ // some kind of form field
31643             return this.addField(el);
31644         } 
31645         if (el.render){ // some kind of Toolbar.Item
31646             return this.addItem(el);
31647         }
31648         if (typeof el == "string"){ // string
31649             if(el == "separator" || el == "-"){
31650                 return this.addSeparator();
31651             }
31652             if (el == " "){
31653                 return this.addSpacer();
31654             }
31655             if(el == "->"){
31656                 return this.addFill();
31657             }
31658             return this.addText(el);
31659             
31660         }
31661         if(el.tagName){ // element
31662             return this.addElement(el);
31663         }
31664         if(typeof el == "object"){ // must be button config?
31665             return this.addButton(el);
31666         }
31667         // and now what?!?!
31668         return false;
31669         
31670     },
31671     
31672     /**
31673      * Add an Xtype element
31674      * @param {Object} xtype Xtype Object
31675      * @return {Object} created Object
31676      */
31677     addxtype : function(e){
31678         return this.add(e);  
31679     },
31680     
31681     /**
31682      * Returns the Element for this toolbar.
31683      * @return {Roo.Element}
31684      */
31685     getEl : function(){
31686         return this.el;  
31687     },
31688     
31689     /**
31690      * Adds a separator
31691      * @return {Roo.Toolbar.Item} The separator item
31692      */
31693     addSeparator : function(){
31694         return this.addItem(new Roo.Toolbar.Separator());
31695     },
31696
31697     /**
31698      * Adds a spacer element
31699      * @return {Roo.Toolbar.Spacer} The spacer item
31700      */
31701     addSpacer : function(){
31702         return this.addItem(new Roo.Toolbar.Spacer());
31703     },
31704
31705     /**
31706      * Adds a fill element that forces subsequent additions to the right side of the toolbar
31707      * @return {Roo.Toolbar.Fill} The fill item
31708      */
31709     addFill : function(){
31710         return this.addItem(new Roo.Toolbar.Fill());
31711     },
31712
31713     /**
31714      * Adds any standard HTML element to the toolbar
31715      * @param {String/HTMLElement/Element} el The element or id of the element to add
31716      * @return {Roo.Toolbar.Item} The element's item
31717      */
31718     addElement : function(el){
31719         return this.addItem(new Roo.Toolbar.Item(el));
31720     },
31721     /**
31722      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
31723      * @type Roo.util.MixedCollection  
31724      */
31725     items : false,
31726      
31727     /**
31728      * Adds any Toolbar.Item or subclass
31729      * @param {Roo.Toolbar.Item} item
31730      * @return {Roo.Toolbar.Item} The item
31731      */
31732     addItem : function(item){
31733         var td = this.nextBlock();
31734         item.render(td);
31735         this.items.add(item);
31736         return item;
31737     },
31738     
31739     /**
31740      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
31741      * @param {Object/Array} config A button config or array of configs
31742      * @return {Roo.Toolbar.Button/Array}
31743      */
31744     addButton : function(config){
31745         if(config instanceof Array){
31746             var buttons = [];
31747             for(var i = 0, len = config.length; i < len; i++) {
31748                 buttons.push(this.addButton(config[i]));
31749             }
31750             return buttons;
31751         }
31752         var b = config;
31753         if(!(config instanceof Roo.Toolbar.Button)){
31754             b = config.split ?
31755                 new Roo.Toolbar.SplitButton(config) :
31756                 new Roo.Toolbar.Button(config);
31757         }
31758         var td = this.nextBlock();
31759         b.render(td);
31760         this.items.add(b);
31761         return b;
31762     },
31763     
31764     /**
31765      * Adds text to the toolbar
31766      * @param {String} text The text to add
31767      * @return {Roo.Toolbar.Item} The element's item
31768      */
31769     addText : function(text){
31770         return this.addItem(new Roo.Toolbar.TextItem(text));
31771     },
31772     
31773     /**
31774      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
31775      * @param {Number} index The index where the item is to be inserted
31776      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
31777      * @return {Roo.Toolbar.Button/Item}
31778      */
31779     insertButton : function(index, item){
31780         if(item instanceof Array){
31781             var buttons = [];
31782             for(var i = 0, len = item.length; i < len; i++) {
31783                buttons.push(this.insertButton(index + i, item[i]));
31784             }
31785             return buttons;
31786         }
31787         if (!(item instanceof Roo.Toolbar.Button)){
31788            item = new Roo.Toolbar.Button(item);
31789         }
31790         var td = document.createElement("td");
31791         this.tr.insertBefore(td, this.tr.childNodes[index]);
31792         item.render(td);
31793         this.items.insert(index, item);
31794         return item;
31795     },
31796     
31797     /**
31798      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
31799      * @param {Object} config
31800      * @return {Roo.Toolbar.Item} The element's item
31801      */
31802     addDom : function(config, returnEl){
31803         var td = this.nextBlock();
31804         Roo.DomHelper.overwrite(td, config);
31805         var ti = new Roo.Toolbar.Item(td.firstChild);
31806         ti.render(td);
31807         this.items.add(ti);
31808         return ti;
31809     },
31810
31811     /**
31812      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
31813      * @type Roo.util.MixedCollection  
31814      */
31815     fields : false,
31816     
31817     /**
31818      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
31819      * Note: the field should not have been rendered yet. For a field that has already been
31820      * rendered, use {@link #addElement}.
31821      * @param {Roo.form.Field} field
31822      * @return {Roo.ToolbarItem}
31823      */
31824      
31825       
31826     addField : function(field) {
31827         if (!this.fields) {
31828             var autoId = 0;
31829             this.fields = new Roo.util.MixedCollection(false, function(o){
31830                 return o.id || ("item" + (++autoId));
31831             });
31832
31833         }
31834         
31835         var td = this.nextBlock();
31836         field.render(td);
31837         var ti = new Roo.Toolbar.Item(td.firstChild);
31838         ti.render(td);
31839         this.items.add(ti);
31840         this.fields.add(field);
31841         return ti;
31842     },
31843     /**
31844      * Hide the toolbar
31845      * @method hide
31846      */
31847      
31848       
31849     hide : function()
31850     {
31851         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
31852         this.el.child('div').hide();
31853     },
31854     /**
31855      * Show the toolbar
31856      * @method show
31857      */
31858     show : function()
31859     {
31860         this.el.child('div').show();
31861     },
31862       
31863     // private
31864     nextBlock : function(){
31865         var td = document.createElement("td");
31866         this.tr.appendChild(td);
31867         return td;
31868     },
31869
31870     // private
31871     destroy : function(){
31872         if(this.items){ // rendered?
31873             Roo.destroy.apply(Roo, this.items.items);
31874         }
31875         if(this.fields){ // rendered?
31876             Roo.destroy.apply(Roo, this.fields.items);
31877         }
31878         Roo.Element.uncache(this.el, this.tr);
31879     }
31880 };
31881
31882 /**
31883  * @class Roo.Toolbar.Item
31884  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
31885  * @constructor
31886  * Creates a new Item
31887  * @param {HTMLElement} el 
31888  */
31889 Roo.Toolbar.Item = function(el){
31890     var cfg = {};
31891     if (typeof (el.xtype) != 'undefined') {
31892         cfg = el;
31893         el = cfg.el;
31894     }
31895     
31896     this.el = Roo.getDom(el);
31897     this.id = Roo.id(this.el);
31898     this.hidden = false;
31899     
31900     this.addEvents({
31901          /**
31902              * @event render
31903              * Fires when the button is rendered
31904              * @param {Button} this
31905              */
31906         'render': true
31907     });
31908     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
31909 };
31910 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
31911 //Roo.Toolbar.Item.prototype = {
31912     
31913     /**
31914      * Get this item's HTML Element
31915      * @return {HTMLElement}
31916      */
31917     getEl : function(){
31918        return this.el;  
31919     },
31920
31921     // private
31922     render : function(td){
31923         
31924          this.td = td;
31925         td.appendChild(this.el);
31926         
31927         this.fireEvent('render', this);
31928     },
31929     
31930     /**
31931      * Removes and destroys this item.
31932      */
31933     destroy : function(){
31934         this.td.parentNode.removeChild(this.td);
31935     },
31936     
31937     /**
31938      * Shows this item.
31939      */
31940     show: function(){
31941         this.hidden = false;
31942         this.td.style.display = "";
31943     },
31944     
31945     /**
31946      * Hides this item.
31947      */
31948     hide: function(){
31949         this.hidden = true;
31950         this.td.style.display = "none";
31951     },
31952     
31953     /**
31954      * Convenience function for boolean show/hide.
31955      * @param {Boolean} visible true to show/false to hide
31956      */
31957     setVisible: function(visible){
31958         if(visible) {
31959             this.show();
31960         }else{
31961             this.hide();
31962         }
31963     },
31964     
31965     /**
31966      * Try to focus this item.
31967      */
31968     focus : function(){
31969         Roo.fly(this.el).focus();
31970     },
31971     
31972     /**
31973      * Disables this item.
31974      */
31975     disable : function(){
31976         Roo.fly(this.td).addClass("x-item-disabled");
31977         this.disabled = true;
31978         this.el.disabled = true;
31979     },
31980     
31981     /**
31982      * Enables this item.
31983      */
31984     enable : function(){
31985         Roo.fly(this.td).removeClass("x-item-disabled");
31986         this.disabled = false;
31987         this.el.disabled = false;
31988     }
31989 });
31990
31991
31992 /**
31993  * @class Roo.Toolbar.Separator
31994  * @extends Roo.Toolbar.Item
31995  * A simple toolbar separator class
31996  * @constructor
31997  * Creates a new Separator
31998  */
31999 Roo.Toolbar.Separator = function(cfg){
32000     
32001     var s = document.createElement("span");
32002     s.className = "ytb-sep";
32003     if (cfg) {
32004         cfg.el = s;
32005     }
32006     
32007     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
32008 };
32009 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
32010     enable:Roo.emptyFn,
32011     disable:Roo.emptyFn,
32012     focus:Roo.emptyFn
32013 });
32014
32015 /**
32016  * @class Roo.Toolbar.Spacer
32017  * @extends Roo.Toolbar.Item
32018  * A simple element that adds extra horizontal space to a toolbar.
32019  * @constructor
32020  * Creates a new Spacer
32021  */
32022 Roo.Toolbar.Spacer = function(cfg){
32023     var s = document.createElement("div");
32024     s.className = "ytb-spacer";
32025     if (cfg) {
32026         cfg.el = s;
32027     }
32028     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
32029 };
32030 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
32031     enable:Roo.emptyFn,
32032     disable:Roo.emptyFn,
32033     focus:Roo.emptyFn
32034 });
32035
32036 /**
32037  * @class Roo.Toolbar.Fill
32038  * @extends Roo.Toolbar.Spacer
32039  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
32040  * @constructor
32041  * Creates a new Spacer
32042  */
32043 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
32044     // private
32045     render : function(td){
32046         td.style.width = '100%';
32047         Roo.Toolbar.Fill.superclass.render.call(this, td);
32048     }
32049 });
32050
32051 /**
32052  * @class Roo.Toolbar.TextItem
32053  * @extends Roo.Toolbar.Item
32054  * A simple class that renders text directly into a toolbar.
32055  * @constructor
32056  * Creates a new TextItem
32057  * @cfg {string} text 
32058  */
32059 Roo.Toolbar.TextItem = function(cfg){
32060     var  text = cfg || "";
32061     if (typeof(cfg) == 'object') {
32062         text = cfg.text || "";
32063     }  else {
32064         cfg = null;
32065     }
32066     var s = document.createElement("span");
32067     s.className = "ytb-text";
32068     s.innerHTML = text;
32069     if (cfg) {
32070         cfg.el  = s;
32071     }
32072     
32073     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
32074 };
32075 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
32076     
32077      
32078     enable:Roo.emptyFn,
32079     disable:Roo.emptyFn,
32080     focus:Roo.emptyFn,
32081      /**
32082      * Shows this button
32083      */
32084     show: function(){
32085         this.hidden = false;
32086         this.el.style.display = "";
32087     },
32088     
32089     /**
32090      * Hides this button
32091      */
32092     hide: function(){
32093         this.hidden = true;
32094         this.el.style.display = "none";
32095     }
32096     
32097 });
32098
32099 /**
32100  * @class Roo.Toolbar.Button
32101  * @extends Roo.Button
32102  * A button that renders into a toolbar.
32103  * @constructor
32104  * Creates a new Button
32105  * @param {Object} config A standard {@link Roo.Button} config object
32106  */
32107 Roo.Toolbar.Button = function(config){
32108     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
32109 };
32110 Roo.extend(Roo.Toolbar.Button, Roo.Button,
32111 {
32112     
32113     
32114     render : function(td){
32115         this.td = td;
32116         Roo.Toolbar.Button.superclass.render.call(this, td);
32117     },
32118     
32119     /**
32120      * Removes and destroys this button
32121      */
32122     destroy : function(){
32123         Roo.Toolbar.Button.superclass.destroy.call(this);
32124         this.td.parentNode.removeChild(this.td);
32125     },
32126     
32127     /**
32128      * Shows this button
32129      */
32130     show: function(){
32131         this.hidden = false;
32132         this.td.style.display = "";
32133     },
32134     
32135     /**
32136      * Hides this button
32137      */
32138     hide: function(){
32139         this.hidden = true;
32140         this.td.style.display = "none";
32141     },
32142
32143     /**
32144      * Disables this item
32145      */
32146     disable : function(){
32147         Roo.fly(this.td).addClass("x-item-disabled");
32148         this.disabled = true;
32149     },
32150
32151     /**
32152      * Enables this item
32153      */
32154     enable : function(){
32155         Roo.fly(this.td).removeClass("x-item-disabled");
32156         this.disabled = false;
32157     }
32158 });
32159 // backwards compat
32160 Roo.ToolbarButton = Roo.Toolbar.Button;
32161
32162 /**
32163  * @class Roo.Toolbar.SplitButton
32164  * @extends Roo.SplitButton
32165  * A menu button that renders into a toolbar.
32166  * @constructor
32167  * Creates a new SplitButton
32168  * @param {Object} config A standard {@link Roo.SplitButton} config object
32169  */
32170 Roo.Toolbar.SplitButton = function(config){
32171     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
32172 };
32173 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
32174     render : function(td){
32175         this.td = td;
32176         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
32177     },
32178     
32179     /**
32180      * Removes and destroys this button
32181      */
32182     destroy : function(){
32183         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
32184         this.td.parentNode.removeChild(this.td);
32185     },
32186     
32187     /**
32188      * Shows this button
32189      */
32190     show: function(){
32191         this.hidden = false;
32192         this.td.style.display = "";
32193     },
32194     
32195     /**
32196      * Hides this button
32197      */
32198     hide: function(){
32199         this.hidden = true;
32200         this.td.style.display = "none";
32201     }
32202 });
32203
32204 // backwards compat
32205 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
32206  * Based on:
32207  * Ext JS Library 1.1.1
32208  * Copyright(c) 2006-2007, Ext JS, LLC.
32209  *
32210  * Originally Released Under LGPL - original licence link has changed is not relivant.
32211  *
32212  * Fork - LGPL
32213  * <script type="text/javascript">
32214  */
32215  
32216 /**
32217  * @class Roo.PagingToolbar
32218  * @extends Roo.Toolbar
32219  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field
32220  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
32221  * @constructor
32222  * Create a new PagingToolbar
32223  * @param {Object} config The config object
32224  */
32225 Roo.PagingToolbar = function(el, ds, config)
32226 {
32227     // old args format still supported... - xtype is prefered..
32228     if (typeof(el) == 'object' && el.xtype) {
32229         // created from xtype...
32230         config = el;
32231         ds = el.dataSource;
32232         el = config.container;
32233     }
32234     var items = [];
32235     if (config.items) {
32236         items = config.items;
32237         config.items = [];
32238     }
32239     
32240     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
32241     this.ds = ds;
32242     this.cursor = 0;
32243     this.renderButtons(this.el);
32244     this.bind(ds);
32245     
32246     // supprot items array.
32247    
32248     Roo.each(items, function(e) {
32249         this.add(Roo.factory(e));
32250     },this);
32251     
32252 };
32253
32254 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
32255    
32256     /**
32257      * @cfg {String/HTMLElement/Element} container
32258      * container The id or element that will contain the toolbar
32259      */
32260     /**
32261      * @cfg {Boolean} displayInfo
32262      * True to display the displayMsg (defaults to false)
32263      */
32264     
32265     
32266     /**
32267      * @cfg {Number} pageSize
32268      * The number of records to display per page (defaults to 20)
32269      */
32270     pageSize: 20,
32271     /**
32272      * @cfg {String} displayMsg
32273      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
32274      */
32275     displayMsg : 'Displaying {0} - {1} of {2}',
32276     /**
32277      * @cfg {String} emptyMsg
32278      * The message to display when no records are found (defaults to "No data to display")
32279      */
32280     emptyMsg : 'No data to display',
32281     /**
32282      * Customizable piece of the default paging text (defaults to "Page")
32283      * @type String
32284      */
32285     beforePageText : "Page",
32286     /**
32287      * Customizable piece of the default paging text (defaults to "of %0")
32288      * @type String
32289      */
32290     afterPageText : "of {0}",
32291     /**
32292      * Customizable piece of the default paging text (defaults to "First Page")
32293      * @type String
32294      */
32295     firstText : "First Page",
32296     /**
32297      * Customizable piece of the default paging text (defaults to "Previous Page")
32298      * @type String
32299      */
32300     prevText : "Previous Page",
32301     /**
32302      * Customizable piece of the default paging text (defaults to "Next Page")
32303      * @type String
32304      */
32305     nextText : "Next Page",
32306     /**
32307      * Customizable piece of the default paging text (defaults to "Last Page")
32308      * @type String
32309      */
32310     lastText : "Last Page",
32311     /**
32312      * Customizable piece of the default paging text (defaults to "Refresh")
32313      * @type String
32314      */
32315     refreshText : "Refresh",
32316
32317     // private
32318     renderButtons : function(el){
32319         Roo.PagingToolbar.superclass.render.call(this, el);
32320         this.first = this.addButton({
32321             tooltip: this.firstText,
32322             cls: "x-btn-icon x-grid-page-first",
32323             disabled: true,
32324             handler: this.onClick.createDelegate(this, ["first"])
32325         });
32326         this.prev = this.addButton({
32327             tooltip: this.prevText,
32328             cls: "x-btn-icon x-grid-page-prev",
32329             disabled: true,
32330             handler: this.onClick.createDelegate(this, ["prev"])
32331         });
32332         //this.addSeparator();
32333         this.add(this.beforePageText);
32334         this.field = Roo.get(this.addDom({
32335            tag: "input",
32336            type: "text",
32337            size: "3",
32338            value: "1",
32339            cls: "x-grid-page-number"
32340         }).el);
32341         this.field.on("keydown", this.onPagingKeydown, this);
32342         this.field.on("focus", function(){this.dom.select();});
32343         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
32344         this.field.setHeight(18);
32345         //this.addSeparator();
32346         this.next = this.addButton({
32347             tooltip: this.nextText,
32348             cls: "x-btn-icon x-grid-page-next",
32349             disabled: true,
32350             handler: this.onClick.createDelegate(this, ["next"])
32351         });
32352         this.last = this.addButton({
32353             tooltip: this.lastText,
32354             cls: "x-btn-icon x-grid-page-last",
32355             disabled: true,
32356             handler: this.onClick.createDelegate(this, ["last"])
32357         });
32358         //this.addSeparator();
32359         this.loading = this.addButton({
32360             tooltip: this.refreshText,
32361             cls: "x-btn-icon x-grid-loading",
32362             handler: this.onClick.createDelegate(this, ["refresh"])
32363         });
32364
32365         if(this.displayInfo){
32366             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
32367         }
32368     },
32369
32370     // private
32371     updateInfo : function(){
32372         if(this.displayEl){
32373             var count = this.ds.getCount();
32374             var msg = count == 0 ?
32375                 this.emptyMsg :
32376                 String.format(
32377                     this.displayMsg,
32378                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
32379                 );
32380             this.displayEl.update(msg);
32381         }
32382     },
32383
32384     // private
32385     onLoad : function(ds, r, o){
32386        this.cursor = o.params ? o.params.start : 0;
32387        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
32388
32389        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
32390        this.field.dom.value = ap;
32391        this.first.setDisabled(ap == 1);
32392        this.prev.setDisabled(ap == 1);
32393        this.next.setDisabled(ap == ps);
32394        this.last.setDisabled(ap == ps);
32395        this.loading.enable();
32396        this.updateInfo();
32397     },
32398
32399     // private
32400     getPageData : function(){
32401         var total = this.ds.getTotalCount();
32402         return {
32403             total : total,
32404             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
32405             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
32406         };
32407     },
32408
32409     // private
32410     onLoadError : function(){
32411         this.loading.enable();
32412     },
32413
32414     // private
32415     onPagingKeydown : function(e){
32416         var k = e.getKey();
32417         var d = this.getPageData();
32418         if(k == e.RETURN){
32419             var v = this.field.dom.value, pageNum;
32420             if(!v || isNaN(pageNum = parseInt(v, 10))){
32421                 this.field.dom.value = d.activePage;
32422                 return;
32423             }
32424             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
32425             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
32426             e.stopEvent();
32427         }
32428         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))
32429         {
32430           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
32431           this.field.dom.value = pageNum;
32432           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
32433           e.stopEvent();
32434         }
32435         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
32436         {
32437           var v = this.field.dom.value, pageNum; 
32438           var increment = (e.shiftKey) ? 10 : 1;
32439           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
32440             increment *= -1;
32441           }
32442           if(!v || isNaN(pageNum = parseInt(v, 10))) {
32443             this.field.dom.value = d.activePage;
32444             return;
32445           }
32446           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
32447           {
32448             this.field.dom.value = parseInt(v, 10) + increment;
32449             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
32450             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
32451           }
32452           e.stopEvent();
32453         }
32454     },
32455
32456     // private
32457     beforeLoad : function(){
32458         if(this.loading){
32459             this.loading.disable();
32460         }
32461     },
32462     /**
32463      * event that occurs when you click on the navigation buttons - can be used to trigger load of a grid.
32464      * @param {String} which (first|prev|next|last|refresh)  which button to press.
32465      *
32466      */
32467     // private
32468     onClick : function(which){
32469         var ds = this.ds;
32470         switch(which){
32471             case "first":
32472                 ds.load({params:{start: 0, limit: this.pageSize}});
32473             break;
32474             case "prev":
32475                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
32476             break;
32477             case "next":
32478                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
32479             break;
32480             case "last":
32481                 var total = ds.getTotalCount();
32482                 var extra = total % this.pageSize;
32483                 var lastStart = extra ? (total - extra) : total-this.pageSize;
32484                 ds.load({params:{start: lastStart, limit: this.pageSize}});
32485             break;
32486             case "refresh":
32487                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
32488             break;
32489         }
32490     },
32491
32492     /**
32493      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
32494      * @param {Roo.data.Store} store The data store to unbind
32495      */
32496     unbind : function(ds){
32497         ds.un("beforeload", this.beforeLoad, this);
32498         ds.un("load", this.onLoad, this);
32499         ds.un("loadexception", this.onLoadError, this);
32500         ds.un("remove", this.updateInfo, this);
32501         ds.un("add", this.updateInfo, this);
32502         this.ds = undefined;
32503     },
32504
32505     /**
32506      * Binds the paging toolbar to the specified {@link Roo.data.Store}
32507      * @param {Roo.data.Store} store The data store to bind
32508      */
32509     bind : function(ds){
32510         ds.on("beforeload", this.beforeLoad, this);
32511         ds.on("load", this.onLoad, this);
32512         ds.on("loadexception", this.onLoadError, this);
32513         ds.on("remove", this.updateInfo, this);
32514         ds.on("add", this.updateInfo, this);
32515         this.ds = ds;
32516     }
32517 });/*
32518  * Based on:
32519  * Ext JS Library 1.1.1
32520  * Copyright(c) 2006-2007, Ext JS, LLC.
32521  *
32522  * Originally Released Under LGPL - original licence link has changed is not relivant.
32523  *
32524  * Fork - LGPL
32525  * <script type="text/javascript">
32526  */
32527
32528 /**
32529  * @class Roo.Resizable
32530  * @extends Roo.util.Observable
32531  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
32532  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
32533  * 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
32534  * the element will be wrapped for you automatically.</p>
32535  * <p>Here is the list of valid resize handles:</p>
32536  * <pre>
32537 Value   Description
32538 ------  -------------------
32539  'n'     north
32540  's'     south
32541  'e'     east
32542  'w'     west
32543  'nw'    northwest
32544  'sw'    southwest
32545  'se'    southeast
32546  'ne'    northeast
32547  'hd'    horizontal drag
32548  'all'   all
32549 </pre>
32550  * <p>Here's an example showing the creation of a typical Resizable:</p>
32551  * <pre><code>
32552 var resizer = new Roo.Resizable("element-id", {
32553     handles: 'all',
32554     minWidth: 200,
32555     minHeight: 100,
32556     maxWidth: 500,
32557     maxHeight: 400,
32558     pinned: true
32559 });
32560 resizer.on("resize", myHandler);
32561 </code></pre>
32562  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
32563  * resizer.east.setDisplayed(false);</p>
32564  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
32565  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
32566  * resize operation's new size (defaults to [0, 0])
32567  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
32568  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
32569  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
32570  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
32571  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
32572  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
32573  * @cfg {Number} width The width of the element in pixels (defaults to null)
32574  * @cfg {Number} height The height of the element in pixels (defaults to null)
32575  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
32576  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
32577  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
32578  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
32579  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
32580  * in favor of the handles config option (defaults to false)
32581  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
32582  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
32583  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
32584  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
32585  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
32586  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
32587  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
32588  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
32589  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
32590  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
32591  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
32592  * @constructor
32593  * Create a new resizable component
32594  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
32595  * @param {Object} config configuration options
32596   */
32597 Roo.Resizable = function(el, config)
32598 {
32599     this.el = Roo.get(el);
32600
32601     if(config && config.wrap){
32602         config.resizeChild = this.el;
32603         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
32604         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
32605         this.el.setStyle("overflow", "hidden");
32606         this.el.setPositioning(config.resizeChild.getPositioning());
32607         config.resizeChild.clearPositioning();
32608         if(!config.width || !config.height){
32609             var csize = config.resizeChild.getSize();
32610             this.el.setSize(csize.width, csize.height);
32611         }
32612         if(config.pinned && !config.adjustments){
32613             config.adjustments = "auto";
32614         }
32615     }
32616
32617     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
32618     this.proxy.unselectable();
32619     this.proxy.enableDisplayMode('block');
32620
32621     Roo.apply(this, config);
32622
32623     if(this.pinned){
32624         this.disableTrackOver = true;
32625         this.el.addClass("x-resizable-pinned");
32626     }
32627     // if the element isn't positioned, make it relative
32628     var position = this.el.getStyle("position");
32629     if(position != "absolute" && position != "fixed"){
32630         this.el.setStyle("position", "relative");
32631     }
32632     if(!this.handles){ // no handles passed, must be legacy style
32633         this.handles = 's,e,se';
32634         if(this.multiDirectional){
32635             this.handles += ',n,w';
32636         }
32637     }
32638     if(this.handles == "all"){
32639         this.handles = "n s e w ne nw se sw";
32640     }
32641     var hs = this.handles.split(/\s*?[,;]\s*?| /);
32642     var ps = Roo.Resizable.positions;
32643     for(var i = 0, len = hs.length; i < len; i++){
32644         if(hs[i] && ps[hs[i]]){
32645             var pos = ps[hs[i]];
32646             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
32647         }
32648     }
32649     // legacy
32650     this.corner = this.southeast;
32651     
32652     // updateBox = the box can move..
32653     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
32654         this.updateBox = true;
32655     }
32656
32657     this.activeHandle = null;
32658
32659     if(this.resizeChild){
32660         if(typeof this.resizeChild == "boolean"){
32661             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
32662         }else{
32663             this.resizeChild = Roo.get(this.resizeChild, true);
32664         }
32665     }
32666     
32667     if(this.adjustments == "auto"){
32668         var rc = this.resizeChild;
32669         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
32670         if(rc && (hw || hn)){
32671             rc.position("relative");
32672             rc.setLeft(hw ? hw.el.getWidth() : 0);
32673             rc.setTop(hn ? hn.el.getHeight() : 0);
32674         }
32675         this.adjustments = [
32676             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
32677             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
32678         ];
32679     }
32680
32681     if(this.draggable){
32682         this.dd = this.dynamic ?
32683             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
32684         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
32685     }
32686
32687     // public events
32688     this.addEvents({
32689         /**
32690          * @event beforeresize
32691          * Fired before resize is allowed. Set enabled to false to cancel resize.
32692          * @param {Roo.Resizable} this
32693          * @param {Roo.EventObject} e The mousedown event
32694          */
32695         "beforeresize" : true,
32696         /**
32697          * @event resizing
32698          * Fired a resizing.
32699          * @param {Roo.Resizable} this
32700          * @param {Number} x The new x position
32701          * @param {Number} y The new y position
32702          * @param {Number} w The new w width
32703          * @param {Number} h The new h hight
32704          * @param {Roo.EventObject} e The mouseup event
32705          */
32706         "resizing" : true,
32707         /**
32708          * @event resize
32709          * Fired after a resize.
32710          * @param {Roo.Resizable} this
32711          * @param {Number} width The new width
32712          * @param {Number} height The new height
32713          * @param {Roo.EventObject} e The mouseup event
32714          */
32715         "resize" : true
32716     });
32717
32718     if(this.width !== null && this.height !== null){
32719         this.resizeTo(this.width, this.height);
32720     }else{
32721         this.updateChildSize();
32722     }
32723     if(Roo.isIE){
32724         this.el.dom.style.zoom = 1;
32725     }
32726     Roo.Resizable.superclass.constructor.call(this);
32727 };
32728
32729 Roo.extend(Roo.Resizable, Roo.util.Observable, {
32730         resizeChild : false,
32731         adjustments : [0, 0],
32732         minWidth : 5,
32733         minHeight : 5,
32734         maxWidth : 10000,
32735         maxHeight : 10000,
32736         enabled : true,
32737         animate : false,
32738         duration : .35,
32739         dynamic : false,
32740         handles : false,
32741         multiDirectional : false,
32742         disableTrackOver : false,
32743         easing : 'easeOutStrong',
32744         widthIncrement : 0,
32745         heightIncrement : 0,
32746         pinned : false,
32747         width : null,
32748         height : null,
32749         preserveRatio : false,
32750         transparent: false,
32751         minX: 0,
32752         minY: 0,
32753         draggable: false,
32754
32755         /**
32756          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
32757          */
32758         constrainTo: undefined,
32759         /**
32760          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
32761          */
32762         resizeRegion: undefined,
32763
32764
32765     /**
32766      * Perform a manual resize
32767      * @param {Number} width
32768      * @param {Number} height
32769      */
32770     resizeTo : function(width, height){
32771         this.el.setSize(width, height);
32772         this.updateChildSize();
32773         this.fireEvent("resize", this, width, height, null);
32774     },
32775
32776     // private
32777     startSizing : function(e, handle){
32778         this.fireEvent("beforeresize", this, e);
32779         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
32780
32781             if(!this.overlay){
32782                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
32783                 this.overlay.unselectable();
32784                 this.overlay.enableDisplayMode("block");
32785                 this.overlay.on("mousemove", this.onMouseMove, this);
32786                 this.overlay.on("mouseup", this.onMouseUp, this);
32787             }
32788             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
32789
32790             this.resizing = true;
32791             this.startBox = this.el.getBox();
32792             this.startPoint = e.getXY();
32793             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
32794                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
32795
32796             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32797             this.overlay.show();
32798
32799             if(this.constrainTo) {
32800                 var ct = Roo.get(this.constrainTo);
32801                 this.resizeRegion = ct.getRegion().adjust(
32802                     ct.getFrameWidth('t'),
32803                     ct.getFrameWidth('l'),
32804                     -ct.getFrameWidth('b'),
32805                     -ct.getFrameWidth('r')
32806                 );
32807             }
32808
32809             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
32810             this.proxy.show();
32811             this.proxy.setBox(this.startBox);
32812             if(!this.dynamic){
32813                 this.proxy.setStyle('visibility', 'visible');
32814             }
32815         }
32816     },
32817
32818     // private
32819     onMouseDown : function(handle, e){
32820         if(this.enabled){
32821             e.stopEvent();
32822             this.activeHandle = handle;
32823             this.startSizing(e, handle);
32824         }
32825     },
32826
32827     // private
32828     onMouseUp : function(e){
32829         var size = this.resizeElement();
32830         this.resizing = false;
32831         this.handleOut();
32832         this.overlay.hide();
32833         this.proxy.hide();
32834         this.fireEvent("resize", this, size.width, size.height, e);
32835     },
32836
32837     // private
32838     updateChildSize : function(){
32839         
32840         if(this.resizeChild){
32841             var el = this.el;
32842             var child = this.resizeChild;
32843             var adj = this.adjustments;
32844             if(el.dom.offsetWidth){
32845                 var b = el.getSize(true);
32846                 child.setSize(b.width+adj[0], b.height+adj[1]);
32847             }
32848             // Second call here for IE
32849             // The first call enables instant resizing and
32850             // the second call corrects scroll bars if they
32851             // exist
32852             if(Roo.isIE){
32853                 setTimeout(function(){
32854                     if(el.dom.offsetWidth){
32855                         var b = el.getSize(true);
32856                         child.setSize(b.width+adj[0], b.height+adj[1]);
32857                     }
32858                 }, 10);
32859             }
32860         }
32861     },
32862
32863     // private
32864     snap : function(value, inc, min){
32865         if(!inc || !value) {
32866             return value;
32867         }
32868         var newValue = value;
32869         var m = value % inc;
32870         if(m > 0){
32871             if(m > (inc/2)){
32872                 newValue = value + (inc-m);
32873             }else{
32874                 newValue = value - m;
32875             }
32876         }
32877         return Math.max(min, newValue);
32878     },
32879
32880     // private
32881     resizeElement : function(){
32882         var box = this.proxy.getBox();
32883         if(this.updateBox){
32884             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
32885         }else{
32886             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
32887         }
32888         this.updateChildSize();
32889         if(!this.dynamic){
32890             this.proxy.hide();
32891         }
32892         return box;
32893     },
32894
32895     // private
32896     constrain : function(v, diff, m, mx){
32897         if(v - diff < m){
32898             diff = v - m;
32899         }else if(v - diff > mx){
32900             diff = mx - v;
32901         }
32902         return diff;
32903     },
32904
32905     // private
32906     onMouseMove : function(e){
32907         
32908         if(this.enabled){
32909             try{// try catch so if something goes wrong the user doesn't get hung
32910
32911             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
32912                 return;
32913             }
32914
32915             //var curXY = this.startPoint;
32916             var curSize = this.curSize || this.startBox;
32917             var x = this.startBox.x, y = this.startBox.y;
32918             var ox = x, oy = y;
32919             var w = curSize.width, h = curSize.height;
32920             var ow = w, oh = h;
32921             var mw = this.minWidth, mh = this.minHeight;
32922             var mxw = this.maxWidth, mxh = this.maxHeight;
32923             var wi = this.widthIncrement;
32924             var hi = this.heightIncrement;
32925
32926             var eventXY = e.getXY();
32927             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
32928             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
32929
32930             var pos = this.activeHandle.position;
32931
32932             switch(pos){
32933                 case "east":
32934                     w += diffX;
32935                     w = Math.min(Math.max(mw, w), mxw);
32936                     break;
32937              
32938                 case "south":
32939                     h += diffY;
32940                     h = Math.min(Math.max(mh, h), mxh);
32941                     break;
32942                 case "southeast":
32943                     w += diffX;
32944                     h += diffY;
32945                     w = Math.min(Math.max(mw, w), mxw);
32946                     h = Math.min(Math.max(mh, h), mxh);
32947                     break;
32948                 case "north":
32949                     diffY = this.constrain(h, diffY, mh, mxh);
32950                     y += diffY;
32951                     h -= diffY;
32952                     break;
32953                 case "hdrag":
32954                     
32955                     if (wi) {
32956                         var adiffX = Math.abs(diffX);
32957                         var sub = (adiffX % wi); // how much 
32958                         if (sub > (wi/2)) { // far enough to snap
32959                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
32960                         } else {
32961                             // remove difference.. 
32962                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
32963                         }
32964                     }
32965                     x += diffX;
32966                     x = Math.max(this.minX, x);
32967                     break;
32968                 case "west":
32969                     diffX = this.constrain(w, diffX, mw, mxw);
32970                     x += diffX;
32971                     w -= diffX;
32972                     break;
32973                 case "northeast":
32974                     w += diffX;
32975                     w = Math.min(Math.max(mw, w), mxw);
32976                     diffY = this.constrain(h, diffY, mh, mxh);
32977                     y += diffY;
32978                     h -= diffY;
32979                     break;
32980                 case "northwest":
32981                     diffX = this.constrain(w, diffX, mw, mxw);
32982                     diffY = this.constrain(h, diffY, mh, mxh);
32983                     y += diffY;
32984                     h -= diffY;
32985                     x += diffX;
32986                     w -= diffX;
32987                     break;
32988                case "southwest":
32989                     diffX = this.constrain(w, diffX, mw, mxw);
32990                     h += diffY;
32991                     h = Math.min(Math.max(mh, h), mxh);
32992                     x += diffX;
32993                     w -= diffX;
32994                     break;
32995             }
32996
32997             var sw = this.snap(w, wi, mw);
32998             var sh = this.snap(h, hi, mh);
32999             if(sw != w || sh != h){
33000                 switch(pos){
33001                     case "northeast":
33002                         y -= sh - h;
33003                     break;
33004                     case "north":
33005                         y -= sh - h;
33006                         break;
33007                     case "southwest":
33008                         x -= sw - w;
33009                     break;
33010                     case "west":
33011                         x -= sw - w;
33012                         break;
33013                     case "northwest":
33014                         x -= sw - w;
33015                         y -= sh - h;
33016                     break;
33017                 }
33018                 w = sw;
33019                 h = sh;
33020             }
33021
33022             if(this.preserveRatio){
33023                 switch(pos){
33024                     case "southeast":
33025                     case "east":
33026                         h = oh * (w/ow);
33027                         h = Math.min(Math.max(mh, h), mxh);
33028                         w = ow * (h/oh);
33029                        break;
33030                     case "south":
33031                         w = ow * (h/oh);
33032                         w = Math.min(Math.max(mw, w), mxw);
33033                         h = oh * (w/ow);
33034                         break;
33035                     case "northeast":
33036                         w = ow * (h/oh);
33037                         w = Math.min(Math.max(mw, w), mxw);
33038                         h = oh * (w/ow);
33039                     break;
33040                     case "north":
33041                         var tw = w;
33042                         w = ow * (h/oh);
33043                         w = Math.min(Math.max(mw, w), mxw);
33044                         h = oh * (w/ow);
33045                         x += (tw - w) / 2;
33046                         break;
33047                     case "southwest":
33048                         h = oh * (w/ow);
33049                         h = Math.min(Math.max(mh, h), mxh);
33050                         var tw = w;
33051                         w = ow * (h/oh);
33052                         x += tw - w;
33053                         break;
33054                     case "west":
33055                         var th = h;
33056                         h = oh * (w/ow);
33057                         h = Math.min(Math.max(mh, h), mxh);
33058                         y += (th - h) / 2;
33059                         var tw = w;
33060                         w = ow * (h/oh);
33061                         x += tw - w;
33062                        break;
33063                     case "northwest":
33064                         var tw = w;
33065                         var th = h;
33066                         h = oh * (w/ow);
33067                         h = Math.min(Math.max(mh, h), mxh);
33068                         w = ow * (h/oh);
33069                         y += th - h;
33070                         x += tw - w;
33071                        break;
33072
33073                 }
33074             }
33075             if (pos == 'hdrag') {
33076                 w = ow;
33077             }
33078             this.proxy.setBounds(x, y, w, h);
33079             if(this.dynamic){
33080                 this.resizeElement();
33081             }
33082             }catch(e){}
33083         }
33084         this.fireEvent("resizing", this, x, y, w, h, e);
33085     },
33086
33087     // private
33088     handleOver : function(){
33089         if(this.enabled){
33090             this.el.addClass("x-resizable-over");
33091         }
33092     },
33093
33094     // private
33095     handleOut : function(){
33096         if(!this.resizing){
33097             this.el.removeClass("x-resizable-over");
33098         }
33099     },
33100
33101     /**
33102      * Returns the element this component is bound to.
33103      * @return {Roo.Element}
33104      */
33105     getEl : function(){
33106         return this.el;
33107     },
33108
33109     /**
33110      * Returns the resizeChild element (or null).
33111      * @return {Roo.Element}
33112      */
33113     getResizeChild : function(){
33114         return this.resizeChild;
33115     },
33116     groupHandler : function()
33117     {
33118         
33119     },
33120     /**
33121      * Destroys this resizable. If the element was wrapped and
33122      * removeEl is not true then the element remains.
33123      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
33124      */
33125     destroy : function(removeEl){
33126         this.proxy.remove();
33127         if(this.overlay){
33128             this.overlay.removeAllListeners();
33129             this.overlay.remove();
33130         }
33131         var ps = Roo.Resizable.positions;
33132         for(var k in ps){
33133             if(typeof ps[k] != "function" && this[ps[k]]){
33134                 var h = this[ps[k]];
33135                 h.el.removeAllListeners();
33136                 h.el.remove();
33137             }
33138         }
33139         if(removeEl){
33140             this.el.update("");
33141             this.el.remove();
33142         }
33143     }
33144 });
33145
33146 // private
33147 // hash to map config positions to true positions
33148 Roo.Resizable.positions = {
33149     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
33150     hd: "hdrag"
33151 };
33152
33153 // private
33154 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
33155     if(!this.tpl){
33156         // only initialize the template if resizable is used
33157         var tpl = Roo.DomHelper.createTemplate(
33158             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
33159         );
33160         tpl.compile();
33161         Roo.Resizable.Handle.prototype.tpl = tpl;
33162     }
33163     this.position = pos;
33164     this.rz = rz;
33165     // show north drag fro topdra
33166     var handlepos = pos == 'hdrag' ? 'north' : pos;
33167     
33168     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
33169     if (pos == 'hdrag') {
33170         this.el.setStyle('cursor', 'pointer');
33171     }
33172     this.el.unselectable();
33173     if(transparent){
33174         this.el.setOpacity(0);
33175     }
33176     this.el.on("mousedown", this.onMouseDown, this);
33177     if(!disableTrackOver){
33178         this.el.on("mouseover", this.onMouseOver, this);
33179         this.el.on("mouseout", this.onMouseOut, this);
33180     }
33181 };
33182
33183 // private
33184 Roo.Resizable.Handle.prototype = {
33185     afterResize : function(rz){
33186         Roo.log('after?');
33187         // do nothing
33188     },
33189     // private
33190     onMouseDown : function(e){
33191         this.rz.onMouseDown(this, e);
33192     },
33193     // private
33194     onMouseOver : function(e){
33195         this.rz.handleOver(this, e);
33196     },
33197     // private
33198     onMouseOut : function(e){
33199         this.rz.handleOut(this, e);
33200     }
33201 };/*
33202  * Based on:
33203  * Ext JS Library 1.1.1
33204  * Copyright(c) 2006-2007, Ext JS, LLC.
33205  *
33206  * Originally Released Under LGPL - original licence link has changed is not relivant.
33207  *
33208  * Fork - LGPL
33209  * <script type="text/javascript">
33210  */
33211
33212 /**
33213  * @class Roo.Editor
33214  * @extends Roo.Component
33215  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
33216  * @constructor
33217  * Create a new Editor
33218  * @param {Roo.form.Field} field The Field object (or descendant)
33219  * @param {Object} config The config object
33220  */
33221 Roo.Editor = function(field, config){
33222     Roo.Editor.superclass.constructor.call(this, config);
33223     this.field = field;
33224     this.addEvents({
33225         /**
33226              * @event beforestartedit
33227              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
33228              * false from the handler of this event.
33229              * @param {Editor} this
33230              * @param {Roo.Element} boundEl The underlying element bound to this editor
33231              * @param {Mixed} value The field value being set
33232              */
33233         "beforestartedit" : true,
33234         /**
33235              * @event startedit
33236              * Fires when this editor is displayed
33237              * @param {Roo.Element} boundEl The underlying element bound to this editor
33238              * @param {Mixed} value The starting field value
33239              */
33240         "startedit" : true,
33241         /**
33242              * @event beforecomplete
33243              * Fires after a change has been made to the field, but before the change is reflected in the underlying
33244              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
33245              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
33246              * event will not fire since no edit actually occurred.
33247              * @param {Editor} this
33248              * @param {Mixed} value The current field value
33249              * @param {Mixed} startValue The original field value
33250              */
33251         "beforecomplete" : true,
33252         /**
33253              * @event complete
33254              * Fires after editing is complete and any changed value has been written to the underlying field.
33255              * @param {Editor} this
33256              * @param {Mixed} value The current field value
33257              * @param {Mixed} startValue The original field value
33258              */
33259         "complete" : true,
33260         /**
33261          * @event specialkey
33262          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
33263          * {@link Roo.EventObject#getKey} to determine which key was pressed.
33264          * @param {Roo.form.Field} this
33265          * @param {Roo.EventObject} e The event object
33266          */
33267         "specialkey" : true
33268     });
33269 };
33270
33271 Roo.extend(Roo.Editor, Roo.Component, {
33272     /**
33273      * @cfg {Boolean/String} autosize
33274      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
33275      * or "height" to adopt the height only (defaults to false)
33276      */
33277     /**
33278      * @cfg {Boolean} revertInvalid
33279      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
33280      * validation fails (defaults to true)
33281      */
33282     /**
33283      * @cfg {Boolean} ignoreNoChange
33284      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
33285      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
33286      * will never be ignored.
33287      */
33288     /**
33289      * @cfg {Boolean} hideEl
33290      * False to keep the bound element visible while the editor is displayed (defaults to true)
33291      */
33292     /**
33293      * @cfg {Mixed} value
33294      * The data value of the underlying field (defaults to "")
33295      */
33296     value : "",
33297     /**
33298      * @cfg {String} alignment
33299      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
33300      */
33301     alignment: "c-c?",
33302     /**
33303      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
33304      * for bottom-right shadow (defaults to "frame")
33305      */
33306     shadow : "frame",
33307     /**
33308      * @cfg {Boolean} constrain True to constrain the editor to the viewport
33309      */
33310     constrain : false,
33311     /**
33312      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
33313      */
33314     completeOnEnter : false,
33315     /**
33316      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
33317      */
33318     cancelOnEsc : false,
33319     /**
33320      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
33321      */
33322     updateEl : false,
33323
33324     // private
33325     onRender : function(ct, position){
33326         this.el = new Roo.Layer({
33327             shadow: this.shadow,
33328             cls: "x-editor",
33329             parentEl : ct,
33330             shim : this.shim,
33331             shadowOffset:4,
33332             id: this.id,
33333             constrain: this.constrain
33334         });
33335         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
33336         if(this.field.msgTarget != 'title'){
33337             this.field.msgTarget = 'qtip';
33338         }
33339         this.field.render(this.el);
33340         if(Roo.isGecko){
33341             this.field.el.dom.setAttribute('autocomplete', 'off');
33342         }
33343         this.field.on("specialkey", this.onSpecialKey, this);
33344         if(this.swallowKeys){
33345             this.field.el.swallowEvent(['keydown','keypress']);
33346         }
33347         this.field.show();
33348         this.field.on("blur", this.onBlur, this);
33349         if(this.field.grow){
33350             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
33351         }
33352     },
33353
33354     onSpecialKey : function(field, e)
33355     {
33356         //Roo.log('editor onSpecialKey');
33357         if(this.completeOnEnter && e.getKey() == e.ENTER){
33358             e.stopEvent();
33359             this.completeEdit();
33360             return;
33361         }
33362         // do not fire special key otherwise it might hide close the editor...
33363         if(e.getKey() == e.ENTER){    
33364             return;
33365         }
33366         if(this.cancelOnEsc && e.getKey() == e.ESC){
33367             this.cancelEdit();
33368             return;
33369         } 
33370         this.fireEvent('specialkey', field, e);
33371     
33372     },
33373
33374     /**
33375      * Starts the editing process and shows the editor.
33376      * @param {String/HTMLElement/Element} el The element to edit
33377      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
33378       * to the innerHTML of el.
33379      */
33380     startEdit : function(el, value){
33381         if(this.editing){
33382             this.completeEdit();
33383         }
33384         this.boundEl = Roo.get(el);
33385         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
33386         if(!this.rendered){
33387             this.render(this.parentEl || document.body);
33388         }
33389         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
33390             return;
33391         }
33392         this.startValue = v;
33393         this.field.setValue(v);
33394         if(this.autoSize){
33395             var sz = this.boundEl.getSize();
33396             switch(this.autoSize){
33397                 case "width":
33398                 this.setSize(sz.width,  "");
33399                 break;
33400                 case "height":
33401                 this.setSize("",  sz.height);
33402                 break;
33403                 default:
33404                 this.setSize(sz.width,  sz.height);
33405             }
33406         }
33407         this.el.alignTo(this.boundEl, this.alignment);
33408         this.editing = true;
33409         if(Roo.QuickTips){
33410             Roo.QuickTips.disable();
33411         }
33412         this.show();
33413     },
33414
33415     /**
33416      * Sets the height and width of this editor.
33417      * @param {Number} width The new width
33418      * @param {Number} height The new height
33419      */
33420     setSize : function(w, h){
33421         this.field.setSize(w, h);
33422         if(this.el){
33423             this.el.sync();
33424         }
33425     },
33426
33427     /**
33428      * Realigns the editor to the bound field based on the current alignment config value.
33429      */
33430     realign : function(){
33431         this.el.alignTo(this.boundEl, this.alignment);
33432     },
33433
33434     /**
33435      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
33436      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
33437      */
33438     completeEdit : function(remainVisible){
33439         if(!this.editing){
33440             return;
33441         }
33442         var v = this.getValue();
33443         if(this.revertInvalid !== false && !this.field.isValid()){
33444             v = this.startValue;
33445             this.cancelEdit(true);
33446         }
33447         if(String(v) === String(this.startValue) && this.ignoreNoChange){
33448             this.editing = false;
33449             this.hide();
33450             return;
33451         }
33452         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
33453             this.editing = false;
33454             if(this.updateEl && this.boundEl){
33455                 this.boundEl.update(v);
33456             }
33457             if(remainVisible !== true){
33458                 this.hide();
33459             }
33460             this.fireEvent("complete", this, v, this.startValue);
33461         }
33462     },
33463
33464     // private
33465     onShow : function(){
33466         this.el.show();
33467         if(this.hideEl !== false){
33468             this.boundEl.hide();
33469         }
33470         this.field.show();
33471         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
33472             this.fixIEFocus = true;
33473             this.deferredFocus.defer(50, this);
33474         }else{
33475             this.field.focus();
33476         }
33477         this.fireEvent("startedit", this.boundEl, this.startValue);
33478     },
33479
33480     deferredFocus : function(){
33481         if(this.editing){
33482             this.field.focus();
33483         }
33484     },
33485
33486     /**
33487      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
33488      * reverted to the original starting value.
33489      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
33490      * cancel (defaults to false)
33491      */
33492     cancelEdit : function(remainVisible){
33493         if(this.editing){
33494             this.setValue(this.startValue);
33495             if(remainVisible !== true){
33496                 this.hide();
33497             }
33498         }
33499     },
33500
33501     // private
33502     onBlur : function(){
33503         if(this.allowBlur !== true && this.editing){
33504             this.completeEdit();
33505         }
33506     },
33507
33508     // private
33509     onHide : function(){
33510         if(this.editing){
33511             this.completeEdit();
33512             return;
33513         }
33514         this.field.blur();
33515         if(this.field.collapse){
33516             this.field.collapse();
33517         }
33518         this.el.hide();
33519         if(this.hideEl !== false){
33520             this.boundEl.show();
33521         }
33522         if(Roo.QuickTips){
33523             Roo.QuickTips.enable();
33524         }
33525     },
33526
33527     /**
33528      * Sets the data value of the editor
33529      * @param {Mixed} value Any valid value supported by the underlying field
33530      */
33531     setValue : function(v){
33532         this.field.setValue(v);
33533     },
33534
33535     /**
33536      * Gets the data value of the editor
33537      * @return {Mixed} The data value
33538      */
33539     getValue : function(){
33540         return this.field.getValue();
33541     }
33542 });/*
33543  * Based on:
33544  * Ext JS Library 1.1.1
33545  * Copyright(c) 2006-2007, Ext JS, LLC.
33546  *
33547  * Originally Released Under LGPL - original licence link has changed is not relivant.
33548  *
33549  * Fork - LGPL
33550  * <script type="text/javascript">
33551  */
33552  
33553 /**
33554  * @class Roo.BasicDialog
33555  * @extends Roo.util.Observable
33556  * @parent none builder
33557  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
33558  * <pre><code>
33559 var dlg = new Roo.BasicDialog("my-dlg", {
33560     height: 200,
33561     width: 300,
33562     minHeight: 100,
33563     minWidth: 150,
33564     modal: true,
33565     proxyDrag: true,
33566     shadow: true
33567 });
33568 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
33569 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
33570 dlg.addButton('Cancel', dlg.hide, dlg);
33571 dlg.show();
33572 </code></pre>
33573   <b>A Dialog should always be a direct child of the body element.</b>
33574  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
33575  * @cfg {String} title Default text to display in the title bar (defaults to null)
33576  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
33577  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
33578  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
33579  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
33580  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
33581  * (defaults to null with no animation)
33582  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
33583  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
33584  * property for valid values (defaults to 'all')
33585  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
33586  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
33587  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
33588  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
33589  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
33590  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
33591  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
33592  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
33593  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
33594  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
33595  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
33596  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
33597  * draggable = true (defaults to false)
33598  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
33599  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
33600  * shadow (defaults to false)
33601  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
33602  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
33603  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
33604  * @cfg {Array} buttons Array of buttons
33605  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
33606  * @constructor
33607  * Create a new BasicDialog.
33608  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
33609  * @param {Object} config Configuration options
33610  */
33611 Roo.BasicDialog = function(el, config){
33612     this.el = Roo.get(el);
33613     var dh = Roo.DomHelper;
33614     if(!this.el && config && config.autoCreate){
33615         if(typeof config.autoCreate == "object"){
33616             if(!config.autoCreate.id){
33617                 config.autoCreate.id = el;
33618             }
33619             this.el = dh.append(document.body,
33620                         config.autoCreate, true);
33621         }else{
33622             this.el = dh.append(document.body,
33623                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
33624         }
33625     }
33626     el = this.el;
33627     el.setDisplayed(true);
33628     el.hide = this.hideAction;
33629     this.id = el.id;
33630     el.addClass("x-dlg");
33631
33632     Roo.apply(this, config);
33633
33634     this.proxy = el.createProxy("x-dlg-proxy");
33635     this.proxy.hide = this.hideAction;
33636     this.proxy.setOpacity(.5);
33637     this.proxy.hide();
33638
33639     if(config.width){
33640         el.setWidth(config.width);
33641     }
33642     if(config.height){
33643         el.setHeight(config.height);
33644     }
33645     this.size = el.getSize();
33646     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
33647         this.xy = [config.x,config.y];
33648     }else{
33649         this.xy = el.getCenterXY(true);
33650     }
33651     /** The header element @type Roo.Element */
33652     this.header = el.child("> .x-dlg-hd");
33653     /** The body element @type Roo.Element */
33654     this.body = el.child("> .x-dlg-bd");
33655     /** The footer element @type Roo.Element */
33656     this.footer = el.child("> .x-dlg-ft");
33657
33658     if(!this.header){
33659         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
33660     }
33661     if(!this.body){
33662         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
33663     }
33664
33665     this.header.unselectable();
33666     if(this.title){
33667         this.header.update(this.title);
33668     }
33669     // this element allows the dialog to be focused for keyboard event
33670     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
33671     this.focusEl.swallowEvent("click", true);
33672
33673     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
33674
33675     // wrap the body and footer for special rendering
33676     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
33677     if(this.footer){
33678         this.bwrap.dom.appendChild(this.footer.dom);
33679     }
33680
33681     this.bg = this.el.createChild({
33682         tag: "div", cls:"x-dlg-bg",
33683         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
33684     });
33685     this.centerBg = this.bg.child("div.x-dlg-bg-center");
33686
33687
33688     if(this.autoScroll !== false && !this.autoTabs){
33689         this.body.setStyle("overflow", "auto");
33690     }
33691
33692     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
33693
33694     if(this.closable !== false){
33695         this.el.addClass("x-dlg-closable");
33696         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
33697         this.close.on("click", this.closeClick, this);
33698         this.close.addClassOnOver("x-dlg-close-over");
33699     }
33700     if(this.collapsible !== false){
33701         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
33702         this.collapseBtn.on("click", this.collapseClick, this);
33703         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
33704         this.header.on("dblclick", this.collapseClick, this);
33705     }
33706     if(this.resizable !== false){
33707         this.el.addClass("x-dlg-resizable");
33708         this.resizer = new Roo.Resizable(el, {
33709             minWidth: this.minWidth || 80,
33710             minHeight:this.minHeight || 80,
33711             handles: this.resizeHandles || "all",
33712             pinned: true
33713         });
33714         this.resizer.on("beforeresize", this.beforeResize, this);
33715         this.resizer.on("resize", this.onResize, this);
33716     }
33717     if(this.draggable !== false){
33718         el.addClass("x-dlg-draggable");
33719         if (!this.proxyDrag) {
33720             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
33721         }
33722         else {
33723             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
33724         }
33725         dd.setHandleElId(this.header.id);
33726         dd.endDrag = this.endMove.createDelegate(this);
33727         dd.startDrag = this.startMove.createDelegate(this);
33728         dd.onDrag = this.onDrag.createDelegate(this);
33729         dd.scroll = false;
33730         this.dd = dd;
33731     }
33732     if(this.modal){
33733         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
33734         this.mask.enableDisplayMode("block");
33735         this.mask.hide();
33736         this.el.addClass("x-dlg-modal");
33737     }
33738     if(this.shadow){
33739         this.shadow = new Roo.Shadow({
33740             mode : typeof this.shadow == "string" ? this.shadow : "sides",
33741             offset : this.shadowOffset
33742         });
33743     }else{
33744         this.shadowOffset = 0;
33745     }
33746     if(Roo.useShims && this.shim !== false){
33747         this.shim = this.el.createShim();
33748         this.shim.hide = this.hideAction;
33749         this.shim.hide();
33750     }else{
33751         this.shim = false;
33752     }
33753     if(this.autoTabs){
33754         this.initTabs();
33755     }
33756     if (this.buttons) { 
33757         var bts= this.buttons;
33758         this.buttons = [];
33759         Roo.each(bts, function(b) {
33760             this.addButton(b);
33761         }, this);
33762     }
33763     
33764     
33765     this.addEvents({
33766         /**
33767          * @event keydown
33768          * Fires when a key is pressed
33769          * @param {Roo.BasicDialog} this
33770          * @param {Roo.EventObject} e
33771          */
33772         "keydown" : true,
33773         /**
33774          * @event move
33775          * Fires when this dialog is moved by the user.
33776          * @param {Roo.BasicDialog} this
33777          * @param {Number} x The new page X
33778          * @param {Number} y The new page Y
33779          */
33780         "move" : true,
33781         /**
33782          * @event resize
33783          * Fires when this dialog is resized by the user.
33784          * @param {Roo.BasicDialog} this
33785          * @param {Number} width The new width
33786          * @param {Number} height The new height
33787          */
33788         "resize" : true,
33789         /**
33790          * @event beforehide
33791          * Fires before this dialog is hidden.
33792          * @param {Roo.BasicDialog} this
33793          */
33794         "beforehide" : true,
33795         /**
33796          * @event hide
33797          * Fires when this dialog is hidden.
33798          * @param {Roo.BasicDialog} this
33799          */
33800         "hide" : true,
33801         /**
33802          * @event beforeshow
33803          * Fires before this dialog is shown.
33804          * @param {Roo.BasicDialog} this
33805          */
33806         "beforeshow" : true,
33807         /**
33808          * @event show
33809          * Fires when this dialog is shown.
33810          * @param {Roo.BasicDialog} this
33811          */
33812         "show" : true
33813     });
33814     el.on("keydown", this.onKeyDown, this);
33815     el.on("mousedown", this.toFront, this);
33816     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
33817     this.el.hide();
33818     Roo.DialogManager.register(this);
33819     Roo.BasicDialog.superclass.constructor.call(this);
33820 };
33821
33822 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
33823     shadowOffset: Roo.isIE ? 6 : 5,
33824     minHeight: 80,
33825     minWidth: 200,
33826     minButtonWidth: 75,
33827     defaultButton: null,
33828     buttonAlign: "right",
33829     tabTag: 'div',
33830     firstShow: true,
33831
33832     /**
33833      * Sets the dialog title text
33834      * @param {String} text The title text to display
33835      * @return {Roo.BasicDialog} this
33836      */
33837     setTitle : function(text){
33838         this.header.update(text);
33839         return this;
33840     },
33841
33842     // private
33843     closeClick : function(){
33844         this.hide();
33845     },
33846
33847     // private
33848     collapseClick : function(){
33849         this[this.collapsed ? "expand" : "collapse"]();
33850     },
33851
33852     /**
33853      * Collapses the dialog to its minimized state (only the title bar is visible).
33854      * Equivalent to the user clicking the collapse dialog button.
33855      */
33856     collapse : function(){
33857         if(!this.collapsed){
33858             this.collapsed = true;
33859             this.el.addClass("x-dlg-collapsed");
33860             this.restoreHeight = this.el.getHeight();
33861             this.resizeTo(this.el.getWidth(), this.header.getHeight());
33862         }
33863     },
33864
33865     /**
33866      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
33867      * clicking the expand dialog button.
33868      */
33869     expand : function(){
33870         if(this.collapsed){
33871             this.collapsed = false;
33872             this.el.removeClass("x-dlg-collapsed");
33873             this.resizeTo(this.el.getWidth(), this.restoreHeight);
33874         }
33875     },
33876
33877     /**
33878      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
33879      * @return {Roo.TabPanel} The tabs component
33880      */
33881     initTabs : function(){
33882         var tabs = this.getTabs();
33883         while(tabs.getTab(0)){
33884             tabs.removeTab(0);
33885         }
33886         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
33887             var dom = el.dom;
33888             tabs.addTab(Roo.id(dom), dom.title);
33889             dom.title = "";
33890         });
33891         tabs.activate(0);
33892         return tabs;
33893     },
33894
33895     // private
33896     beforeResize : function(){
33897         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
33898     },
33899
33900     // private
33901     onResize : function(){
33902         this.refreshSize();
33903         this.syncBodyHeight();
33904         this.adjustAssets();
33905         this.focus();
33906         this.fireEvent("resize", this, this.size.width, this.size.height);
33907     },
33908
33909     // private
33910     onKeyDown : function(e){
33911         if(this.isVisible()){
33912             this.fireEvent("keydown", this, e);
33913         }
33914     },
33915
33916     /**
33917      * Resizes the dialog.
33918      * @param {Number} width
33919      * @param {Number} height
33920      * @return {Roo.BasicDialog} this
33921      */
33922     resizeTo : function(width, height){
33923         this.el.setSize(width, height);
33924         this.size = {width: width, height: height};
33925         this.syncBodyHeight();
33926         if(this.fixedcenter){
33927             this.center();
33928         }
33929         if(this.isVisible()){
33930             this.constrainXY();
33931             this.adjustAssets();
33932         }
33933         this.fireEvent("resize", this, width, height);
33934         return this;
33935     },
33936
33937
33938     /**
33939      * Resizes the dialog to fit the specified content size.
33940      * @param {Number} width
33941      * @param {Number} height
33942      * @return {Roo.BasicDialog} this
33943      */
33944     setContentSize : function(w, h){
33945         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
33946         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
33947         //if(!this.el.isBorderBox()){
33948             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
33949             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
33950         //}
33951         if(this.tabs){
33952             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
33953             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
33954         }
33955         this.resizeTo(w, h);
33956         return this;
33957     },
33958
33959     /**
33960      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
33961      * executed in response to a particular key being pressed while the dialog is active.
33962      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
33963      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
33964      * @param {Function} fn The function to call
33965      * @param {Object} scope (optional) The scope of the function
33966      * @return {Roo.BasicDialog} this
33967      */
33968     addKeyListener : function(key, fn, scope){
33969         var keyCode, shift, ctrl, alt;
33970         if(typeof key == "object" && !(key instanceof Array)){
33971             keyCode = key["key"];
33972             shift = key["shift"];
33973             ctrl = key["ctrl"];
33974             alt = key["alt"];
33975         }else{
33976             keyCode = key;
33977         }
33978         var handler = function(dlg, e){
33979             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
33980                 var k = e.getKey();
33981                 if(keyCode instanceof Array){
33982                     for(var i = 0, len = keyCode.length; i < len; i++){
33983                         if(keyCode[i] == k){
33984                           fn.call(scope || window, dlg, k, e);
33985                           return;
33986                         }
33987                     }
33988                 }else{
33989                     if(k == keyCode){
33990                         fn.call(scope || window, dlg, k, e);
33991                     }
33992                 }
33993             }
33994         };
33995         this.on("keydown", handler);
33996         return this;
33997     },
33998
33999     /**
34000      * Returns the TabPanel component (creates it if it doesn't exist).
34001      * Note: If you wish to simply check for the existence of tabs without creating them,
34002      * check for a null 'tabs' property.
34003      * @return {Roo.TabPanel} The tabs component
34004      */
34005     getTabs : function(){
34006         if(!this.tabs){
34007             this.el.addClass("x-dlg-auto-tabs");
34008             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
34009             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
34010         }
34011         return this.tabs;
34012     },
34013
34014     /**
34015      * Adds a button to the footer section of the dialog.
34016      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
34017      * object or a valid Roo.DomHelper element config
34018      * @param {Function} handler The function called when the button is clicked
34019      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
34020      * @return {Roo.Button} The new button
34021      */
34022     addButton : function(config, handler, scope){
34023         var dh = Roo.DomHelper;
34024         if(!this.footer){
34025             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
34026         }
34027         if(!this.btnContainer){
34028             var tb = this.footer.createChild({
34029
34030                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
34031                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
34032             }, null, true);
34033             this.btnContainer = tb.firstChild.firstChild.firstChild;
34034         }
34035         var bconfig = {
34036             handler: handler,
34037             scope: scope,
34038             minWidth: this.minButtonWidth,
34039             hideParent:true
34040         };
34041         if(typeof config == "string"){
34042             bconfig.text = config;
34043         }else{
34044             if(config.tag){
34045                 bconfig.dhconfig = config;
34046             }else{
34047                 Roo.apply(bconfig, config);
34048             }
34049         }
34050         var fc = false;
34051         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
34052             bconfig.position = Math.max(0, bconfig.position);
34053             fc = this.btnContainer.childNodes[bconfig.position];
34054         }
34055          
34056         var btn = new Roo.Button(
34057             fc ? 
34058                 this.btnContainer.insertBefore(document.createElement("td"),fc)
34059                 : this.btnContainer.appendChild(document.createElement("td")),
34060             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
34061             bconfig
34062         );
34063         this.syncBodyHeight();
34064         if(!this.buttons){
34065             /**
34066              * Array of all the buttons that have been added to this dialog via addButton
34067              * @type Array
34068              */
34069             this.buttons = [];
34070         }
34071         this.buttons.push(btn);
34072         return btn;
34073     },
34074
34075     /**
34076      * Sets the default button to be focused when the dialog is displayed.
34077      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
34078      * @return {Roo.BasicDialog} this
34079      */
34080     setDefaultButton : function(btn){
34081         this.defaultButton = btn;
34082         return this;
34083     },
34084
34085     // private
34086     getHeaderFooterHeight : function(safe){
34087         var height = 0;
34088         if(this.header){
34089            height += this.header.getHeight();
34090         }
34091         if(this.footer){
34092            var fm = this.footer.getMargins();
34093             height += (this.footer.getHeight()+fm.top+fm.bottom);
34094         }
34095         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
34096         height += this.centerBg.getPadding("tb");
34097         return height;
34098     },
34099
34100     // private
34101     syncBodyHeight : function()
34102     {
34103         var bd = this.body, // the text
34104             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
34105             bw = this.bwrap;
34106         var height = this.size.height - this.getHeaderFooterHeight(false);
34107         bd.setHeight(height-bd.getMargins("tb"));
34108         var hh = this.header.getHeight();
34109         var h = this.size.height-hh;
34110         cb.setHeight(h);
34111         
34112         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
34113         bw.setHeight(h-cb.getPadding("tb"));
34114         
34115         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
34116         bd.setWidth(bw.getWidth(true));
34117         if(this.tabs){
34118             this.tabs.syncHeight();
34119             if(Roo.isIE){
34120                 this.tabs.el.repaint();
34121             }
34122         }
34123     },
34124
34125     /**
34126      * Restores the previous state of the dialog if Roo.state is configured.
34127      * @return {Roo.BasicDialog} this
34128      */
34129     restoreState : function(){
34130         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
34131         if(box && box.width){
34132             this.xy = [box.x, box.y];
34133             this.resizeTo(box.width, box.height);
34134         }
34135         return this;
34136     },
34137
34138     // private
34139     beforeShow : function(){
34140         this.expand();
34141         if(this.fixedcenter){
34142             this.xy = this.el.getCenterXY(true);
34143         }
34144         if(this.modal){
34145             Roo.get(document.body).addClass("x-body-masked");
34146             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34147             this.mask.show();
34148         }
34149         this.constrainXY();
34150     },
34151
34152     // private
34153     animShow : function(){
34154         var b = Roo.get(this.animateTarget).getBox();
34155         this.proxy.setSize(b.width, b.height);
34156         this.proxy.setLocation(b.x, b.y);
34157         this.proxy.show();
34158         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
34159                     true, .35, this.showEl.createDelegate(this));
34160     },
34161
34162     /**
34163      * Shows the dialog.
34164      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
34165      * @return {Roo.BasicDialog} this
34166      */
34167     show : function(animateTarget){
34168         if (this.fireEvent("beforeshow", this) === false){
34169             return;
34170         }
34171         if(this.syncHeightBeforeShow){
34172             this.syncBodyHeight();
34173         }else if(this.firstShow){
34174             this.firstShow = false;
34175             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
34176         }
34177         this.animateTarget = animateTarget || this.animateTarget;
34178         if(!this.el.isVisible()){
34179             this.beforeShow();
34180             if(this.animateTarget && Roo.get(this.animateTarget)){
34181                 this.animShow();
34182             }else{
34183                 this.showEl();
34184             }
34185         }
34186         return this;
34187     },
34188
34189     // private
34190     showEl : function(){
34191         this.proxy.hide();
34192         this.el.setXY(this.xy);
34193         this.el.show();
34194         this.adjustAssets(true);
34195         this.toFront();
34196         this.focus();
34197         // IE peekaboo bug - fix found by Dave Fenwick
34198         if(Roo.isIE){
34199             this.el.repaint();
34200         }
34201         this.fireEvent("show", this);
34202     },
34203
34204     /**
34205      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
34206      * dialog itself will receive focus.
34207      */
34208     focus : function(){
34209         if(this.defaultButton){
34210             this.defaultButton.focus();
34211         }else{
34212             this.focusEl.focus();
34213         }
34214     },
34215
34216     // private
34217     constrainXY : function(){
34218         if(this.constraintoviewport !== false){
34219             if(!this.viewSize){
34220                 if(this.container){
34221                     var s = this.container.getSize();
34222                     this.viewSize = [s.width, s.height];
34223                 }else{
34224                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
34225                 }
34226             }
34227             var s = Roo.get(this.container||document).getScroll();
34228
34229             var x = this.xy[0], y = this.xy[1];
34230             var w = this.size.width, h = this.size.height;
34231             var vw = this.viewSize[0], vh = this.viewSize[1];
34232             // only move it if it needs it
34233             var moved = false;
34234             // first validate right/bottom
34235             if(x + w > vw+s.left){
34236                 x = vw - w;
34237                 moved = true;
34238             }
34239             if(y + h > vh+s.top){
34240                 y = vh - h;
34241                 moved = true;
34242             }
34243             // then make sure top/left isn't negative
34244             if(x < s.left){
34245                 x = s.left;
34246                 moved = true;
34247             }
34248             if(y < s.top){
34249                 y = s.top;
34250                 moved = true;
34251             }
34252             if(moved){
34253                 // cache xy
34254                 this.xy = [x, y];
34255                 if(this.isVisible()){
34256                     this.el.setLocation(x, y);
34257                     this.adjustAssets();
34258                 }
34259             }
34260         }
34261     },
34262
34263     // private
34264     onDrag : function(){
34265         if(!this.proxyDrag){
34266             this.xy = this.el.getXY();
34267             this.adjustAssets();
34268         }
34269     },
34270
34271     // private
34272     adjustAssets : function(doShow){
34273         var x = this.xy[0], y = this.xy[1];
34274         var w = this.size.width, h = this.size.height;
34275         if(doShow === true){
34276             if(this.shadow){
34277                 this.shadow.show(this.el);
34278             }
34279             if(this.shim){
34280                 this.shim.show();
34281             }
34282         }
34283         if(this.shadow && this.shadow.isVisible()){
34284             this.shadow.show(this.el);
34285         }
34286         if(this.shim && this.shim.isVisible()){
34287             this.shim.setBounds(x, y, w, h);
34288         }
34289     },
34290
34291     // private
34292     adjustViewport : function(w, h){
34293         if(!w || !h){
34294             w = Roo.lib.Dom.getViewWidth();
34295             h = Roo.lib.Dom.getViewHeight();
34296         }
34297         // cache the size
34298         this.viewSize = [w, h];
34299         if(this.modal && this.mask.isVisible()){
34300             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
34301             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34302         }
34303         if(this.isVisible()){
34304             this.constrainXY();
34305         }
34306     },
34307
34308     /**
34309      * Destroys this dialog and all its supporting elements (including any tabs, shim,
34310      * shadow, proxy, mask, etc.)  Also removes all event listeners.
34311      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
34312      */
34313     destroy : function(removeEl){
34314         if(this.isVisible()){
34315             this.animateTarget = null;
34316             this.hide();
34317         }
34318         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
34319         if(this.tabs){
34320             this.tabs.destroy(removeEl);
34321         }
34322         Roo.destroy(
34323              this.shim,
34324              this.proxy,
34325              this.resizer,
34326              this.close,
34327              this.mask
34328         );
34329         if(this.dd){
34330             this.dd.unreg();
34331         }
34332         if(this.buttons){
34333            for(var i = 0, len = this.buttons.length; i < len; i++){
34334                this.buttons[i].destroy();
34335            }
34336         }
34337         this.el.removeAllListeners();
34338         if(removeEl === true){
34339             this.el.update("");
34340             this.el.remove();
34341         }
34342         Roo.DialogManager.unregister(this);
34343     },
34344
34345     // private
34346     startMove : function(){
34347         if(this.proxyDrag){
34348             this.proxy.show();
34349         }
34350         if(this.constraintoviewport !== false){
34351             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
34352         }
34353     },
34354
34355     // private
34356     endMove : function(){
34357         if(!this.proxyDrag){
34358             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
34359         }else{
34360             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
34361             this.proxy.hide();
34362         }
34363         this.refreshSize();
34364         this.adjustAssets();
34365         this.focus();
34366         this.fireEvent("move", this, this.xy[0], this.xy[1]);
34367     },
34368
34369     /**
34370      * Brings this dialog to the front of any other visible dialogs
34371      * @return {Roo.BasicDialog} this
34372      */
34373     toFront : function(){
34374         Roo.DialogManager.bringToFront(this);
34375         return this;
34376     },
34377
34378     /**
34379      * Sends this dialog to the back (under) of any other visible dialogs
34380      * @return {Roo.BasicDialog} this
34381      */
34382     toBack : function(){
34383         Roo.DialogManager.sendToBack(this);
34384         return this;
34385     },
34386
34387     /**
34388      * Centers this dialog in the viewport
34389      * @return {Roo.BasicDialog} this
34390      */
34391     center : function(){
34392         var xy = this.el.getCenterXY(true);
34393         this.moveTo(xy[0], xy[1]);
34394         return this;
34395     },
34396
34397     /**
34398      * Moves the dialog's top-left corner to the specified point
34399      * @param {Number} x
34400      * @param {Number} y
34401      * @return {Roo.BasicDialog} this
34402      */
34403     moveTo : function(x, y){
34404         this.xy = [x,y];
34405         if(this.isVisible()){
34406             this.el.setXY(this.xy);
34407             this.adjustAssets();
34408         }
34409         return this;
34410     },
34411
34412     /**
34413      * Aligns the dialog to the specified element
34414      * @param {String/HTMLElement/Roo.Element} element The element to align to.
34415      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
34416      * @param {Array} offsets (optional) Offset the positioning by [x, y]
34417      * @return {Roo.BasicDialog} this
34418      */
34419     alignTo : function(element, position, offsets){
34420         this.xy = this.el.getAlignToXY(element, position, offsets);
34421         if(this.isVisible()){
34422             this.el.setXY(this.xy);
34423             this.adjustAssets();
34424         }
34425         return this;
34426     },
34427
34428     /**
34429      * Anchors an element to another element and realigns it when the window is resized.
34430      * @param {String/HTMLElement/Roo.Element} element The element to align to.
34431      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
34432      * @param {Array} offsets (optional) Offset the positioning by [x, y]
34433      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
34434      * is a number, it is used as the buffer delay (defaults to 50ms).
34435      * @return {Roo.BasicDialog} this
34436      */
34437     anchorTo : function(el, alignment, offsets, monitorScroll){
34438         var action = function(){
34439             this.alignTo(el, alignment, offsets);
34440         };
34441         Roo.EventManager.onWindowResize(action, this);
34442         var tm = typeof monitorScroll;
34443         if(tm != 'undefined'){
34444             Roo.EventManager.on(window, 'scroll', action, this,
34445                 {buffer: tm == 'number' ? monitorScroll : 50});
34446         }
34447         action.call(this);
34448         return this;
34449     },
34450
34451     /**
34452      * Returns true if the dialog is visible
34453      * @return {Boolean}
34454      */
34455     isVisible : function(){
34456         return this.el.isVisible();
34457     },
34458
34459     // private
34460     animHide : function(callback){
34461         var b = Roo.get(this.animateTarget).getBox();
34462         this.proxy.show();
34463         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
34464         this.el.hide();
34465         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
34466                     this.hideEl.createDelegate(this, [callback]));
34467     },
34468
34469     /**
34470      * Hides the dialog.
34471      * @param {Function} callback (optional) Function to call when the dialog is hidden
34472      * @return {Roo.BasicDialog} this
34473      */
34474     hide : function(callback){
34475         if (this.fireEvent("beforehide", this) === false){
34476             return;
34477         }
34478         if(this.shadow){
34479             this.shadow.hide();
34480         }
34481         if(this.shim) {
34482           this.shim.hide();
34483         }
34484         // sometimes animateTarget seems to get set.. causing problems...
34485         // this just double checks..
34486         if(this.animateTarget && Roo.get(this.animateTarget)) {
34487            this.animHide(callback);
34488         }else{
34489             this.el.hide();
34490             this.hideEl(callback);
34491         }
34492         return this;
34493     },
34494
34495     // private
34496     hideEl : function(callback){
34497         this.proxy.hide();
34498         if(this.modal){
34499             this.mask.hide();
34500             Roo.get(document.body).removeClass("x-body-masked");
34501         }
34502         this.fireEvent("hide", this);
34503         if(typeof callback == "function"){
34504             callback();
34505         }
34506     },
34507
34508     // private
34509     hideAction : function(){
34510         this.setLeft("-10000px");
34511         this.setTop("-10000px");
34512         this.setStyle("visibility", "hidden");
34513     },
34514
34515     // private
34516     refreshSize : function(){
34517         this.size = this.el.getSize();
34518         this.xy = this.el.getXY();
34519         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
34520     },
34521
34522     // private
34523     // z-index is managed by the DialogManager and may be overwritten at any time
34524     setZIndex : function(index){
34525         if(this.modal){
34526             this.mask.setStyle("z-index", index);
34527         }
34528         if(this.shim){
34529             this.shim.setStyle("z-index", ++index);
34530         }
34531         if(this.shadow){
34532             this.shadow.setZIndex(++index);
34533         }
34534         this.el.setStyle("z-index", ++index);
34535         if(this.proxy){
34536             this.proxy.setStyle("z-index", ++index);
34537         }
34538         if(this.resizer){
34539             this.resizer.proxy.setStyle("z-index", ++index);
34540         }
34541
34542         this.lastZIndex = index;
34543     },
34544
34545     /**
34546      * Returns the element for this dialog
34547      * @return {Roo.Element} The underlying dialog Element
34548      */
34549     getEl : function(){
34550         return this.el;
34551     }
34552 });
34553
34554 /**
34555  * @class Roo.DialogManager
34556  * Provides global access to BasicDialogs that have been created and
34557  * support for z-indexing (layering) multiple open dialogs.
34558  */
34559 Roo.DialogManager = function(){
34560     var list = {};
34561     var accessList = [];
34562     var front = null;
34563
34564     // private
34565     var sortDialogs = function(d1, d2){
34566         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
34567     };
34568
34569     // private
34570     var orderDialogs = function(){
34571         accessList.sort(sortDialogs);
34572         var seed = Roo.DialogManager.zseed;
34573         for(var i = 0, len = accessList.length; i < len; i++){
34574             var dlg = accessList[i];
34575             if(dlg){
34576                 dlg.setZIndex(seed + (i*10));
34577             }
34578         }
34579     };
34580
34581     return {
34582         /**
34583          * The starting z-index for BasicDialogs (defaults to 9000)
34584          * @type Number The z-index value
34585          */
34586         zseed : 9000,
34587
34588         // private
34589         register : function(dlg){
34590             list[dlg.id] = dlg;
34591             accessList.push(dlg);
34592         },
34593
34594         // private
34595         unregister : function(dlg){
34596             delete list[dlg.id];
34597             var i=0;
34598             var len=0;
34599             if(!accessList.indexOf){
34600                 for(  i = 0, len = accessList.length; i < len; i++){
34601                     if(accessList[i] == dlg){
34602                         accessList.splice(i, 1);
34603                         return;
34604                     }
34605                 }
34606             }else{
34607                  i = accessList.indexOf(dlg);
34608                 if(i != -1){
34609                     accessList.splice(i, 1);
34610                 }
34611             }
34612         },
34613
34614         /**
34615          * Gets a registered dialog by id
34616          * @param {String/Object} id The id of the dialog or a dialog
34617          * @return {Roo.BasicDialog} this
34618          */
34619         get : function(id){
34620             return typeof id == "object" ? id : list[id];
34621         },
34622
34623         /**
34624          * Brings the specified dialog to the front
34625          * @param {String/Object} dlg The id of the dialog or a dialog
34626          * @return {Roo.BasicDialog} this
34627          */
34628         bringToFront : function(dlg){
34629             dlg = this.get(dlg);
34630             if(dlg != front){
34631                 front = dlg;
34632                 dlg._lastAccess = new Date().getTime();
34633                 orderDialogs();
34634             }
34635             return dlg;
34636         },
34637
34638         /**
34639          * Sends the specified dialog to the back
34640          * @param {String/Object} dlg The id of the dialog or a dialog
34641          * @return {Roo.BasicDialog} this
34642          */
34643         sendToBack : function(dlg){
34644             dlg = this.get(dlg);
34645             dlg._lastAccess = -(new Date().getTime());
34646             orderDialogs();
34647             return dlg;
34648         },
34649
34650         /**
34651          * Hides all dialogs
34652          */
34653         hideAll : function(){
34654             for(var id in list){
34655                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
34656                     list[id].hide();
34657                 }
34658             }
34659         }
34660     };
34661 }();
34662
34663 /**
34664  * @class Roo.LayoutDialog
34665  * @extends Roo.BasicDialog
34666  * @children Roo.ContentPanel
34667  * @parent builder none
34668  * Dialog which provides adjustments for working with a layout in a Dialog.
34669  * Add your necessary layout config options to the dialog's config.<br>
34670  * Example usage (including a nested layout):
34671  * <pre><code>
34672 if(!dialog){
34673     dialog = new Roo.LayoutDialog("download-dlg", {
34674         modal: true,
34675         width:600,
34676         height:450,
34677         shadow:true,
34678         minWidth:500,
34679         minHeight:350,
34680         autoTabs:true,
34681         proxyDrag:true,
34682         // layout config merges with the dialog config
34683         center:{
34684             tabPosition: "top",
34685             alwaysShowTabs: true
34686         }
34687     });
34688     dialog.addKeyListener(27, dialog.hide, dialog);
34689     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
34690     dialog.addButton("Build It!", this.getDownload, this);
34691
34692     // we can even add nested layouts
34693     var innerLayout = new Roo.BorderLayout("dl-inner", {
34694         east: {
34695             initialSize: 200,
34696             autoScroll:true,
34697             split:true
34698         },
34699         center: {
34700             autoScroll:true
34701         }
34702     });
34703     innerLayout.beginUpdate();
34704     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
34705     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
34706     innerLayout.endUpdate(true);
34707
34708     var layout = dialog.getLayout();
34709     layout.beginUpdate();
34710     layout.add("center", new Roo.ContentPanel("standard-panel",
34711                         {title: "Download the Source", fitToFrame:true}));
34712     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
34713                {title: "Build your own roo.js"}));
34714     layout.getRegion("center").showPanel(sp);
34715     layout.endUpdate();
34716 }
34717 </code></pre>
34718     * @constructor
34719     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
34720     * @param {Object} config configuration options
34721   */
34722 Roo.LayoutDialog = function(el, cfg){
34723     
34724     var config=  cfg;
34725     if (typeof(cfg) == 'undefined') {
34726         config = Roo.apply({}, el);
34727         // not sure why we use documentElement here.. - it should always be body.
34728         // IE7 borks horribly if we use documentElement.
34729         // webkit also does not like documentElement - it creates a body element...
34730         el = Roo.get( document.body || document.documentElement ).createChild();
34731         //config.autoCreate = true;
34732     }
34733     
34734     
34735     config.autoTabs = false;
34736     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
34737     this.body.setStyle({overflow:"hidden", position:"relative"});
34738     this.layout = new Roo.BorderLayout(this.body.dom, config);
34739     this.layout.monitorWindowResize = false;
34740     this.el.addClass("x-dlg-auto-layout");
34741     // fix case when center region overwrites center function
34742     this.center = Roo.BasicDialog.prototype.center;
34743     this.on("show", this.layout.layout, this.layout, true);
34744     if (config.items) {
34745         var xitems = config.items;
34746         delete config.items;
34747         Roo.each(xitems, this.addxtype, this);
34748     }
34749     
34750     
34751 };
34752 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
34753     
34754     
34755     /**
34756      * @cfg {Roo.LayoutRegion} east  
34757      */
34758     /**
34759      * @cfg {Roo.LayoutRegion} west
34760      */
34761     /**
34762      * @cfg {Roo.LayoutRegion} south
34763      */
34764     /**
34765      * @cfg {Roo.LayoutRegion} north
34766      */
34767     /**
34768      * @cfg {Roo.LayoutRegion} center
34769      */
34770     /**
34771      * @cfg {Roo.Button} buttons[]  Bottom buttons..
34772      */
34773     
34774     
34775     /**
34776      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
34777      * @deprecated
34778      */
34779     endUpdate : function(){
34780         this.layout.endUpdate();
34781     },
34782
34783     /**
34784      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
34785      *  @deprecated
34786      */
34787     beginUpdate : function(){
34788         this.layout.beginUpdate();
34789     },
34790
34791     /**
34792      * Get the BorderLayout for this dialog
34793      * @return {Roo.BorderLayout}
34794      */
34795     getLayout : function(){
34796         return this.layout;
34797     },
34798
34799     showEl : function(){
34800         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
34801         if(Roo.isIE7){
34802             this.layout.layout();
34803         }
34804     },
34805
34806     // private
34807     // Use the syncHeightBeforeShow config option to control this automatically
34808     syncBodyHeight : function(){
34809         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
34810         if(this.layout){this.layout.layout();}
34811     },
34812     
34813       /**
34814      * Add an xtype element (actually adds to the layout.)
34815      * @return {Object} xdata xtype object data.
34816      */
34817     
34818     addxtype : function(c) {
34819         return this.layout.addxtype(c);
34820     }
34821 });/*
34822  * Based on:
34823  * Ext JS Library 1.1.1
34824  * Copyright(c) 2006-2007, Ext JS, LLC.
34825  *
34826  * Originally Released Under LGPL - original licence link has changed is not relivant.
34827  *
34828  * Fork - LGPL
34829  * <script type="text/javascript">
34830  */
34831  
34832 /**
34833  * @class Roo.MessageBox
34834  * @static
34835  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
34836  * Example usage:
34837  *<pre><code>
34838 // Basic alert:
34839 Roo.Msg.alert('Status', 'Changes saved successfully.');
34840
34841 // Prompt for user data:
34842 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
34843     if (btn == 'ok'){
34844         // process text value...
34845     }
34846 });
34847
34848 // Show a dialog using config options:
34849 Roo.Msg.show({
34850    title:'Save Changes?',
34851    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
34852    buttons: Roo.Msg.YESNOCANCEL,
34853    fn: processResult,
34854    animEl: 'elId'
34855 });
34856 </code></pre>
34857  * @static
34858  */
34859 Roo.MessageBox = function(){
34860     var dlg, opt, mask, waitTimer;
34861     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
34862     var buttons, activeTextEl, bwidth;
34863
34864     // private
34865     var handleButton = function(button){
34866         dlg.hide();
34867         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
34868     };
34869
34870     // private
34871     var handleHide = function(){
34872         if(opt && opt.cls){
34873             dlg.el.removeClass(opt.cls);
34874         }
34875         if(waitTimer){
34876             Roo.TaskMgr.stop(waitTimer);
34877             waitTimer = null;
34878         }
34879     };
34880
34881     // private
34882     var updateButtons = function(b){
34883         var width = 0;
34884         if(!b){
34885             buttons["ok"].hide();
34886             buttons["cancel"].hide();
34887             buttons["yes"].hide();
34888             buttons["no"].hide();
34889             dlg.footer.dom.style.display = 'none';
34890             return width;
34891         }
34892         dlg.footer.dom.style.display = '';
34893         for(var k in buttons){
34894             if(typeof buttons[k] != "function"){
34895                 if(b[k]){
34896                     buttons[k].show();
34897                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
34898                     width += buttons[k].el.getWidth()+15;
34899                 }else{
34900                     buttons[k].hide();
34901                 }
34902             }
34903         }
34904         return width;
34905     };
34906
34907     // private
34908     var handleEsc = function(d, k, e){
34909         if(opt && opt.closable !== false){
34910             dlg.hide();
34911         }
34912         if(e){
34913             e.stopEvent();
34914         }
34915     };
34916
34917     return {
34918         /**
34919          * Returns a reference to the underlying {@link Roo.BasicDialog} element
34920          * @return {Roo.BasicDialog} The BasicDialog element
34921          */
34922         getDialog : function(){
34923            if(!dlg){
34924                 dlg = new Roo.BasicDialog("x-msg-box", {
34925                     autoCreate : true,
34926                     shadow: true,
34927                     draggable: true,
34928                     resizable:false,
34929                     constraintoviewport:false,
34930                     fixedcenter:true,
34931                     collapsible : false,
34932                     shim:true,
34933                     modal: true,
34934                     width:400, height:100,
34935                     buttonAlign:"center",
34936                     closeClick : function(){
34937                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
34938                             handleButton("no");
34939                         }else{
34940                             handleButton("cancel");
34941                         }
34942                     }
34943                 });
34944               
34945                 dlg.on("hide", handleHide);
34946                 mask = dlg.mask;
34947                 dlg.addKeyListener(27, handleEsc);
34948                 buttons = {};
34949                 var bt = this.buttonText;
34950                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
34951                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
34952                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
34953                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
34954                 bodyEl = dlg.body.createChild({
34955
34956                     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>'
34957                 });
34958                 msgEl = bodyEl.dom.firstChild;
34959                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
34960                 textboxEl.enableDisplayMode();
34961                 textboxEl.addKeyListener([10,13], function(){
34962                     if(dlg.isVisible() && opt && opt.buttons){
34963                         if(opt.buttons.ok){
34964                             handleButton("ok");
34965                         }else if(opt.buttons.yes){
34966                             handleButton("yes");
34967                         }
34968                     }
34969                 });
34970                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
34971                 textareaEl.enableDisplayMode();
34972                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
34973                 progressEl.enableDisplayMode();
34974                 var pf = progressEl.dom.firstChild;
34975                 if (pf) {
34976                     pp = Roo.get(pf.firstChild);
34977                     pp.setHeight(pf.offsetHeight);
34978                 }
34979                 
34980             }
34981             return dlg;
34982         },
34983
34984         /**
34985          * Updates the message box body text
34986          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
34987          * the XHTML-compliant non-breaking space character '&amp;#160;')
34988          * @return {Roo.MessageBox} This message box
34989          */
34990         updateText : function(text){
34991             if(!dlg.isVisible() && !opt.width){
34992                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
34993             }
34994             msgEl.innerHTML = text || '&#160;';
34995       
34996             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
34997             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
34998             var w = Math.max(
34999                     Math.min(opt.width || cw , this.maxWidth), 
35000                     Math.max(opt.minWidth || this.minWidth, bwidth)
35001             );
35002             if(opt.prompt){
35003                 activeTextEl.setWidth(w);
35004             }
35005             if(dlg.isVisible()){
35006                 dlg.fixedcenter = false;
35007             }
35008             // to big, make it scroll. = But as usual stupid IE does not support
35009             // !important..
35010             
35011             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
35012                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
35013                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
35014             } else {
35015                 bodyEl.dom.style.height = '';
35016                 bodyEl.dom.style.overflowY = '';
35017             }
35018             if (cw > w) {
35019                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
35020             } else {
35021                 bodyEl.dom.style.overflowX = '';
35022             }
35023             
35024             dlg.setContentSize(w, bodyEl.getHeight());
35025             if(dlg.isVisible()){
35026                 dlg.fixedcenter = true;
35027             }
35028             return this;
35029         },
35030
35031         /**
35032          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
35033          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
35034          * @param {Number} value Any number between 0 and 1 (e.g., .5)
35035          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
35036          * @return {Roo.MessageBox} This message box
35037          */
35038         updateProgress : function(value, text){
35039             if(text){
35040                 this.updateText(text);
35041             }
35042             if (pp) { // weird bug on my firefox - for some reason this is not defined
35043                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
35044             }
35045             return this;
35046         },        
35047
35048         /**
35049          * Returns true if the message box is currently displayed
35050          * @return {Boolean} True if the message box is visible, else false
35051          */
35052         isVisible : function(){
35053             return dlg && dlg.isVisible();  
35054         },
35055
35056         /**
35057          * Hides the message box if it is displayed
35058          */
35059         hide : function(){
35060             if(this.isVisible()){
35061                 dlg.hide();
35062             }  
35063         },
35064
35065         /**
35066          * Displays a new message box, or reinitializes an existing message box, based on the config options
35067          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
35068          * The following config object properties are supported:
35069          * <pre>
35070 Property    Type             Description
35071 ----------  ---------------  ------------------------------------------------------------------------------------
35072 animEl            String/Element   An id or Element from which the message box should animate as it opens and
35073                                    closes (defaults to undefined)
35074 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
35075                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
35076 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
35077                                    progress and wait dialogs will ignore this property and always hide the
35078                                    close button as they can only be closed programmatically.
35079 cls               String           A custom CSS class to apply to the message box element
35080 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
35081                                    displayed (defaults to 75)
35082 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
35083                                    function will be btn (the name of the button that was clicked, if applicable,
35084                                    e.g. "ok"), and text (the value of the active text field, if applicable).
35085                                    Progress and wait dialogs will ignore this option since they do not respond to
35086                                    user actions and can only be closed programmatically, so any required function
35087                                    should be called by the same code after it closes the dialog.
35088 icon              String           A CSS class that provides a background image to be used as an icon for
35089                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
35090 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
35091 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
35092 modal             Boolean          False to allow user interaction with the page while the message box is
35093                                    displayed (defaults to true)
35094 msg               String           A string that will replace the existing message box body text (defaults
35095                                    to the XHTML-compliant non-breaking space character '&#160;')
35096 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
35097 progress          Boolean          True to display a progress bar (defaults to false)
35098 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
35099 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
35100 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
35101 title             String           The title text
35102 value             String           The string value to set into the active textbox element if displayed
35103 wait              Boolean          True to display a progress bar (defaults to false)
35104 width             Number           The width of the dialog in pixels
35105 </pre>
35106          *
35107          * Example usage:
35108          * <pre><code>
35109 Roo.Msg.show({
35110    title: 'Address',
35111    msg: 'Please enter your address:',
35112    width: 300,
35113    buttons: Roo.MessageBox.OKCANCEL,
35114    multiline: true,
35115    fn: saveAddress,
35116    animEl: 'addAddressBtn'
35117 });
35118 </code></pre>
35119          * @param {Object} config Configuration options
35120          * @return {Roo.MessageBox} This message box
35121          */
35122         show : function(options)
35123         {
35124             
35125             // this causes nightmares if you show one dialog after another
35126             // especially on callbacks..
35127              
35128             if(this.isVisible()){
35129                 
35130                 this.hide();
35131                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
35132                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
35133                 Roo.log("New Dialog Message:" +  options.msg )
35134                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
35135                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
35136                 
35137             }
35138             var d = this.getDialog();
35139             opt = options;
35140             d.setTitle(opt.title || "&#160;");
35141             d.close.setDisplayed(opt.closable !== false);
35142             activeTextEl = textboxEl;
35143             opt.prompt = opt.prompt || (opt.multiline ? true : false);
35144             if(opt.prompt){
35145                 if(opt.multiline){
35146                     textboxEl.hide();
35147                     textareaEl.show();
35148                     textareaEl.setHeight(typeof opt.multiline == "number" ?
35149                         opt.multiline : this.defaultTextHeight);
35150                     activeTextEl = textareaEl;
35151                 }else{
35152                     textboxEl.show();
35153                     textareaEl.hide();
35154                 }
35155             }else{
35156                 textboxEl.hide();
35157                 textareaEl.hide();
35158             }
35159             progressEl.setDisplayed(opt.progress === true);
35160             this.updateProgress(0);
35161             activeTextEl.dom.value = opt.value || "";
35162             if(opt.prompt){
35163                 dlg.setDefaultButton(activeTextEl);
35164             }else{
35165                 var bs = opt.buttons;
35166                 var db = null;
35167                 if(bs && bs.ok){
35168                     db = buttons["ok"];
35169                 }else if(bs && bs.yes){
35170                     db = buttons["yes"];
35171                 }
35172                 dlg.setDefaultButton(db);
35173             }
35174             bwidth = updateButtons(opt.buttons);
35175             this.updateText(opt.msg);
35176             if(opt.cls){
35177                 d.el.addClass(opt.cls);
35178             }
35179             d.proxyDrag = opt.proxyDrag === true;
35180             d.modal = opt.modal !== false;
35181             d.mask = opt.modal !== false ? mask : false;
35182             if(!d.isVisible()){
35183                 // force it to the end of the z-index stack so it gets a cursor in FF
35184                 document.body.appendChild(dlg.el.dom);
35185                 d.animateTarget = null;
35186                 d.show(options.animEl);
35187             }
35188             dlg.toFront();
35189             return this;
35190         },
35191
35192         /**
35193          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
35194          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
35195          * and closing the message box when the process is complete.
35196          * @param {String} title The title bar text
35197          * @param {String} msg The message box body text
35198          * @return {Roo.MessageBox} This message box
35199          */
35200         progress : function(title, msg){
35201             this.show({
35202                 title : title,
35203                 msg : msg,
35204                 buttons: false,
35205                 progress:true,
35206                 closable:false,
35207                 minWidth: this.minProgressWidth,
35208                 modal : true
35209             });
35210             return this;
35211         },
35212
35213         /**
35214          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
35215          * If a callback function is passed it will be called after the user clicks the button, and the
35216          * id of the button that was clicked will be passed as the only parameter to the callback
35217          * (could also be the top-right close button).
35218          * @param {String} title The title bar text
35219          * @param {String} msg The message box body text
35220          * @param {Function} fn (optional) The callback function invoked after the message box is closed
35221          * @param {Object} scope (optional) The scope of the callback function
35222          * @return {Roo.MessageBox} This message box
35223          */
35224         alert : function(title, msg, fn, scope){
35225             this.show({
35226                 title : title,
35227                 msg : msg,
35228                 buttons: this.OK,
35229                 fn: fn,
35230                 scope : scope,
35231                 modal : true
35232             });
35233             return this;
35234         },
35235
35236         /**
35237          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
35238          * interaction while waiting for a long-running process to complete that does not have defined intervals.
35239          * You are responsible for closing the message box when the process is complete.
35240          * @param {String} msg The message box body text
35241          * @param {String} title (optional) The title bar text
35242          * @return {Roo.MessageBox} This message box
35243          */
35244         wait : function(msg, title){
35245             this.show({
35246                 title : title,
35247                 msg : msg,
35248                 buttons: false,
35249                 closable:false,
35250                 progress:true,
35251                 modal:true,
35252                 width:300,
35253                 wait:true
35254             });
35255             waitTimer = Roo.TaskMgr.start({
35256                 run: function(i){
35257                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
35258                 },
35259                 interval: 1000
35260             });
35261             return this;
35262         },
35263
35264         /**
35265          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
35266          * If a callback function is passed it will be called after the user clicks either button, and the id of the
35267          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
35268          * @param {String} title The title bar text
35269          * @param {String} msg The message box body text
35270          * @param {Function} fn (optional) The callback function invoked after the message box is closed
35271          * @param {Object} scope (optional) The scope of the callback function
35272          * @return {Roo.MessageBox} This message box
35273          */
35274         confirm : function(title, msg, fn, scope){
35275             this.show({
35276                 title : title,
35277                 msg : msg,
35278                 buttons: this.YESNO,
35279                 fn: fn,
35280                 scope : scope,
35281                 modal : true
35282             });
35283             return this;
35284         },
35285
35286         /**
35287          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
35288          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
35289          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
35290          * (could also be the top-right close button) and the text that was entered will be passed as the two
35291          * parameters to the callback.
35292          * @param {String} title The title bar text
35293          * @param {String} msg The message box body text
35294          * @param {Function} fn (optional) The callback function invoked after the message box is closed
35295          * @param {Object} scope (optional) The scope of the callback function
35296          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
35297          * property, or the height in pixels to create the textbox (defaults to false / single-line)
35298          * @return {Roo.MessageBox} This message box
35299          */
35300         prompt : function(title, msg, fn, scope, multiline){
35301             this.show({
35302                 title : title,
35303                 msg : msg,
35304                 buttons: this.OKCANCEL,
35305                 fn: fn,
35306                 minWidth:250,
35307                 scope : scope,
35308                 prompt:true,
35309                 multiline: multiline,
35310                 modal : true
35311             });
35312             return this;
35313         },
35314
35315         /**
35316          * Button config that displays a single OK button
35317          * @type Object
35318          */
35319         OK : {ok:true},
35320         /**
35321          * Button config that displays Yes and No buttons
35322          * @type Object
35323          */
35324         YESNO : {yes:true, no:true},
35325         /**
35326          * Button config that displays OK and Cancel buttons
35327          * @type Object
35328          */
35329         OKCANCEL : {ok:true, cancel:true},
35330         /**
35331          * Button config that displays Yes, No and Cancel buttons
35332          * @type Object
35333          */
35334         YESNOCANCEL : {yes:true, no:true, cancel:true},
35335
35336         /**
35337          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
35338          * @type Number
35339          */
35340         defaultTextHeight : 75,
35341         /**
35342          * The maximum width in pixels of the message box (defaults to 600)
35343          * @type Number
35344          */
35345         maxWidth : 600,
35346         /**
35347          * The minimum width in pixels of the message box (defaults to 100)
35348          * @type Number
35349          */
35350         minWidth : 100,
35351         /**
35352          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
35353          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
35354          * @type Number
35355          */
35356         minProgressWidth : 250,
35357         /**
35358          * An object containing the default button text strings that can be overriden for localized language support.
35359          * Supported properties are: ok, cancel, yes and no.
35360          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
35361          * @type Object
35362          */
35363         buttonText : {
35364             ok : "OK",
35365             cancel : "Cancel",
35366             yes : "Yes",
35367             no : "No"
35368         }
35369     };
35370 }();
35371
35372 /**
35373  * Shorthand for {@link Roo.MessageBox}
35374  */
35375 Roo.Msg = Roo.MessageBox;/*
35376  * Based on:
35377  * Ext JS Library 1.1.1
35378  * Copyright(c) 2006-2007, Ext JS, LLC.
35379  *
35380  * Originally Released Under LGPL - original licence link has changed is not relivant.
35381  *
35382  * Fork - LGPL
35383  * <script type="text/javascript">
35384  */
35385 /**
35386  * @class Roo.QuickTips
35387  * Provides attractive and customizable tooltips for any element.
35388  * @static
35389  */
35390 Roo.QuickTips = function(){
35391     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
35392     var ce, bd, xy, dd;
35393     var visible = false, disabled = true, inited = false;
35394     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
35395     
35396     var onOver = function(e){
35397         if(disabled){
35398             return;
35399         }
35400         var t = e.getTarget();
35401         if(!t || t.nodeType !== 1 || t == document || t == document.body){
35402             return;
35403         }
35404         if(ce && t == ce.el){
35405             clearTimeout(hideProc);
35406             return;
35407         }
35408         if(t && tagEls[t.id]){
35409             tagEls[t.id].el = t;
35410             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
35411             return;
35412         }
35413         var ttp, et = Roo.fly(t);
35414         var ns = cfg.namespace;
35415         if(tm.interceptTitles && t.title){
35416             ttp = t.title;
35417             t.qtip = ttp;
35418             t.removeAttribute("title");
35419             e.preventDefault();
35420         }else{
35421             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
35422         }
35423         if(ttp){
35424             showProc = show.defer(tm.showDelay, tm, [{
35425                 el: t, 
35426                 text: ttp.replace(/\\n/g,'<br/>'),
35427                 width: et.getAttributeNS(ns, cfg.width),
35428                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
35429                 title: et.getAttributeNS(ns, cfg.title),
35430                     cls: et.getAttributeNS(ns, cfg.cls)
35431             }]);
35432         }
35433     };
35434     
35435     var onOut = function(e){
35436         clearTimeout(showProc);
35437         var t = e.getTarget();
35438         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
35439             hideProc = setTimeout(hide, tm.hideDelay);
35440         }
35441     };
35442     
35443     var onMove = function(e){
35444         if(disabled){
35445             return;
35446         }
35447         xy = e.getXY();
35448         xy[1] += 18;
35449         if(tm.trackMouse && ce){
35450             el.setXY(xy);
35451         }
35452     };
35453     
35454     var onDown = function(e){
35455         clearTimeout(showProc);
35456         clearTimeout(hideProc);
35457         if(!e.within(el)){
35458             if(tm.hideOnClick){
35459                 hide();
35460                 tm.disable();
35461                 tm.enable.defer(100, tm);
35462             }
35463         }
35464     };
35465     
35466     var getPad = function(){
35467         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
35468     };
35469
35470     var show = function(o){
35471         if(disabled){
35472             return;
35473         }
35474         clearTimeout(dismissProc);
35475         ce = o;
35476         if(removeCls){ // in case manually hidden
35477             el.removeClass(removeCls);
35478             removeCls = null;
35479         }
35480         if(ce.cls){
35481             el.addClass(ce.cls);
35482             removeCls = ce.cls;
35483         }
35484         if(ce.title){
35485             tipTitle.update(ce.title);
35486             tipTitle.show();
35487         }else{
35488             tipTitle.update('');
35489             tipTitle.hide();
35490         }
35491         el.dom.style.width  = tm.maxWidth+'px';
35492         //tipBody.dom.style.width = '';
35493         tipBodyText.update(o.text);
35494         var p = getPad(), w = ce.width;
35495         if(!w){
35496             var td = tipBodyText.dom;
35497             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
35498             if(aw > tm.maxWidth){
35499                 w = tm.maxWidth;
35500             }else if(aw < tm.minWidth){
35501                 w = tm.minWidth;
35502             }else{
35503                 w = aw;
35504             }
35505         }
35506         //tipBody.setWidth(w);
35507         el.setWidth(parseInt(w, 10) + p);
35508         if(ce.autoHide === false){
35509             close.setDisplayed(true);
35510             if(dd){
35511                 dd.unlock();
35512             }
35513         }else{
35514             close.setDisplayed(false);
35515             if(dd){
35516                 dd.lock();
35517             }
35518         }
35519         if(xy){
35520             el.avoidY = xy[1]-18;
35521             el.setXY(xy);
35522         }
35523         if(tm.animate){
35524             el.setOpacity(.1);
35525             el.setStyle("visibility", "visible");
35526             el.fadeIn({callback: afterShow});
35527         }else{
35528             afterShow();
35529         }
35530     };
35531     
35532     var afterShow = function(){
35533         if(ce){
35534             el.show();
35535             esc.enable();
35536             if(tm.autoDismiss && ce.autoHide !== false){
35537                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
35538             }
35539         }
35540     };
35541     
35542     var hide = function(noanim){
35543         clearTimeout(dismissProc);
35544         clearTimeout(hideProc);
35545         ce = null;
35546         if(el.isVisible()){
35547             esc.disable();
35548             if(noanim !== true && tm.animate){
35549                 el.fadeOut({callback: afterHide});
35550             }else{
35551                 afterHide();
35552             } 
35553         }
35554     };
35555     
35556     var afterHide = function(){
35557         el.hide();
35558         if(removeCls){
35559             el.removeClass(removeCls);
35560             removeCls = null;
35561         }
35562     };
35563     
35564     return {
35565         /**
35566         * @cfg {Number} minWidth
35567         * The minimum width of the quick tip (defaults to 40)
35568         */
35569        minWidth : 40,
35570         /**
35571         * @cfg {Number} maxWidth
35572         * The maximum width of the quick tip (defaults to 300)
35573         */
35574        maxWidth : 300,
35575         /**
35576         * @cfg {Boolean} interceptTitles
35577         * True to automatically use the element's DOM title value if available (defaults to false)
35578         */
35579        interceptTitles : false,
35580         /**
35581         * @cfg {Boolean} trackMouse
35582         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
35583         */
35584        trackMouse : false,
35585         /**
35586         * @cfg {Boolean} hideOnClick
35587         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
35588         */
35589        hideOnClick : true,
35590         /**
35591         * @cfg {Number} showDelay
35592         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
35593         */
35594        showDelay : 500,
35595         /**
35596         * @cfg {Number} hideDelay
35597         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
35598         */
35599        hideDelay : 200,
35600         /**
35601         * @cfg {Boolean} autoHide
35602         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
35603         * Used in conjunction with hideDelay.
35604         */
35605        autoHide : true,
35606         /**
35607         * @cfg {Boolean}
35608         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
35609         * (defaults to true).  Used in conjunction with autoDismissDelay.
35610         */
35611        autoDismiss : true,
35612         /**
35613         * @cfg {Number}
35614         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
35615         */
35616        autoDismissDelay : 5000,
35617        /**
35618         * @cfg {Boolean} animate
35619         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
35620         */
35621        animate : false,
35622
35623        /**
35624         * @cfg {String} title
35625         * Title text to display (defaults to '').  This can be any valid HTML markup.
35626         */
35627         title: '',
35628        /**
35629         * @cfg {String} text
35630         * Body text to display (defaults to '').  This can be any valid HTML markup.
35631         */
35632         text : '',
35633        /**
35634         * @cfg {String} cls
35635         * A CSS class to apply to the base quick tip element (defaults to '').
35636         */
35637         cls : '',
35638        /**
35639         * @cfg {Number} width
35640         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
35641         * minWidth or maxWidth.
35642         */
35643         width : null,
35644
35645     /**
35646      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
35647      * or display QuickTips in a page.
35648      */
35649        init : function(){
35650           tm = Roo.QuickTips;
35651           cfg = tm.tagConfig;
35652           if(!inited){
35653               if(!Roo.isReady){ // allow calling of init() before onReady
35654                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
35655                   return;
35656               }
35657               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
35658               el.fxDefaults = {stopFx: true};
35659               // maximum custom styling
35660               //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>');
35661               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>');              
35662               tipTitle = el.child('h3');
35663               tipTitle.enableDisplayMode("block");
35664               tipBody = el.child('div.x-tip-bd');
35665               tipBodyText = el.child('div.x-tip-bd-inner');
35666               //bdLeft = el.child('div.x-tip-bd-left');
35667               //bdRight = el.child('div.x-tip-bd-right');
35668               close = el.child('div.x-tip-close');
35669               close.enableDisplayMode("block");
35670               close.on("click", hide);
35671               var d = Roo.get(document);
35672               d.on("mousedown", onDown);
35673               d.on("mouseover", onOver);
35674               d.on("mouseout", onOut);
35675               d.on("mousemove", onMove);
35676               esc = d.addKeyListener(27, hide);
35677               esc.disable();
35678               if(Roo.dd.DD){
35679                   dd = el.initDD("default", null, {
35680                       onDrag : function(){
35681                           el.sync();  
35682                       }
35683                   });
35684                   dd.setHandleElId(tipTitle.id);
35685                   dd.lock();
35686               }
35687               inited = true;
35688           }
35689           this.enable(); 
35690        },
35691
35692     /**
35693      * Configures a new quick tip instance and assigns it to a target element.  The following config options
35694      * are supported:
35695      * <pre>
35696 Property    Type                   Description
35697 ----------  ---------------------  ------------------------------------------------------------------------
35698 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
35699      * </ul>
35700      * @param {Object} config The config object
35701      */
35702        register : function(config){
35703            var cs = config instanceof Array ? config : arguments;
35704            for(var i = 0, len = cs.length; i < len; i++) {
35705                var c = cs[i];
35706                var target = c.target;
35707                if(target){
35708                    if(target instanceof Array){
35709                        for(var j = 0, jlen = target.length; j < jlen; j++){
35710                            tagEls[target[j]] = c;
35711                        }
35712                    }else{
35713                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
35714                    }
35715                }
35716            }
35717        },
35718
35719     /**
35720      * Removes this quick tip from its element and destroys it.
35721      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
35722      */
35723        unregister : function(el){
35724            delete tagEls[Roo.id(el)];
35725        },
35726
35727     /**
35728      * Enable this quick tip.
35729      */
35730        enable : function(){
35731            if(inited && disabled){
35732                locks.pop();
35733                if(locks.length < 1){
35734                    disabled = false;
35735                }
35736            }
35737        },
35738
35739     /**
35740      * Disable this quick tip.
35741      */
35742        disable : function(){
35743           disabled = true;
35744           clearTimeout(showProc);
35745           clearTimeout(hideProc);
35746           clearTimeout(dismissProc);
35747           if(ce){
35748               hide(true);
35749           }
35750           locks.push(1);
35751        },
35752
35753     /**
35754      * Returns true if the quick tip is enabled, else false.
35755      */
35756        isEnabled : function(){
35757             return !disabled;
35758        },
35759
35760         // private
35761        tagConfig : {
35762            namespace : "roo", // was ext?? this may break..
35763            alt_namespace : "ext",
35764            attribute : "qtip",
35765            width : "width",
35766            target : "target",
35767            title : "qtitle",
35768            hide : "hide",
35769            cls : "qclass"
35770        }
35771    };
35772 }();
35773
35774 // backwards compat
35775 Roo.QuickTips.tips = Roo.QuickTips.register;/*
35776  * Based on:
35777  * Ext JS Library 1.1.1
35778  * Copyright(c) 2006-2007, Ext JS, LLC.
35779  *
35780  * Originally Released Under LGPL - original licence link has changed is not relivant.
35781  *
35782  * Fork - LGPL
35783  * <script type="text/javascript">
35784  */
35785  
35786
35787 /**
35788  * @class Roo.tree.TreePanel
35789  * @extends Roo.data.Tree
35790  * @cfg {Roo.tree.TreeNode} root The root node
35791  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
35792  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
35793  * @cfg {Boolean} enableDD true to enable drag and drop
35794  * @cfg {Boolean} enableDrag true to enable just drag
35795  * @cfg {Boolean} enableDrop true to enable just drop
35796  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
35797  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
35798  * @cfg {String} ddGroup The DD group this TreePanel belongs to
35799  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
35800  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
35801  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
35802  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
35803  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
35804  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
35805  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
35806  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
35807  * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
35808  * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
35809  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
35810  * @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>
35811  * @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>
35812  * 
35813  * @constructor
35814  * @param {String/HTMLElement/Element} el The container element
35815  * @param {Object} config
35816  */
35817 Roo.tree.TreePanel = function(el, config){
35818     var root = false;
35819     var loader = false;
35820     if (config.root) {
35821         root = config.root;
35822         delete config.root;
35823     }
35824     if (config.loader) {
35825         loader = config.loader;
35826         delete config.loader;
35827     }
35828     
35829     Roo.apply(this, config);
35830     Roo.tree.TreePanel.superclass.constructor.call(this);
35831     this.el = Roo.get(el);
35832     this.el.addClass('x-tree');
35833     //console.log(root);
35834     if (root) {
35835         this.setRootNode( Roo.factory(root, Roo.tree));
35836     }
35837     if (loader) {
35838         this.loader = Roo.factory(loader, Roo.tree);
35839     }
35840    /**
35841     * Read-only. The id of the container element becomes this TreePanel's id.
35842     */
35843     this.id = this.el.id;
35844     this.addEvents({
35845         /**
35846         * @event beforeload
35847         * Fires before a node is loaded, return false to cancel
35848         * @param {Node} node The node being loaded
35849         */
35850         "beforeload" : true,
35851         /**
35852         * @event load
35853         * Fires when a node is loaded
35854         * @param {Node} node The node that was loaded
35855         */
35856         "load" : true,
35857         /**
35858         * @event textchange
35859         * Fires when the text for a node is changed
35860         * @param {Node} node The node
35861         * @param {String} text The new text
35862         * @param {String} oldText The old text
35863         */
35864         "textchange" : true,
35865         /**
35866         * @event beforeexpand
35867         * Fires before a node is expanded, return false to cancel.
35868         * @param {Node} node The node
35869         * @param {Boolean} deep
35870         * @param {Boolean} anim
35871         */
35872         "beforeexpand" : true,
35873         /**
35874         * @event beforecollapse
35875         * Fires before a node is collapsed, return false to cancel.
35876         * @param {Node} node The node
35877         * @param {Boolean} deep
35878         * @param {Boolean} anim
35879         */
35880         "beforecollapse" : true,
35881         /**
35882         * @event expand
35883         * Fires when a node is expanded
35884         * @param {Node} node The node
35885         */
35886         "expand" : true,
35887         /**
35888         * @event disabledchange
35889         * Fires when the disabled status of a node changes
35890         * @param {Node} node The node
35891         * @param {Boolean} disabled
35892         */
35893         "disabledchange" : true,
35894         /**
35895         * @event collapse
35896         * Fires when a node is collapsed
35897         * @param {Node} node The node
35898         */
35899         "collapse" : true,
35900         /**
35901         * @event beforeclick
35902         * Fires before click processing on a node. Return false to cancel the default action.
35903         * @param {Node} node The node
35904         * @param {Roo.EventObject} e The event object
35905         */
35906         "beforeclick":true,
35907         /**
35908         * @event checkchange
35909         * Fires when a node with a checkbox's checked property changes
35910         * @param {Node} this This node
35911         * @param {Boolean} checked
35912         */
35913         "checkchange":true,
35914         /**
35915         * @event click
35916         * Fires when a node is clicked
35917         * @param {Node} node The node
35918         * @param {Roo.EventObject} e The event object
35919         */
35920         "click":true,
35921         /**
35922         * @event dblclick
35923         * Fires when a node is double clicked
35924         * @param {Node} node The node
35925         * @param {Roo.EventObject} e The event object
35926         */
35927         "dblclick":true,
35928         /**
35929         * @event contextmenu
35930         * Fires when a node is right clicked
35931         * @param {Node} node The node
35932         * @param {Roo.EventObject} e The event object
35933         */
35934         "contextmenu":true,
35935         /**
35936         * @event beforechildrenrendered
35937         * Fires right before the child nodes for a node are rendered
35938         * @param {Node} node The node
35939         */
35940         "beforechildrenrendered":true,
35941         /**
35942         * @event startdrag
35943         * Fires when a node starts being dragged
35944         * @param {Roo.tree.TreePanel} this
35945         * @param {Roo.tree.TreeNode} node
35946         * @param {event} e The raw browser event
35947         */ 
35948        "startdrag" : true,
35949        /**
35950         * @event enddrag
35951         * Fires when a drag operation is complete
35952         * @param {Roo.tree.TreePanel} this
35953         * @param {Roo.tree.TreeNode} node
35954         * @param {event} e The raw browser event
35955         */
35956        "enddrag" : true,
35957        /**
35958         * @event dragdrop
35959         * Fires when a dragged node is dropped on a valid DD target
35960         * @param {Roo.tree.TreePanel} this
35961         * @param {Roo.tree.TreeNode} node
35962         * @param {DD} dd The dd it was dropped on
35963         * @param {event} e The raw browser event
35964         */
35965        "dragdrop" : true,
35966        /**
35967         * @event beforenodedrop
35968         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
35969         * passed to handlers has the following properties:<br />
35970         * <ul style="padding:5px;padding-left:16px;">
35971         * <li>tree - The TreePanel</li>
35972         * <li>target - The node being targeted for the drop</li>
35973         * <li>data - The drag data from the drag source</li>
35974         * <li>point - The point of the drop - append, above or below</li>
35975         * <li>source - The drag source</li>
35976         * <li>rawEvent - Raw mouse event</li>
35977         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
35978         * to be inserted by setting them on this object.</li>
35979         * <li>cancel - Set this to true to cancel the drop.</li>
35980         * </ul>
35981         * @param {Object} dropEvent
35982         */
35983        "beforenodedrop" : true,
35984        /**
35985         * @event nodedrop
35986         * Fires after a DD object is dropped on a node in this tree. The dropEvent
35987         * passed to handlers has the following properties:<br />
35988         * <ul style="padding:5px;padding-left:16px;">
35989         * <li>tree - The TreePanel</li>
35990         * <li>target - The node being targeted for the drop</li>
35991         * <li>data - The drag data from the drag source</li>
35992         * <li>point - The point of the drop - append, above or below</li>
35993         * <li>source - The drag source</li>
35994         * <li>rawEvent - Raw mouse event</li>
35995         * <li>dropNode - Dropped node(s).</li>
35996         * </ul>
35997         * @param {Object} dropEvent
35998         */
35999        "nodedrop" : true,
36000         /**
36001         * @event nodedragover
36002         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
36003         * passed to handlers has the following properties:<br />
36004         * <ul style="padding:5px;padding-left:16px;">
36005         * <li>tree - The TreePanel</li>
36006         * <li>target - The node being targeted for the drop</li>
36007         * <li>data - The drag data from the drag source</li>
36008         * <li>point - The point of the drop - append, above or below</li>
36009         * <li>source - The drag source</li>
36010         * <li>rawEvent - Raw mouse event</li>
36011         * <li>dropNode - Drop node(s) provided by the source.</li>
36012         * <li>cancel - Set this to true to signal drop not allowed.</li>
36013         * </ul>
36014         * @param {Object} dragOverEvent
36015         */
36016        "nodedragover" : true,
36017        /**
36018         * @event appendnode
36019         * Fires when append node to the tree
36020         * @param {Roo.tree.TreePanel} this
36021         * @param {Roo.tree.TreeNode} node
36022         * @param {Number} index The index of the newly appended node
36023         */
36024        "appendnode" : true
36025         
36026     });
36027     if(this.singleExpand){
36028        this.on("beforeexpand", this.restrictExpand, this);
36029     }
36030     if (this.editor) {
36031         this.editor.tree = this;
36032         this.editor = Roo.factory(this.editor, Roo.tree);
36033     }
36034     
36035     if (this.selModel) {
36036         this.selModel = Roo.factory(this.selModel, Roo.tree);
36037     }
36038    
36039 };
36040 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
36041     rootVisible : true,
36042     animate: Roo.enableFx,
36043     lines : true,
36044     enableDD : false,
36045     hlDrop : Roo.enableFx,
36046   
36047     renderer: false,
36048     
36049     rendererTip: false,
36050     // private
36051     restrictExpand : function(node){
36052         var p = node.parentNode;
36053         if(p){
36054             if(p.expandedChild && p.expandedChild.parentNode == p){
36055                 p.expandedChild.collapse();
36056             }
36057             p.expandedChild = node;
36058         }
36059     },
36060
36061     // private override
36062     setRootNode : function(node){
36063         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
36064         if(!this.rootVisible){
36065             node.ui = new Roo.tree.RootTreeNodeUI(node);
36066         }
36067         return node;
36068     },
36069
36070     /**
36071      * Returns the container element for this TreePanel
36072      */
36073     getEl : function(){
36074         return this.el;
36075     },
36076
36077     /**
36078      * Returns the default TreeLoader for this TreePanel
36079      */
36080     getLoader : function(){
36081         return this.loader;
36082     },
36083
36084     /**
36085      * Expand all nodes
36086      */
36087     expandAll : function(){
36088         this.root.expand(true);
36089     },
36090
36091     /**
36092      * Collapse all nodes
36093      */
36094     collapseAll : function(){
36095         this.root.collapse(true);
36096     },
36097
36098     /**
36099      * Returns the selection model used by this TreePanel
36100      */
36101     getSelectionModel : function(){
36102         if(!this.selModel){
36103             this.selModel = new Roo.tree.DefaultSelectionModel();
36104         }
36105         return this.selModel;
36106     },
36107
36108     /**
36109      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
36110      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
36111      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
36112      * @return {Array}
36113      */
36114     getChecked : function(a, startNode){
36115         startNode = startNode || this.root;
36116         var r = [];
36117         var f = function(){
36118             if(this.attributes.checked){
36119                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
36120             }
36121         }
36122         startNode.cascade(f);
36123         return r;
36124     },
36125
36126     /**
36127      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
36128      * @param {String} path
36129      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
36130      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
36131      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
36132      */
36133     expandPath : function(path, attr, callback){
36134         attr = attr || "id";
36135         var keys = path.split(this.pathSeparator);
36136         var curNode = this.root;
36137         if(curNode.attributes[attr] != keys[1]){ // invalid root
36138             if(callback){
36139                 callback(false, null);
36140             }
36141             return;
36142         }
36143         var index = 1;
36144         var f = function(){
36145             if(++index == keys.length){
36146                 if(callback){
36147                     callback(true, curNode);
36148                 }
36149                 return;
36150             }
36151             var c = curNode.findChild(attr, keys[index]);
36152             if(!c){
36153                 if(callback){
36154                     callback(false, curNode);
36155                 }
36156                 return;
36157             }
36158             curNode = c;
36159             c.expand(false, false, f);
36160         };
36161         curNode.expand(false, false, f);
36162     },
36163
36164     /**
36165      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
36166      * @param {String} path
36167      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
36168      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
36169      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
36170      */
36171     selectPath : function(path, attr, callback){
36172         attr = attr || "id";
36173         var keys = path.split(this.pathSeparator);
36174         var v = keys.pop();
36175         if(keys.length > 0){
36176             var f = function(success, node){
36177                 if(success && node){
36178                     var n = node.findChild(attr, v);
36179                     if(n){
36180                         n.select();
36181                         if(callback){
36182                             callback(true, n);
36183                         }
36184                     }else if(callback){
36185                         callback(false, n);
36186                     }
36187                 }else{
36188                     if(callback){
36189                         callback(false, n);
36190                     }
36191                 }
36192             };
36193             this.expandPath(keys.join(this.pathSeparator), attr, f);
36194         }else{
36195             this.root.select();
36196             if(callback){
36197                 callback(true, this.root);
36198             }
36199         }
36200     },
36201
36202     getTreeEl : function(){
36203         return this.el;
36204     },
36205
36206     /**
36207      * Trigger rendering of this TreePanel
36208      */
36209     render : function(){
36210         if (this.innerCt) {
36211             return this; // stop it rendering more than once!!
36212         }
36213         
36214         this.innerCt = this.el.createChild({tag:"ul",
36215                cls:"x-tree-root-ct " +
36216                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
36217
36218         if(this.containerScroll){
36219             Roo.dd.ScrollManager.register(this.el);
36220         }
36221         if((this.enableDD || this.enableDrop) && !this.dropZone){
36222            /**
36223             * The dropZone used by this tree if drop is enabled
36224             * @type Roo.tree.TreeDropZone
36225             */
36226              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
36227                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
36228            });
36229         }
36230         if((this.enableDD || this.enableDrag) && !this.dragZone){
36231            /**
36232             * The dragZone used by this tree if drag is enabled
36233             * @type Roo.tree.TreeDragZone
36234             */
36235             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
36236                ddGroup: this.ddGroup || "TreeDD",
36237                scroll: this.ddScroll
36238            });
36239         }
36240         this.getSelectionModel().init(this);
36241         if (!this.root) {
36242             Roo.log("ROOT not set in tree");
36243             return this;
36244         }
36245         this.root.render();
36246         if(!this.rootVisible){
36247             this.root.renderChildren();
36248         }
36249         return this;
36250     }
36251 });/*
36252  * Based on:
36253  * Ext JS Library 1.1.1
36254  * Copyright(c) 2006-2007, Ext JS, LLC.
36255  *
36256  * Originally Released Under LGPL - original licence link has changed is not relivant.
36257  *
36258  * Fork - LGPL
36259  * <script type="text/javascript">
36260  */
36261  
36262
36263 /**
36264  * @class Roo.tree.DefaultSelectionModel
36265  * @extends Roo.util.Observable
36266  * The default single selection for a TreePanel.
36267  * @param {Object} cfg Configuration
36268  */
36269 Roo.tree.DefaultSelectionModel = function(cfg){
36270    this.selNode = null;
36271    
36272    
36273    
36274    this.addEvents({
36275        /**
36276         * @event selectionchange
36277         * Fires when the selected node changes
36278         * @param {DefaultSelectionModel} this
36279         * @param {TreeNode} node the new selection
36280         */
36281        "selectionchange" : true,
36282
36283        /**
36284         * @event beforeselect
36285         * Fires before the selected node changes, return false to cancel the change
36286         * @param {DefaultSelectionModel} this
36287         * @param {TreeNode} node the new selection
36288         * @param {TreeNode} node the old selection
36289         */
36290        "beforeselect" : true
36291    });
36292    
36293     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
36294 };
36295
36296 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
36297     init : function(tree){
36298         this.tree = tree;
36299         tree.getTreeEl().on("keydown", this.onKeyDown, this);
36300         tree.on("click", this.onNodeClick, this);
36301     },
36302     
36303     onNodeClick : function(node, e){
36304         if (e.ctrlKey && this.selNode == node)  {
36305             this.unselect(node);
36306             return;
36307         }
36308         this.select(node);
36309     },
36310     
36311     /**
36312      * Select a node.
36313      * @param {TreeNode} node The node to select
36314      * @return {TreeNode} The selected node
36315      */
36316     select : function(node){
36317         var last = this.selNode;
36318         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
36319             if(last){
36320                 last.ui.onSelectedChange(false);
36321             }
36322             this.selNode = node;
36323             node.ui.onSelectedChange(true);
36324             this.fireEvent("selectionchange", this, node, last);
36325         }
36326         return node;
36327     },
36328     
36329     /**
36330      * Deselect a node.
36331      * @param {TreeNode} node The node to unselect
36332      */
36333     unselect : function(node){
36334         if(this.selNode == node){
36335             this.clearSelections();
36336         }    
36337     },
36338     
36339     /**
36340      * Clear all selections
36341      */
36342     clearSelections : function(){
36343         var n = this.selNode;
36344         if(n){
36345             n.ui.onSelectedChange(false);
36346             this.selNode = null;
36347             this.fireEvent("selectionchange", this, null);
36348         }
36349         return n;
36350     },
36351     
36352     /**
36353      * Get the selected node
36354      * @return {TreeNode} The selected node
36355      */
36356     getSelectedNode : function(){
36357         return this.selNode;    
36358     },
36359     
36360     /**
36361      * Returns true if the node is selected
36362      * @param {TreeNode} node The node to check
36363      * @return {Boolean}
36364      */
36365     isSelected : function(node){
36366         return this.selNode == node;  
36367     },
36368
36369     /**
36370      * Selects the node above the selected node in the tree, intelligently walking the nodes
36371      * @return TreeNode The new selection
36372      */
36373     selectPrevious : function(){
36374         var s = this.selNode || this.lastSelNode;
36375         if(!s){
36376             return null;
36377         }
36378         var ps = s.previousSibling;
36379         if(ps){
36380             if(!ps.isExpanded() || ps.childNodes.length < 1){
36381                 return this.select(ps);
36382             } else{
36383                 var lc = ps.lastChild;
36384                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
36385                     lc = lc.lastChild;
36386                 }
36387                 return this.select(lc);
36388             }
36389         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
36390             return this.select(s.parentNode);
36391         }
36392         return null;
36393     },
36394
36395     /**
36396      * Selects the node above the selected node in the tree, intelligently walking the nodes
36397      * @return TreeNode The new selection
36398      */
36399     selectNext : function(){
36400         var s = this.selNode || this.lastSelNode;
36401         if(!s){
36402             return null;
36403         }
36404         if(s.firstChild && s.isExpanded()){
36405              return this.select(s.firstChild);
36406          }else if(s.nextSibling){
36407              return this.select(s.nextSibling);
36408          }else if(s.parentNode){
36409             var newS = null;
36410             s.parentNode.bubble(function(){
36411                 if(this.nextSibling){
36412                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
36413                     return false;
36414                 }
36415             });
36416             return newS;
36417          }
36418         return null;
36419     },
36420
36421     onKeyDown : function(e){
36422         var s = this.selNode || this.lastSelNode;
36423         // undesirable, but required
36424         var sm = this;
36425         if(!s){
36426             return;
36427         }
36428         var k = e.getKey();
36429         switch(k){
36430              case e.DOWN:
36431                  e.stopEvent();
36432                  this.selectNext();
36433              break;
36434              case e.UP:
36435                  e.stopEvent();
36436                  this.selectPrevious();
36437              break;
36438              case e.RIGHT:
36439                  e.preventDefault();
36440                  if(s.hasChildNodes()){
36441                      if(!s.isExpanded()){
36442                          s.expand();
36443                      }else if(s.firstChild){
36444                          this.select(s.firstChild, e);
36445                      }
36446                  }
36447              break;
36448              case e.LEFT:
36449                  e.preventDefault();
36450                  if(s.hasChildNodes() && s.isExpanded()){
36451                      s.collapse();
36452                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
36453                      this.select(s.parentNode, e);
36454                  }
36455              break;
36456         };
36457     }
36458 });
36459
36460 /**
36461  * @class Roo.tree.MultiSelectionModel
36462  * @extends Roo.util.Observable
36463  * Multi selection for a TreePanel.
36464  * @param {Object} cfg Configuration
36465  */
36466 Roo.tree.MultiSelectionModel = function(){
36467    this.selNodes = [];
36468    this.selMap = {};
36469    this.addEvents({
36470        /**
36471         * @event selectionchange
36472         * Fires when the selected nodes change
36473         * @param {MultiSelectionModel} this
36474         * @param {Array} nodes Array of the selected nodes
36475         */
36476        "selectionchange" : true
36477    });
36478    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
36479    
36480 };
36481
36482 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
36483     init : function(tree){
36484         this.tree = tree;
36485         tree.getTreeEl().on("keydown", this.onKeyDown, this);
36486         tree.on("click", this.onNodeClick, this);
36487     },
36488     
36489     onNodeClick : function(node, e){
36490         this.select(node, e, e.ctrlKey);
36491     },
36492     
36493     /**
36494      * Select a node.
36495      * @param {TreeNode} node The node to select
36496      * @param {EventObject} e (optional) An event associated with the selection
36497      * @param {Boolean} keepExisting True to retain existing selections
36498      * @return {TreeNode} The selected node
36499      */
36500     select : function(node, e, keepExisting){
36501         if(keepExisting !== true){
36502             this.clearSelections(true);
36503         }
36504         if(this.isSelected(node)){
36505             this.lastSelNode = node;
36506             return node;
36507         }
36508         this.selNodes.push(node);
36509         this.selMap[node.id] = node;
36510         this.lastSelNode = node;
36511         node.ui.onSelectedChange(true);
36512         this.fireEvent("selectionchange", this, this.selNodes);
36513         return node;
36514     },
36515     
36516     /**
36517      * Deselect a node.
36518      * @param {TreeNode} node The node to unselect
36519      */
36520     unselect : function(node){
36521         if(this.selMap[node.id]){
36522             node.ui.onSelectedChange(false);
36523             var sn = this.selNodes;
36524             var index = -1;
36525             if(sn.indexOf){
36526                 index = sn.indexOf(node);
36527             }else{
36528                 for(var i = 0, len = sn.length; i < len; i++){
36529                     if(sn[i] == node){
36530                         index = i;
36531                         break;
36532                     }
36533                 }
36534             }
36535             if(index != -1){
36536                 this.selNodes.splice(index, 1);
36537             }
36538             delete this.selMap[node.id];
36539             this.fireEvent("selectionchange", this, this.selNodes);
36540         }
36541     },
36542     
36543     /**
36544      * Clear all selections
36545      */
36546     clearSelections : function(suppressEvent){
36547         var sn = this.selNodes;
36548         if(sn.length > 0){
36549             for(var i = 0, len = sn.length; i < len; i++){
36550                 sn[i].ui.onSelectedChange(false);
36551             }
36552             this.selNodes = [];
36553             this.selMap = {};
36554             if(suppressEvent !== true){
36555                 this.fireEvent("selectionchange", this, this.selNodes);
36556             }
36557         }
36558     },
36559     
36560     /**
36561      * Returns true if the node is selected
36562      * @param {TreeNode} node The node to check
36563      * @return {Boolean}
36564      */
36565     isSelected : function(node){
36566         return this.selMap[node.id] ? true : false;  
36567     },
36568     
36569     /**
36570      * Returns an array of the selected nodes
36571      * @return {Array}
36572      */
36573     getSelectedNodes : function(){
36574         return this.selNodes;    
36575     },
36576
36577     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
36578
36579     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
36580
36581     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
36582 });/*
36583  * Based on:
36584  * Ext JS Library 1.1.1
36585  * Copyright(c) 2006-2007, Ext JS, LLC.
36586  *
36587  * Originally Released Under LGPL - original licence link has changed is not relivant.
36588  *
36589  * Fork - LGPL
36590  * <script type="text/javascript">
36591  */
36592  
36593 /**
36594  * @class Roo.tree.TreeNode
36595  * @extends Roo.data.Node
36596  * @cfg {String} text The text for this node
36597  * @cfg {Boolean} expanded true to start the node expanded
36598  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
36599  * @cfg {Boolean} allowDrop false if this node cannot be drop on
36600  * @cfg {Boolean} disabled true to start the node disabled
36601  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
36602  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
36603  * @cfg {String} cls A css class to be added to the node
36604  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
36605  * @cfg {String} href URL of the link used for the node (defaults to #)
36606  * @cfg {String} hrefTarget target frame for the link
36607  * @cfg {String} qtip An Ext QuickTip for the node
36608  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
36609  * @cfg {Boolean} singleClickExpand True for single click expand on this node
36610  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
36611  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
36612  * (defaults to undefined with no checkbox rendered)
36613  * @constructor
36614  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
36615  */
36616 Roo.tree.TreeNode = function(attributes){
36617     attributes = attributes || {};
36618     if(typeof attributes == "string"){
36619         attributes = {text: attributes};
36620     }
36621     this.childrenRendered = false;
36622     this.rendered = false;
36623     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
36624     this.expanded = attributes.expanded === true;
36625     this.isTarget = attributes.isTarget !== false;
36626     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
36627     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
36628
36629     /**
36630      * Read-only. The text for this node. To change it use setText().
36631      * @type String
36632      */
36633     this.text = attributes.text;
36634     /**
36635      * True if this node is disabled.
36636      * @type Boolean
36637      */
36638     this.disabled = attributes.disabled === true;
36639
36640     this.addEvents({
36641         /**
36642         * @event textchange
36643         * Fires when the text for this node is changed
36644         * @param {Node} this This node
36645         * @param {String} text The new text
36646         * @param {String} oldText The old text
36647         */
36648         "textchange" : true,
36649         /**
36650         * @event beforeexpand
36651         * Fires before this node is expanded, return false to cancel.
36652         * @param {Node} this This node
36653         * @param {Boolean} deep
36654         * @param {Boolean} anim
36655         */
36656         "beforeexpand" : true,
36657         /**
36658         * @event beforecollapse
36659         * Fires before this node is collapsed, return false to cancel.
36660         * @param {Node} this This node
36661         * @param {Boolean} deep
36662         * @param {Boolean} anim
36663         */
36664         "beforecollapse" : true,
36665         /**
36666         * @event expand
36667         * Fires when this node is expanded
36668         * @param {Node} this This node
36669         */
36670         "expand" : true,
36671         /**
36672         * @event disabledchange
36673         * Fires when the disabled status of this node changes
36674         * @param {Node} this This node
36675         * @param {Boolean} disabled
36676         */
36677         "disabledchange" : true,
36678         /**
36679         * @event collapse
36680         * Fires when this node is collapsed
36681         * @param {Node} this This node
36682         */
36683         "collapse" : true,
36684         /**
36685         * @event beforeclick
36686         * Fires before click processing. Return false to cancel the default action.
36687         * @param {Node} this This node
36688         * @param {Roo.EventObject} e The event object
36689         */
36690         "beforeclick":true,
36691         /**
36692         * @event checkchange
36693         * Fires when a node with a checkbox's checked property changes
36694         * @param {Node} this This node
36695         * @param {Boolean} checked
36696         */
36697         "checkchange":true,
36698         /**
36699         * @event click
36700         * Fires when this node is clicked
36701         * @param {Node} this This node
36702         * @param {Roo.EventObject} e The event object
36703         */
36704         "click":true,
36705         /**
36706         * @event dblclick
36707         * Fires when this node is double clicked
36708         * @param {Node} this This node
36709         * @param {Roo.EventObject} e The event object
36710         */
36711         "dblclick":true,
36712         /**
36713         * @event contextmenu
36714         * Fires when this node is right clicked
36715         * @param {Node} this This node
36716         * @param {Roo.EventObject} e The event object
36717         */
36718         "contextmenu":true,
36719         /**
36720         * @event beforechildrenrendered
36721         * Fires right before the child nodes for this node are rendered
36722         * @param {Node} this This node
36723         */
36724         "beforechildrenrendered":true
36725     });
36726
36727     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
36728
36729     /**
36730      * Read-only. The UI for this node
36731      * @type TreeNodeUI
36732      */
36733     this.ui = new uiClass(this);
36734     
36735     // finally support items[]
36736     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
36737         return;
36738     }
36739     
36740     
36741     Roo.each(this.attributes.items, function(c) {
36742         this.appendChild(Roo.factory(c,Roo.Tree));
36743     }, this);
36744     delete this.attributes.items;
36745     
36746     
36747     
36748 };
36749 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
36750     preventHScroll: true,
36751     /**
36752      * Returns true if this node is expanded
36753      * @return {Boolean}
36754      */
36755     isExpanded : function(){
36756         return this.expanded;
36757     },
36758
36759     /**
36760      * Returns the UI object for this node
36761      * @return {TreeNodeUI}
36762      */
36763     getUI : function(){
36764         return this.ui;
36765     },
36766
36767     // private override
36768     setFirstChild : function(node){
36769         var of = this.firstChild;
36770         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
36771         if(this.childrenRendered && of && node != of){
36772             of.renderIndent(true, true);
36773         }
36774         if(this.rendered){
36775             this.renderIndent(true, true);
36776         }
36777     },
36778
36779     // private override
36780     setLastChild : function(node){
36781         var ol = this.lastChild;
36782         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
36783         if(this.childrenRendered && ol && node != ol){
36784             ol.renderIndent(true, true);
36785         }
36786         if(this.rendered){
36787             this.renderIndent(true, true);
36788         }
36789     },
36790
36791     // these methods are overridden to provide lazy rendering support
36792     // private override
36793     appendChild : function()
36794     {
36795         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
36796         if(node && this.childrenRendered){
36797             node.render();
36798         }
36799         this.ui.updateExpandIcon();
36800         return node;
36801     },
36802
36803     // private override
36804     removeChild : function(node){
36805         this.ownerTree.getSelectionModel().unselect(node);
36806         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
36807         // if it's been rendered remove dom node
36808         if(this.childrenRendered){
36809             node.ui.remove();
36810         }
36811         if(this.childNodes.length < 1){
36812             this.collapse(false, false);
36813         }else{
36814             this.ui.updateExpandIcon();
36815         }
36816         if(!this.firstChild) {
36817             this.childrenRendered = false;
36818         }
36819         return node;
36820     },
36821
36822     // private override
36823     insertBefore : function(node, refNode){
36824         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
36825         if(newNode && refNode && this.childrenRendered){
36826             node.render();
36827         }
36828         this.ui.updateExpandIcon();
36829         return newNode;
36830     },
36831
36832     /**
36833      * Sets the text for this node
36834      * @param {String} text
36835      */
36836     setText : function(text){
36837         var oldText = this.text;
36838         this.text = text;
36839         this.attributes.text = text;
36840         if(this.rendered){ // event without subscribing
36841             this.ui.onTextChange(this, text, oldText);
36842         }
36843         this.fireEvent("textchange", this, text, oldText);
36844     },
36845
36846     /**
36847      * Triggers selection of this node
36848      */
36849     select : function(){
36850         this.getOwnerTree().getSelectionModel().select(this);
36851     },
36852
36853     /**
36854      * Triggers deselection of this node
36855      */
36856     unselect : function(){
36857         this.getOwnerTree().getSelectionModel().unselect(this);
36858     },
36859
36860     /**
36861      * Returns true if this node is selected
36862      * @return {Boolean}
36863      */
36864     isSelected : function(){
36865         return this.getOwnerTree().getSelectionModel().isSelected(this);
36866     },
36867
36868     /**
36869      * Expand this node.
36870      * @param {Boolean} deep (optional) True to expand all children as well
36871      * @param {Boolean} anim (optional) false to cancel the default animation
36872      * @param {Function} callback (optional) A callback to be called when
36873      * expanding this node completes (does not wait for deep expand to complete).
36874      * Called with 1 parameter, this node.
36875      */
36876     expand : function(deep, anim, callback){
36877         if(!this.expanded){
36878             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
36879                 return;
36880             }
36881             if(!this.childrenRendered){
36882                 this.renderChildren();
36883             }
36884             this.expanded = true;
36885             
36886             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
36887                 this.ui.animExpand(function(){
36888                     this.fireEvent("expand", this);
36889                     if(typeof callback == "function"){
36890                         callback(this);
36891                     }
36892                     if(deep === true){
36893                         this.expandChildNodes(true);
36894                     }
36895                 }.createDelegate(this));
36896                 return;
36897             }else{
36898                 this.ui.expand();
36899                 this.fireEvent("expand", this);
36900                 if(typeof callback == "function"){
36901                     callback(this);
36902                 }
36903             }
36904         }else{
36905            if(typeof callback == "function"){
36906                callback(this);
36907            }
36908         }
36909         if(deep === true){
36910             this.expandChildNodes(true);
36911         }
36912     },
36913
36914     isHiddenRoot : function(){
36915         return this.isRoot && !this.getOwnerTree().rootVisible;
36916     },
36917
36918     /**
36919      * Collapse this node.
36920      * @param {Boolean} deep (optional) True to collapse all children as well
36921      * @param {Boolean} anim (optional) false to cancel the default animation
36922      */
36923     collapse : function(deep, anim){
36924         if(this.expanded && !this.isHiddenRoot()){
36925             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
36926                 return;
36927             }
36928             this.expanded = false;
36929             if((this.getOwnerTree().animate && anim !== false) || anim){
36930                 this.ui.animCollapse(function(){
36931                     this.fireEvent("collapse", this);
36932                     if(deep === true){
36933                         this.collapseChildNodes(true);
36934                     }
36935                 }.createDelegate(this));
36936                 return;
36937             }else{
36938                 this.ui.collapse();
36939                 this.fireEvent("collapse", this);
36940             }
36941         }
36942         if(deep === true){
36943             var cs = this.childNodes;
36944             for(var i = 0, len = cs.length; i < len; i++) {
36945                 cs[i].collapse(true, false);
36946             }
36947         }
36948     },
36949
36950     // private
36951     delayedExpand : function(delay){
36952         if(!this.expandProcId){
36953             this.expandProcId = this.expand.defer(delay, this);
36954         }
36955     },
36956
36957     // private
36958     cancelExpand : function(){
36959         if(this.expandProcId){
36960             clearTimeout(this.expandProcId);
36961         }
36962         this.expandProcId = false;
36963     },
36964
36965     /**
36966      * Toggles expanded/collapsed state of the node
36967      */
36968     toggle : function(){
36969         if(this.expanded){
36970             this.collapse();
36971         }else{
36972             this.expand();
36973         }
36974     },
36975
36976     /**
36977      * Ensures all parent nodes are expanded
36978      */
36979     ensureVisible : function(callback){
36980         var tree = this.getOwnerTree();
36981         tree.expandPath(this.parentNode.getPath(), false, function(){
36982             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
36983             Roo.callback(callback);
36984         }.createDelegate(this));
36985     },
36986
36987     /**
36988      * Expand all child nodes
36989      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
36990      */
36991     expandChildNodes : function(deep){
36992         var cs = this.childNodes;
36993         for(var i = 0, len = cs.length; i < len; i++) {
36994                 cs[i].expand(deep);
36995         }
36996     },
36997
36998     /**
36999      * Collapse all child nodes
37000      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
37001      */
37002     collapseChildNodes : function(deep){
37003         var cs = this.childNodes;
37004         for(var i = 0, len = cs.length; i < len; i++) {
37005                 cs[i].collapse(deep);
37006         }
37007     },
37008
37009     /**
37010      * Disables this node
37011      */
37012     disable : function(){
37013         this.disabled = true;
37014         this.unselect();
37015         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
37016             this.ui.onDisableChange(this, true);
37017         }
37018         this.fireEvent("disabledchange", this, true);
37019     },
37020
37021     /**
37022      * Enables this node
37023      */
37024     enable : function(){
37025         this.disabled = false;
37026         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
37027             this.ui.onDisableChange(this, false);
37028         }
37029         this.fireEvent("disabledchange", this, false);
37030     },
37031
37032     // private
37033     renderChildren : function(suppressEvent){
37034         if(suppressEvent !== false){
37035             this.fireEvent("beforechildrenrendered", this);
37036         }
37037         var cs = this.childNodes;
37038         for(var i = 0, len = cs.length; i < len; i++){
37039             cs[i].render(true);
37040         }
37041         this.childrenRendered = true;
37042     },
37043
37044     // private
37045     sort : function(fn, scope){
37046         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
37047         if(this.childrenRendered){
37048             var cs = this.childNodes;
37049             for(var i = 0, len = cs.length; i < len; i++){
37050                 cs[i].render(true);
37051             }
37052         }
37053     },
37054
37055     // private
37056     render : function(bulkRender){
37057         this.ui.render(bulkRender);
37058         if(!this.rendered){
37059             this.rendered = true;
37060             if(this.expanded){
37061                 this.expanded = false;
37062                 this.expand(false, false);
37063             }
37064         }
37065     },
37066
37067     // private
37068     renderIndent : function(deep, refresh){
37069         if(refresh){
37070             this.ui.childIndent = null;
37071         }
37072         this.ui.renderIndent();
37073         if(deep === true && this.childrenRendered){
37074             var cs = this.childNodes;
37075             for(var i = 0, len = cs.length; i < len; i++){
37076                 cs[i].renderIndent(true, refresh);
37077             }
37078         }
37079     }
37080 });/*
37081  * Based on:
37082  * Ext JS Library 1.1.1
37083  * Copyright(c) 2006-2007, Ext JS, LLC.
37084  *
37085  * Originally Released Under LGPL - original licence link has changed is not relivant.
37086  *
37087  * Fork - LGPL
37088  * <script type="text/javascript">
37089  */
37090  
37091 /**
37092  * @class Roo.tree.AsyncTreeNode
37093  * @extends Roo.tree.TreeNode
37094  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
37095  * @constructor
37096  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
37097  */
37098  Roo.tree.AsyncTreeNode = function(config){
37099     this.loaded = false;
37100     this.loading = false;
37101     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
37102     /**
37103     * @event beforeload
37104     * Fires before this node is loaded, return false to cancel
37105     * @param {Node} this This node
37106     */
37107     this.addEvents({'beforeload':true, 'load': true});
37108     /**
37109     * @event load
37110     * Fires when this node is loaded
37111     * @param {Node} this This node
37112     */
37113     /**
37114      * The loader used by this node (defaults to using the tree's defined loader)
37115      * @type TreeLoader
37116      * @property loader
37117      */
37118 };
37119 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
37120     expand : function(deep, anim, callback){
37121         if(this.loading){ // if an async load is already running, waiting til it's done
37122             var timer;
37123             var f = function(){
37124                 if(!this.loading){ // done loading
37125                     clearInterval(timer);
37126                     this.expand(deep, anim, callback);
37127                 }
37128             }.createDelegate(this);
37129             timer = setInterval(f, 200);
37130             return;
37131         }
37132         if(!this.loaded){
37133             if(this.fireEvent("beforeload", this) === false){
37134                 return;
37135             }
37136             this.loading = true;
37137             this.ui.beforeLoad(this);
37138             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
37139             if(loader){
37140                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
37141                 return;
37142             }
37143         }
37144         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
37145     },
37146     
37147     /**
37148      * Returns true if this node is currently loading
37149      * @return {Boolean}
37150      */
37151     isLoading : function(){
37152         return this.loading;  
37153     },
37154     
37155     loadComplete : function(deep, anim, callback){
37156         this.loading = false;
37157         this.loaded = true;
37158         this.ui.afterLoad(this);
37159         this.fireEvent("load", this);
37160         this.expand(deep, anim, callback);
37161     },
37162     
37163     /**
37164      * Returns true if this node has been loaded
37165      * @return {Boolean}
37166      */
37167     isLoaded : function(){
37168         return this.loaded;
37169     },
37170     
37171     hasChildNodes : function(){
37172         if(!this.isLeaf() && !this.loaded){
37173             return true;
37174         }else{
37175             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
37176         }
37177     },
37178
37179     /**
37180      * Trigger a reload for this node
37181      * @param {Function} callback
37182      */
37183     reload : function(callback){
37184         this.collapse(false, false);
37185         while(this.firstChild){
37186             this.removeChild(this.firstChild);
37187         }
37188         this.childrenRendered = false;
37189         this.loaded = false;
37190         if(this.isHiddenRoot()){
37191             this.expanded = false;
37192         }
37193         this.expand(false, false, callback);
37194     }
37195 });/*
37196  * Based on:
37197  * Ext JS Library 1.1.1
37198  * Copyright(c) 2006-2007, Ext JS, LLC.
37199  *
37200  * Originally Released Under LGPL - original licence link has changed is not relivant.
37201  *
37202  * Fork - LGPL
37203  * <script type="text/javascript">
37204  */
37205  
37206 /**
37207  * @class Roo.tree.TreeNodeUI
37208  * @constructor
37209  * @param {Object} node The node to render
37210  * The TreeNode UI implementation is separate from the
37211  * tree implementation. Unless you are customizing the tree UI,
37212  * you should never have to use this directly.
37213  */
37214 Roo.tree.TreeNodeUI = function(node){
37215     this.node = node;
37216     this.rendered = false;
37217     this.animating = false;
37218     this.emptyIcon = Roo.BLANK_IMAGE_URL;
37219 };
37220
37221 Roo.tree.TreeNodeUI.prototype = {
37222     removeChild : function(node){
37223         if(this.rendered){
37224             this.ctNode.removeChild(node.ui.getEl());
37225         }
37226     },
37227
37228     beforeLoad : function(){
37229          this.addClass("x-tree-node-loading");
37230     },
37231
37232     afterLoad : function(){
37233          this.removeClass("x-tree-node-loading");
37234     },
37235
37236     onTextChange : function(node, text, oldText){
37237         if(this.rendered){
37238             this.textNode.innerHTML = text;
37239         }
37240     },
37241
37242     onDisableChange : function(node, state){
37243         this.disabled = state;
37244         if(state){
37245             this.addClass("x-tree-node-disabled");
37246         }else{
37247             this.removeClass("x-tree-node-disabled");
37248         }
37249     },
37250
37251     onSelectedChange : function(state){
37252         if(state){
37253             this.focus();
37254             this.addClass("x-tree-selected");
37255         }else{
37256             //this.blur();
37257             this.removeClass("x-tree-selected");
37258         }
37259     },
37260
37261     onMove : function(tree, node, oldParent, newParent, index, refNode){
37262         this.childIndent = null;
37263         if(this.rendered){
37264             var targetNode = newParent.ui.getContainer();
37265             if(!targetNode){//target not rendered
37266                 this.holder = document.createElement("div");
37267                 this.holder.appendChild(this.wrap);
37268                 return;
37269             }
37270             var insertBefore = refNode ? refNode.ui.getEl() : null;
37271             if(insertBefore){
37272                 targetNode.insertBefore(this.wrap, insertBefore);
37273             }else{
37274                 targetNode.appendChild(this.wrap);
37275             }
37276             this.node.renderIndent(true);
37277         }
37278     },
37279
37280     addClass : function(cls){
37281         if(this.elNode){
37282             Roo.fly(this.elNode).addClass(cls);
37283         }
37284     },
37285
37286     removeClass : function(cls){
37287         if(this.elNode){
37288             Roo.fly(this.elNode).removeClass(cls);
37289         }
37290     },
37291
37292     remove : function(){
37293         if(this.rendered){
37294             this.holder = document.createElement("div");
37295             this.holder.appendChild(this.wrap);
37296         }
37297     },
37298
37299     fireEvent : function(){
37300         return this.node.fireEvent.apply(this.node, arguments);
37301     },
37302
37303     initEvents : function(){
37304         this.node.on("move", this.onMove, this);
37305         var E = Roo.EventManager;
37306         var a = this.anchor;
37307
37308         var el = Roo.fly(a, '_treeui');
37309
37310         if(Roo.isOpera){ // opera render bug ignores the CSS
37311             el.setStyle("text-decoration", "none");
37312         }
37313
37314         el.on("click", this.onClick, this);
37315         el.on("dblclick", this.onDblClick, this);
37316
37317         if(this.checkbox){
37318             Roo.EventManager.on(this.checkbox,
37319                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
37320         }
37321
37322         el.on("contextmenu", this.onContextMenu, this);
37323
37324         var icon = Roo.fly(this.iconNode);
37325         icon.on("click", this.onClick, this);
37326         icon.on("dblclick", this.onDblClick, this);
37327         icon.on("contextmenu", this.onContextMenu, this);
37328         E.on(this.ecNode, "click", this.ecClick, this, true);
37329
37330         if(this.node.disabled){
37331             this.addClass("x-tree-node-disabled");
37332         }
37333         if(this.node.hidden){
37334             this.addClass("x-tree-node-disabled");
37335         }
37336         var ot = this.node.getOwnerTree();
37337         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
37338         if(dd && (!this.node.isRoot || ot.rootVisible)){
37339             Roo.dd.Registry.register(this.elNode, {
37340                 node: this.node,
37341                 handles: this.getDDHandles(),
37342                 isHandle: false
37343             });
37344         }
37345     },
37346
37347     getDDHandles : function(){
37348         return [this.iconNode, this.textNode];
37349     },
37350
37351     hide : function(){
37352         if(this.rendered){
37353             this.wrap.style.display = "none";
37354         }
37355     },
37356
37357     show : function(){
37358         if(this.rendered){
37359             this.wrap.style.display = "";
37360         }
37361     },
37362
37363     onContextMenu : function(e){
37364         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
37365             e.preventDefault();
37366             this.focus();
37367             this.fireEvent("contextmenu", this.node, e);
37368         }
37369     },
37370
37371     onClick : function(e){
37372         if(this.dropping){
37373             e.stopEvent();
37374             return;
37375         }
37376         if(this.fireEvent("beforeclick", this.node, e) !== false){
37377             if(!this.disabled && this.node.attributes.href){
37378                 this.fireEvent("click", this.node, e);
37379                 return;
37380             }
37381             e.preventDefault();
37382             if(this.disabled){
37383                 return;
37384             }
37385
37386             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
37387                 this.node.toggle();
37388             }
37389
37390             this.fireEvent("click", this.node, e);
37391         }else{
37392             e.stopEvent();
37393         }
37394     },
37395
37396     onDblClick : function(e){
37397         e.preventDefault();
37398         if(this.disabled){
37399             return;
37400         }
37401         if(this.checkbox){
37402             this.toggleCheck();
37403         }
37404         if(!this.animating && this.node.hasChildNodes()){
37405             this.node.toggle();
37406         }
37407         this.fireEvent("dblclick", this.node, e);
37408     },
37409
37410     onCheckChange : function(){
37411         var checked = this.checkbox.checked;
37412         this.node.attributes.checked = checked;
37413         this.fireEvent('checkchange', this.node, checked);
37414     },
37415
37416     ecClick : function(e){
37417         if(!this.animating && this.node.hasChildNodes()){
37418             this.node.toggle();
37419         }
37420     },
37421
37422     startDrop : function(){
37423         this.dropping = true;
37424     },
37425
37426     // delayed drop so the click event doesn't get fired on a drop
37427     endDrop : function(){
37428        setTimeout(function(){
37429            this.dropping = false;
37430        }.createDelegate(this), 50);
37431     },
37432
37433     expand : function(){
37434         this.updateExpandIcon();
37435         this.ctNode.style.display = "";
37436     },
37437
37438     focus : function(){
37439         if(!this.node.preventHScroll){
37440             try{this.anchor.focus();
37441             }catch(e){}
37442         }else if(!Roo.isIE){
37443             try{
37444                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
37445                 var l = noscroll.scrollLeft;
37446                 this.anchor.focus();
37447                 noscroll.scrollLeft = l;
37448             }catch(e){}
37449         }
37450     },
37451
37452     toggleCheck : function(value){
37453         var cb = this.checkbox;
37454         if(cb){
37455             cb.checked = (value === undefined ? !cb.checked : value);
37456         }
37457     },
37458
37459     blur : function(){
37460         try{
37461             this.anchor.blur();
37462         }catch(e){}
37463     },
37464
37465     animExpand : function(callback){
37466         var ct = Roo.get(this.ctNode);
37467         ct.stopFx();
37468         if(!this.node.hasChildNodes()){
37469             this.updateExpandIcon();
37470             this.ctNode.style.display = "";
37471             Roo.callback(callback);
37472             return;
37473         }
37474         this.animating = true;
37475         this.updateExpandIcon();
37476
37477         ct.slideIn('t', {
37478            callback : function(){
37479                this.animating = false;
37480                Roo.callback(callback);
37481             },
37482             scope: this,
37483             duration: this.node.ownerTree.duration || .25
37484         });
37485     },
37486
37487     highlight : function(){
37488         var tree = this.node.getOwnerTree();
37489         Roo.fly(this.wrap).highlight(
37490             tree.hlColor || "C3DAF9",
37491             {endColor: tree.hlBaseColor}
37492         );
37493     },
37494
37495     collapse : function(){
37496         this.updateExpandIcon();
37497         this.ctNode.style.display = "none";
37498     },
37499
37500     animCollapse : function(callback){
37501         var ct = Roo.get(this.ctNode);
37502         ct.enableDisplayMode('block');
37503         ct.stopFx();
37504
37505         this.animating = true;
37506         this.updateExpandIcon();
37507
37508         ct.slideOut('t', {
37509             callback : function(){
37510                this.animating = false;
37511                Roo.callback(callback);
37512             },
37513             scope: this,
37514             duration: this.node.ownerTree.duration || .25
37515         });
37516     },
37517
37518     getContainer : function(){
37519         return this.ctNode;
37520     },
37521
37522     getEl : function(){
37523         return this.wrap;
37524     },
37525
37526     appendDDGhost : function(ghostNode){
37527         ghostNode.appendChild(this.elNode.cloneNode(true));
37528     },
37529
37530     getDDRepairXY : function(){
37531         return Roo.lib.Dom.getXY(this.iconNode);
37532     },
37533
37534     onRender : function(){
37535         this.render();
37536     },
37537
37538     render : function(bulkRender){
37539         var n = this.node, a = n.attributes;
37540         var targetNode = n.parentNode ?
37541               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
37542
37543         if(!this.rendered){
37544             this.rendered = true;
37545
37546             this.renderElements(n, a, targetNode, bulkRender);
37547
37548             if(a.qtip){
37549                if(this.textNode.setAttributeNS){
37550                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
37551                    if(a.qtipTitle){
37552                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
37553                    }
37554                }else{
37555                    this.textNode.setAttribute("ext:qtip", a.qtip);
37556                    if(a.qtipTitle){
37557                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
37558                    }
37559                }
37560             }else if(a.qtipCfg){
37561                 a.qtipCfg.target = Roo.id(this.textNode);
37562                 Roo.QuickTips.register(a.qtipCfg);
37563             }
37564             this.initEvents();
37565             if(!this.node.expanded){
37566                 this.updateExpandIcon();
37567             }
37568         }else{
37569             if(bulkRender === true) {
37570                 targetNode.appendChild(this.wrap);
37571             }
37572         }
37573     },
37574
37575     renderElements : function(n, a, targetNode, bulkRender)
37576     {
37577         // add some indent caching, this helps performance when rendering a large tree
37578         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
37579         var t = n.getOwnerTree();
37580         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
37581         if (typeof(n.attributes.html) != 'undefined') {
37582             txt = n.attributes.html;
37583         }
37584         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
37585         var cb = typeof a.checked == 'boolean';
37586         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
37587         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
37588             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
37589             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
37590             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
37591             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
37592             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
37593              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
37594                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
37595             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
37596             "</li>"];
37597
37598         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
37599             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
37600                                 n.nextSibling.ui.getEl(), buf.join(""));
37601         }else{
37602             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
37603         }
37604
37605         this.elNode = this.wrap.childNodes[0];
37606         this.ctNode = this.wrap.childNodes[1];
37607         var cs = this.elNode.childNodes;
37608         this.indentNode = cs[0];
37609         this.ecNode = cs[1];
37610         this.iconNode = cs[2];
37611         var index = 3;
37612         if(cb){
37613             this.checkbox = cs[3];
37614             index++;
37615         }
37616         this.anchor = cs[index];
37617         this.textNode = cs[index].firstChild;
37618     },
37619
37620     getAnchor : function(){
37621         return this.anchor;
37622     },
37623
37624     getTextEl : function(){
37625         return this.textNode;
37626     },
37627
37628     getIconEl : function(){
37629         return this.iconNode;
37630     },
37631
37632     isChecked : function(){
37633         return this.checkbox ? this.checkbox.checked : false;
37634     },
37635
37636     updateExpandIcon : function(){
37637         if(this.rendered){
37638             var n = this.node, c1, c2;
37639             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
37640             var hasChild = n.hasChildNodes();
37641             if(hasChild){
37642                 if(n.expanded){
37643                     cls += "-minus";
37644                     c1 = "x-tree-node-collapsed";
37645                     c2 = "x-tree-node-expanded";
37646                 }else{
37647                     cls += "-plus";
37648                     c1 = "x-tree-node-expanded";
37649                     c2 = "x-tree-node-collapsed";
37650                 }
37651                 if(this.wasLeaf){
37652                     this.removeClass("x-tree-node-leaf");
37653                     this.wasLeaf = false;
37654                 }
37655                 if(this.c1 != c1 || this.c2 != c2){
37656                     Roo.fly(this.elNode).replaceClass(c1, c2);
37657                     this.c1 = c1; this.c2 = c2;
37658                 }
37659             }else{
37660                 // this changes non-leafs into leafs if they have no children.
37661                 // it's not very rational behaviour..
37662                 
37663                 if(!this.wasLeaf && this.node.leaf){
37664                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
37665                     delete this.c1;
37666                     delete this.c2;
37667                     this.wasLeaf = true;
37668                 }
37669             }
37670             var ecc = "x-tree-ec-icon "+cls;
37671             if(this.ecc != ecc){
37672                 this.ecNode.className = ecc;
37673                 this.ecc = ecc;
37674             }
37675         }
37676     },
37677
37678     getChildIndent : function(){
37679         if(!this.childIndent){
37680             var buf = [];
37681             var p = this.node;
37682             while(p){
37683                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
37684                     if(!p.isLast()) {
37685                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
37686                     } else {
37687                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
37688                     }
37689                 }
37690                 p = p.parentNode;
37691             }
37692             this.childIndent = buf.join("");
37693         }
37694         return this.childIndent;
37695     },
37696
37697     renderIndent : function(){
37698         if(this.rendered){
37699             var indent = "";
37700             var p = this.node.parentNode;
37701             if(p){
37702                 indent = p.ui.getChildIndent();
37703             }
37704             if(this.indentMarkup != indent){ // don't rerender if not required
37705                 this.indentNode.innerHTML = indent;
37706                 this.indentMarkup = indent;
37707             }
37708             this.updateExpandIcon();
37709         }
37710     }
37711 };
37712
37713 Roo.tree.RootTreeNodeUI = function(){
37714     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
37715 };
37716 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
37717     render : function(){
37718         if(!this.rendered){
37719             var targetNode = this.node.ownerTree.innerCt.dom;
37720             this.node.expanded = true;
37721             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
37722             this.wrap = this.ctNode = targetNode.firstChild;
37723         }
37724     },
37725     collapse : function(){
37726     },
37727     expand : function(){
37728     }
37729 });/*
37730  * Based on:
37731  * Ext JS Library 1.1.1
37732  * Copyright(c) 2006-2007, Ext JS, LLC.
37733  *
37734  * Originally Released Under LGPL - original licence link has changed is not relivant.
37735  *
37736  * Fork - LGPL
37737  * <script type="text/javascript">
37738  */
37739 /**
37740  * @class Roo.tree.TreeLoader
37741  * @extends Roo.util.Observable
37742  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
37743  * nodes from a specified URL. The response must be a javascript Array definition
37744  * who's elements are node definition objects. eg:
37745  * <pre><code>
37746 {  success : true,
37747    data :      [
37748    
37749     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
37750     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
37751     ]
37752 }
37753
37754
37755 </code></pre>
37756  * <br><br>
37757  * The old style respose with just an array is still supported, but not recommended.
37758  * <br><br>
37759  *
37760  * A server request is sent, and child nodes are loaded only when a node is expanded.
37761  * The loading node's id is passed to the server under the parameter name "node" to
37762  * enable the server to produce the correct child nodes.
37763  * <br><br>
37764  * To pass extra parameters, an event handler may be attached to the "beforeload"
37765  * event, and the parameters specified in the TreeLoader's baseParams property:
37766  * <pre><code>
37767     myTreeLoader.on("beforeload", function(treeLoader, node) {
37768         this.baseParams.category = node.attributes.category;
37769     }, this);
37770     
37771 </code></pre>
37772  *
37773  * This would pass an HTTP parameter called "category" to the server containing
37774  * the value of the Node's "category" attribute.
37775  * @constructor
37776  * Creates a new Treeloader.
37777  * @param {Object} config A config object containing config properties.
37778  */
37779 Roo.tree.TreeLoader = function(config){
37780     this.baseParams = {};
37781     this.requestMethod = "POST";
37782     Roo.apply(this, config);
37783
37784     this.addEvents({
37785     
37786         /**
37787          * @event beforeload
37788          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
37789          * @param {Object} This TreeLoader object.
37790          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37791          * @param {Object} callback The callback function specified in the {@link #load} call.
37792          */
37793         beforeload : true,
37794         /**
37795          * @event load
37796          * Fires when the node has been successfuly loaded.
37797          * @param {Object} This TreeLoader object.
37798          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37799          * @param {Object} response The response object containing the data from the server.
37800          */
37801         load : true,
37802         /**
37803          * @event loadexception
37804          * Fires if the network request failed.
37805          * @param {Object} This TreeLoader object.
37806          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37807          * @param {Object} response The response object containing the data from the server.
37808          */
37809         loadexception : true,
37810         /**
37811          * @event create
37812          * Fires before a node is created, enabling you to return custom Node types 
37813          * @param {Object} This TreeLoader object.
37814          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
37815          */
37816         create : true
37817     });
37818
37819     Roo.tree.TreeLoader.superclass.constructor.call(this);
37820 };
37821
37822 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
37823     /**
37824     * @cfg {String} dataUrl The URL from which to request a Json string which
37825     * specifies an array of node definition object representing the child nodes
37826     * to be loaded.
37827     */
37828     /**
37829     * @cfg {String} requestMethod either GET or POST
37830     * defaults to POST (due to BC)
37831     * to be loaded.
37832     */
37833     /**
37834     * @cfg {Object} baseParams (optional) An object containing properties which
37835     * specify HTTP parameters to be passed to each request for child nodes.
37836     */
37837     /**
37838     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
37839     * created by this loader. If the attributes sent by the server have an attribute in this object,
37840     * they take priority.
37841     */
37842     /**
37843     * @cfg {Object} uiProviders (optional) An object containing properties which
37844     * 
37845     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
37846     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
37847     * <i>uiProvider</i> attribute of a returned child node is a string rather
37848     * than a reference to a TreeNodeUI implementation, this that string value
37849     * is used as a property name in the uiProviders object. You can define the provider named
37850     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
37851     */
37852     uiProviders : {},
37853
37854     /**
37855     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
37856     * child nodes before loading.
37857     */
37858     clearOnLoad : true,
37859
37860     /**
37861     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
37862     * property on loading, rather than expecting an array. (eg. more compatible to a standard
37863     * Grid query { data : [ .....] }
37864     */
37865     
37866     root : false,
37867      /**
37868     * @cfg {String} queryParam (optional) 
37869     * Name of the query as it will be passed on the querystring (defaults to 'node')
37870     * eg. the request will be ?node=[id]
37871     */
37872     
37873     
37874     queryParam: false,
37875     
37876     /**
37877      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
37878      * This is called automatically when a node is expanded, but may be used to reload
37879      * a node (or append new children if the {@link #clearOnLoad} option is false.)
37880      * @param {Roo.tree.TreeNode} node
37881      * @param {Function} callback
37882      */
37883     load : function(node, callback){
37884         if(this.clearOnLoad){
37885             while(node.firstChild){
37886                 node.removeChild(node.firstChild);
37887             }
37888         }
37889         if(node.attributes.children){ // preloaded json children
37890             var cs = node.attributes.children;
37891             for(var i = 0, len = cs.length; i < len; i++){
37892                 node.appendChild(this.createNode(cs[i]));
37893             }
37894             if(typeof callback == "function"){
37895                 callback();
37896             }
37897         }else if(this.dataUrl){
37898             this.requestData(node, callback);
37899         }
37900     },
37901
37902     getParams: function(node){
37903         var buf = [], bp = this.baseParams;
37904         for(var key in bp){
37905             if(typeof bp[key] != "function"){
37906                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
37907             }
37908         }
37909         var n = this.queryParam === false ? 'node' : this.queryParam;
37910         buf.push(n + "=", encodeURIComponent(node.id));
37911         return buf.join("");
37912     },
37913
37914     requestData : function(node, callback){
37915         if(this.fireEvent("beforeload", this, node, callback) !== false){
37916             this.transId = Roo.Ajax.request({
37917                 method:this.requestMethod,
37918                 url: this.dataUrl||this.url,
37919                 success: this.handleResponse,
37920                 failure: this.handleFailure,
37921                 scope: this,
37922                 argument: {callback: callback, node: node},
37923                 params: this.getParams(node)
37924             });
37925         }else{
37926             // if the load is cancelled, make sure we notify
37927             // the node that we are done
37928             if(typeof callback == "function"){
37929                 callback();
37930             }
37931         }
37932     },
37933
37934     isLoading : function(){
37935         return this.transId ? true : false;
37936     },
37937
37938     abort : function(){
37939         if(this.isLoading()){
37940             Roo.Ajax.abort(this.transId);
37941         }
37942     },
37943
37944     // private
37945     createNode : function(attr)
37946     {
37947         // apply baseAttrs, nice idea Corey!
37948         if(this.baseAttrs){
37949             Roo.applyIf(attr, this.baseAttrs);
37950         }
37951         if(this.applyLoader !== false){
37952             attr.loader = this;
37953         }
37954         // uiProvider = depreciated..
37955         
37956         if(typeof(attr.uiProvider) == 'string'){
37957            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
37958                 /**  eval:var:attr */ eval(attr.uiProvider);
37959         }
37960         if(typeof(this.uiProviders['default']) != 'undefined') {
37961             attr.uiProvider = this.uiProviders['default'];
37962         }
37963         
37964         this.fireEvent('create', this, attr);
37965         
37966         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
37967         return(attr.leaf ?
37968                         new Roo.tree.TreeNode(attr) :
37969                         new Roo.tree.AsyncTreeNode(attr));
37970     },
37971
37972     processResponse : function(response, node, callback)
37973     {
37974         var json = response.responseText;
37975         try {
37976             
37977             var o = Roo.decode(json);
37978             
37979             if (this.root === false && typeof(o.success) != undefined) {
37980                 this.root = 'data'; // the default behaviour for list like data..
37981                 }
37982                 
37983             if (this.root !== false &&  !o.success) {
37984                 // it's a failure condition.
37985                 var a = response.argument;
37986                 this.fireEvent("loadexception", this, a.node, response);
37987                 Roo.log("Load failed - should have a handler really");
37988                 return;
37989             }
37990             
37991             
37992             
37993             if (this.root !== false) {
37994                  o = o[this.root];
37995             }
37996             
37997             for(var i = 0, len = o.length; i < len; i++){
37998                 var n = this.createNode(o[i]);
37999                 if(n){
38000                     node.appendChild(n);
38001                 }
38002             }
38003             if(typeof callback == "function"){
38004                 callback(this, node);
38005             }
38006         }catch(e){
38007             this.handleFailure(response);
38008         }
38009     },
38010
38011     handleResponse : function(response){
38012         this.transId = false;
38013         var a = response.argument;
38014         this.processResponse(response, a.node, a.callback);
38015         this.fireEvent("load", this, a.node, response);
38016     },
38017
38018     handleFailure : function(response)
38019     {
38020         // should handle failure better..
38021         this.transId = false;
38022         var a = response.argument;
38023         this.fireEvent("loadexception", this, a.node, response);
38024         if(typeof a.callback == "function"){
38025             a.callback(this, a.node);
38026         }
38027     }
38028 });/*
38029  * Based on:
38030  * Ext JS Library 1.1.1
38031  * Copyright(c) 2006-2007, Ext JS, LLC.
38032  *
38033  * Originally Released Under LGPL - original licence link has changed is not relivant.
38034  *
38035  * Fork - LGPL
38036  * <script type="text/javascript">
38037  */
38038
38039 /**
38040 * @class Roo.tree.TreeFilter
38041 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
38042 * @param {TreePanel} tree
38043 * @param {Object} config (optional)
38044  */
38045 Roo.tree.TreeFilter = function(tree, config){
38046     this.tree = tree;
38047     this.filtered = {};
38048     Roo.apply(this, config);
38049 };
38050
38051 Roo.tree.TreeFilter.prototype = {
38052     clearBlank:false,
38053     reverse:false,
38054     autoClear:false,
38055     remove:false,
38056
38057      /**
38058      * Filter the data by a specific attribute.
38059      * @param {String/RegExp} value Either string that the attribute value
38060      * should start with or a RegExp to test against the attribute
38061      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
38062      * @param {TreeNode} startNode (optional) The node to start the filter at.
38063      */
38064     filter : function(value, attr, startNode){
38065         attr = attr || "text";
38066         var f;
38067         if(typeof value == "string"){
38068             var vlen = value.length;
38069             // auto clear empty filter
38070             if(vlen == 0 && this.clearBlank){
38071                 this.clear();
38072                 return;
38073             }
38074             value = value.toLowerCase();
38075             f = function(n){
38076                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
38077             };
38078         }else if(value.exec){ // regex?
38079             f = function(n){
38080                 return value.test(n.attributes[attr]);
38081             };
38082         }else{
38083             throw 'Illegal filter type, must be string or regex';
38084         }
38085         this.filterBy(f, null, startNode);
38086         },
38087
38088     /**
38089      * Filter by a function. The passed function will be called with each
38090      * node in the tree (or from the startNode). If the function returns true, the node is kept
38091      * otherwise it is filtered. If a node is filtered, its children are also filtered.
38092      * @param {Function} fn The filter function
38093      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
38094      */
38095     filterBy : function(fn, scope, startNode){
38096         startNode = startNode || this.tree.root;
38097         if(this.autoClear){
38098             this.clear();
38099         }
38100         var af = this.filtered, rv = this.reverse;
38101         var f = function(n){
38102             if(n == startNode){
38103                 return true;
38104             }
38105             if(af[n.id]){
38106                 return false;
38107             }
38108             var m = fn.call(scope || n, n);
38109             if(!m || rv){
38110                 af[n.id] = n;
38111                 n.ui.hide();
38112                 return false;
38113             }
38114             return true;
38115         };
38116         startNode.cascade(f);
38117         if(this.remove){
38118            for(var id in af){
38119                if(typeof id != "function"){
38120                    var n = af[id];
38121                    if(n && n.parentNode){
38122                        n.parentNode.removeChild(n);
38123                    }
38124                }
38125            }
38126         }
38127     },
38128
38129     /**
38130      * Clears the current filter. Note: with the "remove" option
38131      * set a filter cannot be cleared.
38132      */
38133     clear : function(){
38134         var t = this.tree;
38135         var af = this.filtered;
38136         for(var id in af){
38137             if(typeof id != "function"){
38138                 var n = af[id];
38139                 if(n){
38140                     n.ui.show();
38141                 }
38142             }
38143         }
38144         this.filtered = {};
38145     }
38146 };
38147 /*
38148  * Based on:
38149  * Ext JS Library 1.1.1
38150  * Copyright(c) 2006-2007, Ext JS, LLC.
38151  *
38152  * Originally Released Under LGPL - original licence link has changed is not relivant.
38153  *
38154  * Fork - LGPL
38155  * <script type="text/javascript">
38156  */
38157  
38158
38159 /**
38160  * @class Roo.tree.TreeSorter
38161  * Provides sorting of nodes in a TreePanel
38162  * 
38163  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
38164  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
38165  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
38166  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
38167  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
38168  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
38169  * @constructor
38170  * @param {TreePanel} tree
38171  * @param {Object} config
38172  */
38173 Roo.tree.TreeSorter = function(tree, config){
38174     Roo.apply(this, config);
38175     tree.on("beforechildrenrendered", this.doSort, this);
38176     tree.on("append", this.updateSort, this);
38177     tree.on("insert", this.updateSort, this);
38178     
38179     var dsc = this.dir && this.dir.toLowerCase() == "desc";
38180     var p = this.property || "text";
38181     var sortType = this.sortType;
38182     var fs = this.folderSort;
38183     var cs = this.caseSensitive === true;
38184     var leafAttr = this.leafAttr || 'leaf';
38185
38186     this.sortFn = function(n1, n2){
38187         if(fs){
38188             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
38189                 return 1;
38190             }
38191             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
38192                 return -1;
38193             }
38194         }
38195         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
38196         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
38197         if(v1 < v2){
38198                         return dsc ? +1 : -1;
38199                 }else if(v1 > v2){
38200                         return dsc ? -1 : +1;
38201         }else{
38202                 return 0;
38203         }
38204     };
38205 };
38206
38207 Roo.tree.TreeSorter.prototype = {
38208     doSort : function(node){
38209         node.sort(this.sortFn);
38210     },
38211     
38212     compareNodes : function(n1, n2){
38213         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
38214     },
38215     
38216     updateSort : function(tree, node){
38217         if(node.childrenRendered){
38218             this.doSort.defer(1, this, [node]);
38219         }
38220     }
38221 };/*
38222  * Based on:
38223  * Ext JS Library 1.1.1
38224  * Copyright(c) 2006-2007, Ext JS, LLC.
38225  *
38226  * Originally Released Under LGPL - original licence link has changed is not relivant.
38227  *
38228  * Fork - LGPL
38229  * <script type="text/javascript">
38230  */
38231
38232 if(Roo.dd.DropZone){
38233     
38234 Roo.tree.TreeDropZone = function(tree, config){
38235     this.allowParentInsert = false;
38236     this.allowContainerDrop = false;
38237     this.appendOnly = false;
38238     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
38239     this.tree = tree;
38240     this.lastInsertClass = "x-tree-no-status";
38241     this.dragOverData = {};
38242 };
38243
38244 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
38245     ddGroup : "TreeDD",
38246     scroll:  true,
38247     
38248     expandDelay : 1000,
38249     
38250     expandNode : function(node){
38251         if(node.hasChildNodes() && !node.isExpanded()){
38252             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
38253         }
38254     },
38255     
38256     queueExpand : function(node){
38257         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
38258     },
38259     
38260     cancelExpand : function(){
38261         if(this.expandProcId){
38262             clearTimeout(this.expandProcId);
38263             this.expandProcId = false;
38264         }
38265     },
38266     
38267     isValidDropPoint : function(n, pt, dd, e, data){
38268         if(!n || !data){ return false; }
38269         var targetNode = n.node;
38270         var dropNode = data.node;
38271         // default drop rules
38272         if(!(targetNode && targetNode.isTarget && pt)){
38273             return false;
38274         }
38275         if(pt == "append" && targetNode.allowChildren === false){
38276             return false;
38277         }
38278         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
38279             return false;
38280         }
38281         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
38282             return false;
38283         }
38284         // reuse the object
38285         var overEvent = this.dragOverData;
38286         overEvent.tree = this.tree;
38287         overEvent.target = targetNode;
38288         overEvent.data = data;
38289         overEvent.point = pt;
38290         overEvent.source = dd;
38291         overEvent.rawEvent = e;
38292         overEvent.dropNode = dropNode;
38293         overEvent.cancel = false;  
38294         var result = this.tree.fireEvent("nodedragover", overEvent);
38295         return overEvent.cancel === false && result !== false;
38296     },
38297     
38298     getDropPoint : function(e, n, dd)
38299     {
38300         var tn = n.node;
38301         if(tn.isRoot){
38302             return tn.allowChildren !== false ? "append" : false; // always append for root
38303         }
38304         var dragEl = n.ddel;
38305         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
38306         var y = Roo.lib.Event.getPageY(e);
38307         //var noAppend = tn.allowChildren === false || tn.isLeaf();
38308         
38309         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
38310         var noAppend = tn.allowChildren === false;
38311         if(this.appendOnly || tn.parentNode.allowChildren === false){
38312             return noAppend ? false : "append";
38313         }
38314         var noBelow = false;
38315         if(!this.allowParentInsert){
38316             noBelow = tn.hasChildNodes() && tn.isExpanded();
38317         }
38318         var q = (b - t) / (noAppend ? 2 : 3);
38319         if(y >= t && y < (t + q)){
38320             return "above";
38321         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
38322             return "below";
38323         }else{
38324             return "append";
38325         }
38326     },
38327     
38328     onNodeEnter : function(n, dd, e, data)
38329     {
38330         this.cancelExpand();
38331     },
38332     
38333     onNodeOver : function(n, dd, e, data)
38334     {
38335        
38336         var pt = this.getDropPoint(e, n, dd);
38337         var node = n.node;
38338         
38339         // auto node expand check
38340         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
38341             this.queueExpand(node);
38342         }else if(pt != "append"){
38343             this.cancelExpand();
38344         }
38345         
38346         // set the insert point style on the target node
38347         var returnCls = this.dropNotAllowed;
38348         if(this.isValidDropPoint(n, pt, dd, e, data)){
38349            if(pt){
38350                var el = n.ddel;
38351                var cls;
38352                if(pt == "above"){
38353                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
38354                    cls = "x-tree-drag-insert-above";
38355                }else if(pt == "below"){
38356                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
38357                    cls = "x-tree-drag-insert-below";
38358                }else{
38359                    returnCls = "x-tree-drop-ok-append";
38360                    cls = "x-tree-drag-append";
38361                }
38362                if(this.lastInsertClass != cls){
38363                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
38364                    this.lastInsertClass = cls;
38365                }
38366            }
38367        }
38368        return returnCls;
38369     },
38370     
38371     onNodeOut : function(n, dd, e, data){
38372         
38373         this.cancelExpand();
38374         this.removeDropIndicators(n);
38375     },
38376     
38377     onNodeDrop : function(n, dd, e, data){
38378         var point = this.getDropPoint(e, n, dd);
38379         var targetNode = n.node;
38380         targetNode.ui.startDrop();
38381         if(!this.isValidDropPoint(n, point, dd, e, data)){
38382             targetNode.ui.endDrop();
38383             return false;
38384         }
38385         // first try to find the drop node
38386         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
38387         var dropEvent = {
38388             tree : this.tree,
38389             target: targetNode,
38390             data: data,
38391             point: point,
38392             source: dd,
38393             rawEvent: e,
38394             dropNode: dropNode,
38395             cancel: !dropNode   
38396         };
38397         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
38398         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
38399             targetNode.ui.endDrop();
38400             return false;
38401         }
38402         // allow target changing
38403         targetNode = dropEvent.target;
38404         if(point == "append" && !targetNode.isExpanded()){
38405             targetNode.expand(false, null, function(){
38406                 this.completeDrop(dropEvent);
38407             }.createDelegate(this));
38408         }else{
38409             this.completeDrop(dropEvent);
38410         }
38411         return true;
38412     },
38413     
38414     completeDrop : function(de){
38415         var ns = de.dropNode, p = de.point, t = de.target;
38416         if(!(ns instanceof Array)){
38417             ns = [ns];
38418         }
38419         var n;
38420         for(var i = 0, len = ns.length; i < len; i++){
38421             n = ns[i];
38422             if(p == "above"){
38423                 t.parentNode.insertBefore(n, t);
38424             }else if(p == "below"){
38425                 t.parentNode.insertBefore(n, t.nextSibling);
38426             }else{
38427                 t.appendChild(n);
38428             }
38429         }
38430         n.ui.focus();
38431         if(this.tree.hlDrop){
38432             n.ui.highlight();
38433         }
38434         t.ui.endDrop();
38435         this.tree.fireEvent("nodedrop", de);
38436     },
38437     
38438     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
38439         if(this.tree.hlDrop){
38440             dropNode.ui.focus();
38441             dropNode.ui.highlight();
38442         }
38443         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
38444     },
38445     
38446     getTree : function(){
38447         return this.tree;
38448     },
38449     
38450     removeDropIndicators : function(n){
38451         if(n && n.ddel){
38452             var el = n.ddel;
38453             Roo.fly(el).removeClass([
38454                     "x-tree-drag-insert-above",
38455                     "x-tree-drag-insert-below",
38456                     "x-tree-drag-append"]);
38457             this.lastInsertClass = "_noclass";
38458         }
38459     },
38460     
38461     beforeDragDrop : function(target, e, id){
38462         this.cancelExpand();
38463         return true;
38464     },
38465     
38466     afterRepair : function(data){
38467         if(data && Roo.enableFx){
38468             data.node.ui.highlight();
38469         }
38470         this.hideProxy();
38471     } 
38472     
38473 });
38474
38475 }
38476 /*
38477  * Based on:
38478  * Ext JS Library 1.1.1
38479  * Copyright(c) 2006-2007, Ext JS, LLC.
38480  *
38481  * Originally Released Under LGPL - original licence link has changed is not relivant.
38482  *
38483  * Fork - LGPL
38484  * <script type="text/javascript">
38485  */
38486  
38487
38488 if(Roo.dd.DragZone){
38489 Roo.tree.TreeDragZone = function(tree, config){
38490     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
38491     this.tree = tree;
38492 };
38493
38494 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
38495     ddGroup : "TreeDD",
38496    
38497     onBeforeDrag : function(data, e){
38498         var n = data.node;
38499         return n && n.draggable && !n.disabled;
38500     },
38501      
38502     
38503     onInitDrag : function(e){
38504         var data = this.dragData;
38505         this.tree.getSelectionModel().select(data.node);
38506         this.proxy.update("");
38507         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
38508         this.tree.fireEvent("startdrag", this.tree, data.node, e);
38509     },
38510     
38511     getRepairXY : function(e, data){
38512         return data.node.ui.getDDRepairXY();
38513     },
38514     
38515     onEndDrag : function(data, e){
38516         this.tree.fireEvent("enddrag", this.tree, data.node, e);
38517         
38518         
38519     },
38520     
38521     onValidDrop : function(dd, e, id){
38522         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
38523         this.hideProxy();
38524     },
38525     
38526     beforeInvalidDrop : function(e, id){
38527         // this scrolls the original position back into view
38528         var sm = this.tree.getSelectionModel();
38529         sm.clearSelections();
38530         sm.select(this.dragData.node);
38531     }
38532 });
38533 }/*
38534  * Based on:
38535  * Ext JS Library 1.1.1
38536  * Copyright(c) 2006-2007, Ext JS, LLC.
38537  *
38538  * Originally Released Under LGPL - original licence link has changed is not relivant.
38539  *
38540  * Fork - LGPL
38541  * <script type="text/javascript">
38542  */
38543 /**
38544  * @class Roo.tree.TreeEditor
38545  * @extends Roo.Editor
38546  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
38547  * as the editor field.
38548  * @constructor
38549  * @param {Object} config (used to be the tree panel.)
38550  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
38551  * 
38552  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
38553  * @cfg {Roo.form.TextField} field [required] The field configuration
38554  *
38555  * 
38556  */
38557 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
38558     var tree = config;
38559     var field;
38560     if (oldconfig) { // old style..
38561         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
38562     } else {
38563         // new style..
38564         tree = config.tree;
38565         config.field = config.field  || {};
38566         config.field.xtype = 'TextField';
38567         field = Roo.factory(config.field, Roo.form);
38568     }
38569     config = config || {};
38570     
38571     
38572     this.addEvents({
38573         /**
38574          * @event beforenodeedit
38575          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
38576          * false from the handler of this event.
38577          * @param {Editor} this
38578          * @param {Roo.tree.Node} node 
38579          */
38580         "beforenodeedit" : true
38581     });
38582     
38583     //Roo.log(config);
38584     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
38585
38586     this.tree = tree;
38587
38588     tree.on('beforeclick', this.beforeNodeClick, this);
38589     tree.getTreeEl().on('mousedown', this.hide, this);
38590     this.on('complete', this.updateNode, this);
38591     this.on('beforestartedit', this.fitToTree, this);
38592     this.on('startedit', this.bindScroll, this, {delay:10});
38593     this.on('specialkey', this.onSpecialKey, this);
38594 };
38595
38596 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
38597     /**
38598      * @cfg {String} alignment
38599      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
38600      */
38601     alignment: "l-l",
38602     // inherit
38603     autoSize: false,
38604     /**
38605      * @cfg {Boolean} hideEl
38606      * True to hide the bound element while the editor is displayed (defaults to false)
38607      */
38608     hideEl : false,
38609     /**
38610      * @cfg {String} cls
38611      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
38612      */
38613     cls: "x-small-editor x-tree-editor",
38614     /**
38615      * @cfg {Boolean} shim
38616      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
38617      */
38618     shim:false,
38619     // inherit
38620     shadow:"frame",
38621     /**
38622      * @cfg {Number} maxWidth
38623      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
38624      * the containing tree element's size, it will be automatically limited for you to the container width, taking
38625      * scroll and client offsets into account prior to each edit.
38626      */
38627     maxWidth: 250,
38628
38629     editDelay : 350,
38630
38631     // private
38632     fitToTree : function(ed, el){
38633         var td = this.tree.getTreeEl().dom, nd = el.dom;
38634         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
38635             td.scrollLeft = nd.offsetLeft;
38636         }
38637         var w = Math.min(
38638                 this.maxWidth,
38639                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
38640         this.setSize(w, '');
38641         
38642         return this.fireEvent('beforenodeedit', this, this.editNode);
38643         
38644     },
38645
38646     // private
38647     triggerEdit : function(node){
38648         this.completeEdit();
38649         this.editNode = node;
38650         this.startEdit(node.ui.textNode, node.text);
38651     },
38652
38653     // private
38654     bindScroll : function(){
38655         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
38656     },
38657
38658     // private
38659     beforeNodeClick : function(node, e){
38660         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
38661         this.lastClick = new Date();
38662         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
38663             e.stopEvent();
38664             this.triggerEdit(node);
38665             return false;
38666         }
38667         return true;
38668     },
38669
38670     // private
38671     updateNode : function(ed, value){
38672         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
38673         this.editNode.setText(value);
38674     },
38675
38676     // private
38677     onHide : function(){
38678         Roo.tree.TreeEditor.superclass.onHide.call(this);
38679         if(this.editNode){
38680             this.editNode.ui.focus();
38681         }
38682     },
38683
38684     // private
38685     onSpecialKey : function(field, e){
38686         var k = e.getKey();
38687         if(k == e.ESC){
38688             e.stopEvent();
38689             this.cancelEdit();
38690         }else if(k == e.ENTER && !e.hasModifier()){
38691             e.stopEvent();
38692             this.completeEdit();
38693         }
38694     }
38695 });//<Script type="text/javascript">
38696 /*
38697  * Based on:
38698  * Ext JS Library 1.1.1
38699  * Copyright(c) 2006-2007, Ext JS, LLC.
38700  *
38701  * Originally Released Under LGPL - original licence link has changed is not relivant.
38702  *
38703  * Fork - LGPL
38704  * <script type="text/javascript">
38705  */
38706  
38707 /**
38708  * Not documented??? - probably should be...
38709  */
38710
38711 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
38712     //focus: Roo.emptyFn, // prevent odd scrolling behavior
38713     
38714     renderElements : function(n, a, targetNode, bulkRender){
38715         //consel.log("renderElements?");
38716         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
38717
38718         var t = n.getOwnerTree();
38719         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
38720         
38721         var cols = t.columns;
38722         var bw = t.borderWidth;
38723         var c = cols[0];
38724         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
38725          var cb = typeof a.checked == "boolean";
38726         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38727         var colcls = 'x-t-' + tid + '-c0';
38728         var buf = [
38729             '<li class="x-tree-node">',
38730             
38731                 
38732                 '<div class="x-tree-node-el ', a.cls,'">',
38733                     // extran...
38734                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
38735                 
38736                 
38737                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
38738                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
38739                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
38740                            (a.icon ? ' x-tree-node-inline-icon' : ''),
38741                            (a.iconCls ? ' '+a.iconCls : ''),
38742                            '" unselectable="on" />',
38743                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
38744                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
38745                              
38746                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38747                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
38748                             '<span unselectable="on" qtip="' + tx + '">',
38749                              tx,
38750                              '</span></a>' ,
38751                     '</div>',
38752                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38753                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
38754                  ];
38755         for(var i = 1, len = cols.length; i < len; i++){
38756             c = cols[i];
38757             colcls = 'x-t-' + tid + '-c' +i;
38758             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38759             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
38760                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
38761                       "</div>");
38762          }
38763          
38764          buf.push(
38765             '</a>',
38766             '<div class="x-clear"></div></div>',
38767             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
38768             "</li>");
38769         
38770         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
38771             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
38772                                 n.nextSibling.ui.getEl(), buf.join(""));
38773         }else{
38774             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
38775         }
38776         var el = this.wrap.firstChild;
38777         this.elRow = el;
38778         this.elNode = el.firstChild;
38779         this.ranchor = el.childNodes[1];
38780         this.ctNode = this.wrap.childNodes[1];
38781         var cs = el.firstChild.childNodes;
38782         this.indentNode = cs[0];
38783         this.ecNode = cs[1];
38784         this.iconNode = cs[2];
38785         var index = 3;
38786         if(cb){
38787             this.checkbox = cs[3];
38788             index++;
38789         }
38790         this.anchor = cs[index];
38791         
38792         this.textNode = cs[index].firstChild;
38793         
38794         //el.on("click", this.onClick, this);
38795         //el.on("dblclick", this.onDblClick, this);
38796         
38797         
38798        // console.log(this);
38799     },
38800     initEvents : function(){
38801         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
38802         
38803             
38804         var a = this.ranchor;
38805
38806         var el = Roo.get(a);
38807
38808         if(Roo.isOpera){ // opera render bug ignores the CSS
38809             el.setStyle("text-decoration", "none");
38810         }
38811
38812         el.on("click", this.onClick, this);
38813         el.on("dblclick", this.onDblClick, this);
38814         el.on("contextmenu", this.onContextMenu, this);
38815         
38816     },
38817     
38818     /*onSelectedChange : function(state){
38819         if(state){
38820             this.focus();
38821             this.addClass("x-tree-selected");
38822         }else{
38823             //this.blur();
38824             this.removeClass("x-tree-selected");
38825         }
38826     },*/
38827     addClass : function(cls){
38828         if(this.elRow){
38829             Roo.fly(this.elRow).addClass(cls);
38830         }
38831         
38832     },
38833     
38834     
38835     removeClass : function(cls){
38836         if(this.elRow){
38837             Roo.fly(this.elRow).removeClass(cls);
38838         }
38839     }
38840
38841     
38842     
38843 });//<Script type="text/javascript">
38844
38845 /*
38846  * Based on:
38847  * Ext JS Library 1.1.1
38848  * Copyright(c) 2006-2007, Ext JS, LLC.
38849  *
38850  * Originally Released Under LGPL - original licence link has changed is not relivant.
38851  *
38852  * Fork - LGPL
38853  * <script type="text/javascript">
38854  */
38855  
38856
38857 /**
38858  * @class Roo.tree.ColumnTree
38859  * @extends Roo.tree.TreePanel
38860  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
38861  * @cfg {int} borderWidth  compined right/left border allowance
38862  * @constructor
38863  * @param {String/HTMLElement/Element} el The container element
38864  * @param {Object} config
38865  */
38866 Roo.tree.ColumnTree =  function(el, config)
38867 {
38868    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
38869    this.addEvents({
38870         /**
38871         * @event resize
38872         * Fire this event on a container when it resizes
38873         * @param {int} w Width
38874         * @param {int} h Height
38875         */
38876        "resize" : true
38877     });
38878     this.on('resize', this.onResize, this);
38879 };
38880
38881 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
38882     //lines:false,
38883     
38884     
38885     borderWidth: Roo.isBorderBox ? 0 : 2, 
38886     headEls : false,
38887     
38888     render : function(){
38889         // add the header.....
38890        
38891         Roo.tree.ColumnTree.superclass.render.apply(this);
38892         
38893         this.el.addClass('x-column-tree');
38894         
38895         this.headers = this.el.createChild(
38896             {cls:'x-tree-headers'},this.innerCt.dom);
38897    
38898         var cols = this.columns, c;
38899         var totalWidth = 0;
38900         this.headEls = [];
38901         var  len = cols.length;
38902         for(var i = 0; i < len; i++){
38903              c = cols[i];
38904              totalWidth += c.width;
38905             this.headEls.push(this.headers.createChild({
38906                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
38907                  cn: {
38908                      cls:'x-tree-hd-text',
38909                      html: c.header
38910                  },
38911                  style:'width:'+(c.width-this.borderWidth)+'px;'
38912              }));
38913         }
38914         this.headers.createChild({cls:'x-clear'});
38915         // prevent floats from wrapping when clipped
38916         this.headers.setWidth(totalWidth);
38917         //this.innerCt.setWidth(totalWidth);
38918         this.innerCt.setStyle({ overflow: 'auto' });
38919         this.onResize(this.width, this.height);
38920              
38921         
38922     },
38923     onResize : function(w,h)
38924     {
38925         this.height = h;
38926         this.width = w;
38927         // resize cols..
38928         this.innerCt.setWidth(this.width);
38929         this.innerCt.setHeight(this.height-20);
38930         
38931         // headers...
38932         var cols = this.columns, c;
38933         var totalWidth = 0;
38934         var expEl = false;
38935         var len = cols.length;
38936         for(var i = 0; i < len; i++){
38937             c = cols[i];
38938             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
38939                 // it's the expander..
38940                 expEl  = this.headEls[i];
38941                 continue;
38942             }
38943             totalWidth += c.width;
38944             
38945         }
38946         if (expEl) {
38947             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
38948         }
38949         this.headers.setWidth(w-20);
38950
38951         
38952         
38953         
38954     }
38955 });
38956 /*
38957  * Based on:
38958  * Ext JS Library 1.1.1
38959  * Copyright(c) 2006-2007, Ext JS, LLC.
38960  *
38961  * Originally Released Under LGPL - original licence link has changed is not relivant.
38962  *
38963  * Fork - LGPL
38964  * <script type="text/javascript">
38965  */
38966  
38967 /**
38968  * @class Roo.menu.Menu
38969  * @extends Roo.util.Observable
38970  * @children Roo.menu.Item Roo.menu.Separator Roo.menu.TextItem
38971  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
38972  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
38973  * @constructor
38974  * Creates a new Menu
38975  * @param {Object} config Configuration options
38976  */
38977 Roo.menu.Menu = function(config){
38978     
38979     Roo.menu.Menu.superclass.constructor.call(this, config);
38980     
38981     this.id = this.id || Roo.id();
38982     this.addEvents({
38983         /**
38984          * @event beforeshow
38985          * Fires before this menu is displayed
38986          * @param {Roo.menu.Menu} this
38987          */
38988         beforeshow : true,
38989         /**
38990          * @event beforehide
38991          * Fires before this menu is hidden
38992          * @param {Roo.menu.Menu} this
38993          */
38994         beforehide : true,
38995         /**
38996          * @event show
38997          * Fires after this menu is displayed
38998          * @param {Roo.menu.Menu} this
38999          */
39000         show : true,
39001         /**
39002          * @event hide
39003          * Fires after this menu is hidden
39004          * @param {Roo.menu.Menu} this
39005          */
39006         hide : true,
39007         /**
39008          * @event click
39009          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
39010          * @param {Roo.menu.Menu} this
39011          * @param {Roo.menu.Item} menuItem The menu item that was clicked
39012          * @param {Roo.EventObject} e
39013          */
39014         click : true,
39015         /**
39016          * @event mouseover
39017          * Fires when the mouse is hovering over this menu
39018          * @param {Roo.menu.Menu} this
39019          * @param {Roo.EventObject} e
39020          * @param {Roo.menu.Item} menuItem The menu item that was clicked
39021          */
39022         mouseover : true,
39023         /**
39024          * @event mouseout
39025          * Fires when the mouse exits this menu
39026          * @param {Roo.menu.Menu} this
39027          * @param {Roo.EventObject} e
39028          * @param {Roo.menu.Item} menuItem The menu item that was clicked
39029          */
39030         mouseout : true,
39031         /**
39032          * @event itemclick
39033          * Fires when a menu item contained in this menu is clicked
39034          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
39035          * @param {Roo.EventObject} e
39036          */
39037         itemclick: true
39038     });
39039     if (this.registerMenu) {
39040         Roo.menu.MenuMgr.register(this);
39041     }
39042     
39043     var mis = this.items;
39044     this.items = new Roo.util.MixedCollection();
39045     if(mis){
39046         this.add.apply(this, mis);
39047     }
39048 };
39049
39050 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
39051     /**
39052      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
39053      */
39054     minWidth : 120,
39055     /**
39056      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
39057      * for bottom-right shadow (defaults to "sides")
39058      */
39059     shadow : "sides",
39060     /**
39061      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
39062      * this menu (defaults to "tl-tr?")
39063      */
39064     subMenuAlign : "tl-tr?",
39065     /**
39066      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
39067      * relative to its element of origin (defaults to "tl-bl?")
39068      */
39069     defaultAlign : "tl-bl?",
39070     /**
39071      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
39072      */
39073     allowOtherMenus : false,
39074     /**
39075      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
39076      */
39077     registerMenu : true,
39078
39079     hidden:true,
39080
39081     // private
39082     render : function(){
39083         if(this.el){
39084             return;
39085         }
39086         var el = this.el = new Roo.Layer({
39087             cls: "x-menu",
39088             shadow:this.shadow,
39089             constrain: false,
39090             parentEl: this.parentEl || document.body,
39091             zindex:15000
39092         });
39093
39094         this.keyNav = new Roo.menu.MenuNav(this);
39095
39096         if(this.plain){
39097             el.addClass("x-menu-plain");
39098         }
39099         if(this.cls){
39100             el.addClass(this.cls);
39101         }
39102         // generic focus element
39103         this.focusEl = el.createChild({
39104             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
39105         });
39106         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
39107         //disabling touch- as it's causing issues ..
39108         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
39109         ul.on('click'   , this.onClick, this);
39110         
39111         
39112         ul.on("mouseover", this.onMouseOver, this);
39113         ul.on("mouseout", this.onMouseOut, this);
39114         this.items.each(function(item){
39115             if (item.hidden) {
39116                 return;
39117             }
39118             
39119             var li = document.createElement("li");
39120             li.className = "x-menu-list-item";
39121             ul.dom.appendChild(li);
39122             item.render(li, this);
39123         }, this);
39124         this.ul = ul;
39125         this.autoWidth();
39126     },
39127
39128     // private
39129     autoWidth : function(){
39130         var el = this.el, ul = this.ul;
39131         if(!el){
39132             return;
39133         }
39134         var w = this.width;
39135         if(w){
39136             el.setWidth(w);
39137         }else if(Roo.isIE){
39138             el.setWidth(this.minWidth);
39139             var t = el.dom.offsetWidth; // force recalc
39140             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
39141         }
39142     },
39143
39144     // private
39145     delayAutoWidth : function(){
39146         if(this.rendered){
39147             if(!this.awTask){
39148                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
39149             }
39150             this.awTask.delay(20);
39151         }
39152     },
39153
39154     // private
39155     findTargetItem : function(e){
39156         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
39157         if(t && t.menuItemId){
39158             return this.items.get(t.menuItemId);
39159         }
39160     },
39161
39162     // private
39163     onClick : function(e){
39164         Roo.log("menu.onClick");
39165         var t = this.findTargetItem(e);
39166         if(!t){
39167             return;
39168         }
39169         Roo.log(e);
39170         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
39171             if(t == this.activeItem && t.shouldDeactivate(e)){
39172                 this.activeItem.deactivate();
39173                 delete this.activeItem;
39174                 return;
39175             }
39176             if(t.canActivate){
39177                 this.setActiveItem(t, true);
39178             }
39179             return;
39180             
39181             
39182         }
39183         
39184         t.onClick(e);
39185         this.fireEvent("click", this, t, e);
39186     },
39187
39188     // private
39189     setActiveItem : function(item, autoExpand){
39190         if(item != this.activeItem){
39191             if(this.activeItem){
39192                 this.activeItem.deactivate();
39193             }
39194             this.activeItem = item;
39195             item.activate(autoExpand);
39196         }else if(autoExpand){
39197             item.expandMenu();
39198         }
39199     },
39200
39201     // private
39202     tryActivate : function(start, step){
39203         var items = this.items;
39204         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
39205             var item = items.get(i);
39206             if(!item.disabled && item.canActivate){
39207                 this.setActiveItem(item, false);
39208                 return item;
39209             }
39210         }
39211         return false;
39212     },
39213
39214     // private
39215     onMouseOver : function(e){
39216         var t;
39217         if(t = this.findTargetItem(e)){
39218             if(t.canActivate && !t.disabled){
39219                 this.setActiveItem(t, true);
39220             }
39221         }
39222         this.fireEvent("mouseover", this, e, t);
39223     },
39224
39225     // private
39226     onMouseOut : function(e){
39227         var t;
39228         if(t = this.findTargetItem(e)){
39229             if(t == this.activeItem && t.shouldDeactivate(e)){
39230                 this.activeItem.deactivate();
39231                 delete this.activeItem;
39232             }
39233         }
39234         this.fireEvent("mouseout", this, e, t);
39235     },
39236
39237     /**
39238      * Read-only.  Returns true if the menu is currently displayed, else false.
39239      * @type Boolean
39240      */
39241     isVisible : function(){
39242         return this.el && !this.hidden;
39243     },
39244
39245     /**
39246      * Displays this menu relative to another element
39247      * @param {String/HTMLElement/Roo.Element} element The element to align to
39248      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
39249      * the element (defaults to this.defaultAlign)
39250      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
39251      */
39252     show : function(el, pos, parentMenu){
39253         this.parentMenu = parentMenu;
39254         if(!this.el){
39255             this.render();
39256         }
39257         this.fireEvent("beforeshow", this);
39258         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
39259     },
39260
39261     /**
39262      * Displays this menu at a specific xy position
39263      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
39264      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
39265      */
39266     showAt : function(xy, parentMenu, /* private: */_e){
39267         this.parentMenu = parentMenu;
39268         if(!this.el){
39269             this.render();
39270         }
39271         if(_e !== false){
39272             this.fireEvent("beforeshow", this);
39273             xy = this.el.adjustForConstraints(xy);
39274         }
39275         this.el.setXY(xy);
39276         this.el.show();
39277         this.hidden = false;
39278         this.focus();
39279         this.fireEvent("show", this);
39280     },
39281
39282     focus : function(){
39283         if(!this.hidden){
39284             this.doFocus.defer(50, this);
39285         }
39286     },
39287
39288     doFocus : function(){
39289         if(!this.hidden){
39290             this.focusEl.focus();
39291         }
39292     },
39293
39294     /**
39295      * Hides this menu and optionally all parent menus
39296      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
39297      */
39298     hide : function(deep){
39299         if(this.el && this.isVisible()){
39300             this.fireEvent("beforehide", this);
39301             if(this.activeItem){
39302                 this.activeItem.deactivate();
39303                 this.activeItem = null;
39304             }
39305             this.el.hide();
39306             this.hidden = true;
39307             this.fireEvent("hide", this);
39308         }
39309         if(deep === true && this.parentMenu){
39310             this.parentMenu.hide(true);
39311         }
39312     },
39313
39314     /**
39315      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
39316      * Any of the following are valid:
39317      * <ul>
39318      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
39319      * <li>An HTMLElement object which will be converted to a menu item</li>
39320      * <li>A menu item config object that will be created as a new menu item</li>
39321      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
39322      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
39323      * </ul>
39324      * Usage:
39325      * <pre><code>
39326 // Create the menu
39327 var menu = new Roo.menu.Menu();
39328
39329 // Create a menu item to add by reference
39330 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
39331
39332 // Add a bunch of items at once using different methods.
39333 // Only the last item added will be returned.
39334 var item = menu.add(
39335     menuItem,                // add existing item by ref
39336     'Dynamic Item',          // new TextItem
39337     '-',                     // new separator
39338     { text: 'Config Item' }  // new item by config
39339 );
39340 </code></pre>
39341      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
39342      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
39343      */
39344     add : function(){
39345         var a = arguments, l = a.length, item;
39346         for(var i = 0; i < l; i++){
39347             var el = a[i];
39348             if ((typeof(el) == "object") && el.xtype && el.xns) {
39349                 el = Roo.factory(el, Roo.menu);
39350             }
39351             
39352             if(el.render){ // some kind of Item
39353                 item = this.addItem(el);
39354             }else if(typeof el == "string"){ // string
39355                 if(el == "separator" || el == "-"){
39356                     item = this.addSeparator();
39357                 }else{
39358                     item = this.addText(el);
39359                 }
39360             }else if(el.tagName || el.el){ // element
39361                 item = this.addElement(el);
39362             }else if(typeof el == "object"){ // must be menu item config?
39363                 item = this.addMenuItem(el);
39364             }
39365         }
39366         return item;
39367     },
39368
39369     /**
39370      * Returns this menu's underlying {@link Roo.Element} object
39371      * @return {Roo.Element} The element
39372      */
39373     getEl : function(){
39374         if(!this.el){
39375             this.render();
39376         }
39377         return this.el;
39378     },
39379
39380     /**
39381      * Adds a separator bar to the menu
39382      * @return {Roo.menu.Item} The menu item that was added
39383      */
39384     addSeparator : function(){
39385         return this.addItem(new Roo.menu.Separator());
39386     },
39387
39388     /**
39389      * Adds an {@link Roo.Element} object to the menu
39390      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
39391      * @return {Roo.menu.Item} The menu item that was added
39392      */
39393     addElement : function(el){
39394         return this.addItem(new Roo.menu.BaseItem(el));
39395     },
39396
39397     /**
39398      * Adds an existing object based on {@link Roo.menu.Item} to the menu
39399      * @param {Roo.menu.Item} item The menu item to add
39400      * @return {Roo.menu.Item} The menu item that was added
39401      */
39402     addItem : function(item){
39403         this.items.add(item);
39404         if(this.ul){
39405             var li = document.createElement("li");
39406             li.className = "x-menu-list-item";
39407             this.ul.dom.appendChild(li);
39408             item.render(li, this);
39409             this.delayAutoWidth();
39410         }
39411         return item;
39412     },
39413
39414     /**
39415      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
39416      * @param {Object} config A MenuItem config object
39417      * @return {Roo.menu.Item} The menu item that was added
39418      */
39419     addMenuItem : function(config){
39420         if(!(config instanceof Roo.menu.Item)){
39421             if(typeof config.checked == "boolean"){ // must be check menu item config?
39422                 config = new Roo.menu.CheckItem(config);
39423             }else{
39424                 config = new Roo.menu.Item(config);
39425             }
39426         }
39427         return this.addItem(config);
39428     },
39429
39430     /**
39431      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
39432      * @param {String} text The text to display in the menu item
39433      * @return {Roo.menu.Item} The menu item that was added
39434      */
39435     addText : function(text){
39436         return this.addItem(new Roo.menu.TextItem({ text : text }));
39437     },
39438
39439     /**
39440      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
39441      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
39442      * @param {Roo.menu.Item} item The menu item to add
39443      * @return {Roo.menu.Item} The menu item that was added
39444      */
39445     insert : function(index, item){
39446         this.items.insert(index, item);
39447         if(this.ul){
39448             var li = document.createElement("li");
39449             li.className = "x-menu-list-item";
39450             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
39451             item.render(li, this);
39452             this.delayAutoWidth();
39453         }
39454         return item;
39455     },
39456
39457     /**
39458      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
39459      * @param {Roo.menu.Item} item The menu item to remove
39460      */
39461     remove : function(item){
39462         this.items.removeKey(item.id);
39463         item.destroy();
39464     },
39465
39466     /**
39467      * Removes and destroys all items in the menu
39468      */
39469     removeAll : function(){
39470         var f;
39471         while(f = this.items.first()){
39472             this.remove(f);
39473         }
39474     }
39475 });
39476
39477 // MenuNav is a private utility class used internally by the Menu
39478 Roo.menu.MenuNav = function(menu){
39479     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
39480     this.scope = this.menu = menu;
39481 };
39482
39483 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
39484     doRelay : function(e, h){
39485         var k = e.getKey();
39486         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
39487             this.menu.tryActivate(0, 1);
39488             return false;
39489         }
39490         return h.call(this.scope || this, e, this.menu);
39491     },
39492
39493     up : function(e, m){
39494         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
39495             m.tryActivate(m.items.length-1, -1);
39496         }
39497     },
39498
39499     down : function(e, m){
39500         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
39501             m.tryActivate(0, 1);
39502         }
39503     },
39504
39505     right : function(e, m){
39506         if(m.activeItem){
39507             m.activeItem.expandMenu(true);
39508         }
39509     },
39510
39511     left : function(e, m){
39512         m.hide();
39513         if(m.parentMenu && m.parentMenu.activeItem){
39514             m.parentMenu.activeItem.activate();
39515         }
39516     },
39517
39518     enter : function(e, m){
39519         if(m.activeItem){
39520             e.stopPropagation();
39521             m.activeItem.onClick(e);
39522             m.fireEvent("click", this, m.activeItem);
39523             return true;
39524         }
39525     }
39526 });/*
39527  * Based on:
39528  * Ext JS Library 1.1.1
39529  * Copyright(c) 2006-2007, Ext JS, LLC.
39530  *
39531  * Originally Released Under LGPL - original licence link has changed is not relivant.
39532  *
39533  * Fork - LGPL
39534  * <script type="text/javascript">
39535  */
39536  
39537 /**
39538  * @class Roo.menu.MenuMgr
39539  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
39540  * @static
39541  */
39542 Roo.menu.MenuMgr = function(){
39543    var menus, active, groups = {}, attached = false, lastShow = new Date();
39544
39545    // private - called when first menu is created
39546    function init(){
39547        menus = {};
39548        active = new Roo.util.MixedCollection();
39549        Roo.get(document).addKeyListener(27, function(){
39550            if(active.length > 0){
39551                hideAll();
39552            }
39553        });
39554    }
39555
39556    // private
39557    function hideAll(){
39558        if(active && active.length > 0){
39559            var c = active.clone();
39560            c.each(function(m){
39561                m.hide();
39562            });
39563        }
39564    }
39565
39566    // private
39567    function onHide(m){
39568        active.remove(m);
39569        if(active.length < 1){
39570            Roo.get(document).un("mousedown", onMouseDown);
39571            attached = false;
39572        }
39573    }
39574
39575    // private
39576    function onShow(m){
39577        var last = active.last();
39578        lastShow = new Date();
39579        active.add(m);
39580        if(!attached){
39581            Roo.get(document).on("mousedown", onMouseDown);
39582            attached = true;
39583        }
39584        if(m.parentMenu){
39585           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
39586           m.parentMenu.activeChild = m;
39587        }else if(last && last.isVisible()){
39588           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
39589        }
39590    }
39591
39592    // private
39593    function onBeforeHide(m){
39594        if(m.activeChild){
39595            m.activeChild.hide();
39596        }
39597        if(m.autoHideTimer){
39598            clearTimeout(m.autoHideTimer);
39599            delete m.autoHideTimer;
39600        }
39601    }
39602
39603    // private
39604    function onBeforeShow(m){
39605        var pm = m.parentMenu;
39606        if(!pm && !m.allowOtherMenus){
39607            hideAll();
39608        }else if(pm && pm.activeChild && active != m){
39609            pm.activeChild.hide();
39610        }
39611    }
39612
39613    // private
39614    function onMouseDown(e){
39615        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
39616            hideAll();
39617        }
39618    }
39619
39620    // private
39621    function onBeforeCheck(mi, state){
39622        if(state){
39623            var g = groups[mi.group];
39624            for(var i = 0, l = g.length; i < l; i++){
39625                if(g[i] != mi){
39626                    g[i].setChecked(false);
39627                }
39628            }
39629        }
39630    }
39631
39632    return {
39633
39634        /**
39635         * Hides all menus that are currently visible
39636         */
39637        hideAll : function(){
39638             hideAll();  
39639        },
39640
39641        // private
39642        register : function(menu){
39643            if(!menus){
39644                init();
39645            }
39646            menus[menu.id] = menu;
39647            menu.on("beforehide", onBeforeHide);
39648            menu.on("hide", onHide);
39649            menu.on("beforeshow", onBeforeShow);
39650            menu.on("show", onShow);
39651            var g = menu.group;
39652            if(g && menu.events["checkchange"]){
39653                if(!groups[g]){
39654                    groups[g] = [];
39655                }
39656                groups[g].push(menu);
39657                menu.on("checkchange", onCheck);
39658            }
39659        },
39660
39661         /**
39662          * Returns a {@link Roo.menu.Menu} object
39663          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
39664          * be used to generate and return a new Menu instance.
39665          */
39666        get : function(menu){
39667            if(typeof menu == "string"){ // menu id
39668                return menus[menu];
39669            }else if(menu.events){  // menu instance
39670                return menu;
39671            }else if(typeof menu.length == 'number'){ // array of menu items?
39672                return new Roo.menu.Menu({items:menu});
39673            }else{ // otherwise, must be a config
39674                return new Roo.menu.Menu(menu);
39675            }
39676        },
39677
39678        // private
39679        unregister : function(menu){
39680            delete menus[menu.id];
39681            menu.un("beforehide", onBeforeHide);
39682            menu.un("hide", onHide);
39683            menu.un("beforeshow", onBeforeShow);
39684            menu.un("show", onShow);
39685            var g = menu.group;
39686            if(g && menu.events["checkchange"]){
39687                groups[g].remove(menu);
39688                menu.un("checkchange", onCheck);
39689            }
39690        },
39691
39692        // private
39693        registerCheckable : function(menuItem){
39694            var g = menuItem.group;
39695            if(g){
39696                if(!groups[g]){
39697                    groups[g] = [];
39698                }
39699                groups[g].push(menuItem);
39700                menuItem.on("beforecheckchange", onBeforeCheck);
39701            }
39702        },
39703
39704        // private
39705        unregisterCheckable : function(menuItem){
39706            var g = menuItem.group;
39707            if(g){
39708                groups[g].remove(menuItem);
39709                menuItem.un("beforecheckchange", onBeforeCheck);
39710            }
39711        }
39712    };
39713 }();/*
39714  * Based on:
39715  * Ext JS Library 1.1.1
39716  * Copyright(c) 2006-2007, Ext JS, LLC.
39717  *
39718  * Originally Released Under LGPL - original licence link has changed is not relivant.
39719  *
39720  * Fork - LGPL
39721  * <script type="text/javascript">
39722  */
39723  
39724
39725 /**
39726  * @class Roo.menu.BaseItem
39727  * @extends Roo.Component
39728  * @abstract
39729  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
39730  * management and base configuration options shared by all menu components.
39731  * @constructor
39732  * Creates a new BaseItem
39733  * @param {Object} config Configuration options
39734  */
39735 Roo.menu.BaseItem = function(config){
39736     Roo.menu.BaseItem.superclass.constructor.call(this, config);
39737
39738     this.addEvents({
39739         /**
39740          * @event click
39741          * Fires when this item is clicked
39742          * @param {Roo.menu.BaseItem} this
39743          * @param {Roo.EventObject} e
39744          */
39745         click: true,
39746         /**
39747          * @event activate
39748          * Fires when this item is activated
39749          * @param {Roo.menu.BaseItem} this
39750          */
39751         activate : true,
39752         /**
39753          * @event deactivate
39754          * Fires when this item is deactivated
39755          * @param {Roo.menu.BaseItem} this
39756          */
39757         deactivate : true
39758     });
39759
39760     if(this.handler){
39761         this.on("click", this.handler, this.scope, true);
39762     }
39763 };
39764
39765 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
39766     /**
39767      * @cfg {Function} handler
39768      * A function that will handle the click event of this menu item (defaults to undefined)
39769      */
39770     /**
39771      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
39772      */
39773     canActivate : false,
39774     
39775      /**
39776      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
39777      */
39778     hidden: false,
39779     
39780     /**
39781      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
39782      */
39783     activeClass : "x-menu-item-active",
39784     /**
39785      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
39786      */
39787     hideOnClick : true,
39788     /**
39789      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
39790      */
39791     hideDelay : 100,
39792
39793     // private
39794     ctype: "Roo.menu.BaseItem",
39795
39796     // private
39797     actionMode : "container",
39798
39799     // private
39800     render : function(container, parentMenu){
39801         this.parentMenu = parentMenu;
39802         Roo.menu.BaseItem.superclass.render.call(this, container);
39803         this.container.menuItemId = this.id;
39804     },
39805
39806     // private
39807     onRender : function(container, position){
39808         this.el = Roo.get(this.el);
39809         container.dom.appendChild(this.el.dom);
39810     },
39811
39812     // private
39813     onClick : function(e){
39814         if(!this.disabled && this.fireEvent("click", this, e) !== false
39815                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
39816             this.handleClick(e);
39817         }else{
39818             e.stopEvent();
39819         }
39820     },
39821
39822     // private
39823     activate : function(){
39824         if(this.disabled){
39825             return false;
39826         }
39827         var li = this.container;
39828         li.addClass(this.activeClass);
39829         this.region = li.getRegion().adjust(2, 2, -2, -2);
39830         this.fireEvent("activate", this);
39831         return true;
39832     },
39833
39834     // private
39835     deactivate : function(){
39836         this.container.removeClass(this.activeClass);
39837         this.fireEvent("deactivate", this);
39838     },
39839
39840     // private
39841     shouldDeactivate : function(e){
39842         return !this.region || !this.region.contains(e.getPoint());
39843     },
39844
39845     // private
39846     handleClick : function(e){
39847         if(this.hideOnClick){
39848             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
39849         }
39850     },
39851
39852     // private
39853     expandMenu : function(autoActivate){
39854         // do nothing
39855     },
39856
39857     // private
39858     hideMenu : function(){
39859         // do nothing
39860     }
39861 });/*
39862  * Based on:
39863  * Ext JS Library 1.1.1
39864  * Copyright(c) 2006-2007, Ext JS, LLC.
39865  *
39866  * Originally Released Under LGPL - original licence link has changed is not relivant.
39867  *
39868  * Fork - LGPL
39869  * <script type="text/javascript">
39870  */
39871  
39872 /**
39873  * @class Roo.menu.Adapter
39874  * @extends Roo.menu.BaseItem
39875  * @abstract
39876  * 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.
39877  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
39878  * @constructor
39879  * Creates a new Adapter
39880  * @param {Object} config Configuration options
39881  */
39882 Roo.menu.Adapter = function(component, config){
39883     Roo.menu.Adapter.superclass.constructor.call(this, config);
39884     this.component = component;
39885 };
39886 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
39887     // private
39888     canActivate : true,
39889
39890     // private
39891     onRender : function(container, position){
39892         this.component.render(container);
39893         this.el = this.component.getEl();
39894     },
39895
39896     // private
39897     activate : function(){
39898         if(this.disabled){
39899             return false;
39900         }
39901         this.component.focus();
39902         this.fireEvent("activate", this);
39903         return true;
39904     },
39905
39906     // private
39907     deactivate : function(){
39908         this.fireEvent("deactivate", this);
39909     },
39910
39911     // private
39912     disable : function(){
39913         this.component.disable();
39914         Roo.menu.Adapter.superclass.disable.call(this);
39915     },
39916
39917     // private
39918     enable : function(){
39919         this.component.enable();
39920         Roo.menu.Adapter.superclass.enable.call(this);
39921     }
39922 });/*
39923  * Based on:
39924  * Ext JS Library 1.1.1
39925  * Copyright(c) 2006-2007, Ext JS, LLC.
39926  *
39927  * Originally Released Under LGPL - original licence link has changed is not relivant.
39928  *
39929  * Fork - LGPL
39930  * <script type="text/javascript">
39931  */
39932
39933 /**
39934  * @class Roo.menu.TextItem
39935  * @extends Roo.menu.BaseItem
39936  * Adds a static text string to a menu, usually used as either a heading or group separator.
39937  * Note: old style constructor with text is still supported.
39938  * 
39939  * @constructor
39940  * Creates a new TextItem
39941  * @param {Object} cfg Configuration
39942  */
39943 Roo.menu.TextItem = function(cfg){
39944     if (typeof(cfg) == 'string') {
39945         this.text = cfg;
39946     } else {
39947         Roo.apply(this,cfg);
39948     }
39949     
39950     Roo.menu.TextItem.superclass.constructor.call(this);
39951 };
39952
39953 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
39954     /**
39955      * @cfg {String} text Text to show on item.
39956      */
39957     text : '',
39958     
39959     /**
39960      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39961      */
39962     hideOnClick : false,
39963     /**
39964      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
39965      */
39966     itemCls : "x-menu-text",
39967
39968     // private
39969     onRender : function(){
39970         var s = document.createElement("span");
39971         s.className = this.itemCls;
39972         s.innerHTML = this.text;
39973         this.el = s;
39974         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
39975     }
39976 });/*
39977  * Based on:
39978  * Ext JS Library 1.1.1
39979  * Copyright(c) 2006-2007, Ext JS, LLC.
39980  *
39981  * Originally Released Under LGPL - original licence link has changed is not relivant.
39982  *
39983  * Fork - LGPL
39984  * <script type="text/javascript">
39985  */
39986
39987 /**
39988  * @class Roo.menu.Separator
39989  * @extends Roo.menu.BaseItem
39990  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
39991  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
39992  * @constructor
39993  * @param {Object} config Configuration options
39994  */
39995 Roo.menu.Separator = function(config){
39996     Roo.menu.Separator.superclass.constructor.call(this, config);
39997 };
39998
39999 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
40000     /**
40001      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
40002      */
40003     itemCls : "x-menu-sep",
40004     /**
40005      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
40006      */
40007     hideOnClick : false,
40008
40009     // private
40010     onRender : function(li){
40011         var s = document.createElement("span");
40012         s.className = this.itemCls;
40013         s.innerHTML = "&#160;";
40014         this.el = s;
40015         li.addClass("x-menu-sep-li");
40016         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
40017     }
40018 });/*
40019  * Based on:
40020  * Ext JS Library 1.1.1
40021  * Copyright(c) 2006-2007, Ext JS, LLC.
40022  *
40023  * Originally Released Under LGPL - original licence link has changed is not relivant.
40024  *
40025  * Fork - LGPL
40026  * <script type="text/javascript">
40027  */
40028 /**
40029  * @class Roo.menu.Item
40030  * @extends Roo.menu.BaseItem
40031  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
40032  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
40033  * activation and click handling.
40034  * @constructor
40035  * Creates a new Item
40036  * @param {Object} config Configuration options
40037  */
40038 Roo.menu.Item = function(config){
40039     Roo.menu.Item.superclass.constructor.call(this, config);
40040     if(this.menu){
40041         this.menu = Roo.menu.MenuMgr.get(this.menu);
40042     }
40043 };
40044 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
40045     /**
40046      * @cfg {Roo.menu.Menu} menu
40047      * A Sub menu
40048      */
40049     /**
40050      * @cfg {String} text
40051      * The text to show on the menu item.
40052      */
40053     text: '',
40054      /**
40055      * @cfg {String} html to render in menu
40056      * The text to show on the menu item (HTML version).
40057      */
40058     html: '',
40059     /**
40060      * @cfg {String} icon
40061      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
40062      */
40063     icon: undefined,
40064     /**
40065      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
40066      */
40067     itemCls : "x-menu-item",
40068     /**
40069      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
40070      */
40071     canActivate : true,
40072     /**
40073      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
40074      */
40075     showDelay: 200,
40076     // doc'd in BaseItem
40077     hideDelay: 200,
40078
40079     // private
40080     ctype: "Roo.menu.Item",
40081     
40082     // private
40083     onRender : function(container, position){
40084         var el = document.createElement("a");
40085         el.hideFocus = true;
40086         el.unselectable = "on";
40087         el.href = this.href || "#";
40088         if(this.hrefTarget){
40089             el.target = this.hrefTarget;
40090         }
40091         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
40092         
40093         var html = this.html.length ? this.html  : String.format('{0}',this.text);
40094         
40095         el.innerHTML = String.format(
40096                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
40097                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
40098         this.el = el;
40099         Roo.menu.Item.superclass.onRender.call(this, container, position);
40100     },
40101
40102     /**
40103      * Sets the text to display in this menu item
40104      * @param {String} text The text to display
40105      * @param {Boolean} isHTML true to indicate text is pure html.
40106      */
40107     setText : function(text, isHTML){
40108         if (isHTML) {
40109             this.html = text;
40110         } else {
40111             this.text = text;
40112             this.html = '';
40113         }
40114         if(this.rendered){
40115             var html = this.html.length ? this.html  : String.format('{0}',this.text);
40116      
40117             this.el.update(String.format(
40118                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
40119                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
40120             this.parentMenu.autoWidth();
40121         }
40122     },
40123
40124     // private
40125     handleClick : function(e){
40126         if(!this.href){ // if no link defined, stop the event automatically
40127             e.stopEvent();
40128         }
40129         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
40130     },
40131
40132     // private
40133     activate : function(autoExpand){
40134         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
40135             this.focus();
40136             if(autoExpand){
40137                 this.expandMenu();
40138             }
40139         }
40140         return true;
40141     },
40142
40143     // private
40144     shouldDeactivate : function(e){
40145         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
40146             if(this.menu && this.menu.isVisible()){
40147                 return !this.menu.getEl().getRegion().contains(e.getPoint());
40148             }
40149             return true;
40150         }
40151         return false;
40152     },
40153
40154     // private
40155     deactivate : function(){
40156         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
40157         this.hideMenu();
40158     },
40159
40160     // private
40161     expandMenu : function(autoActivate){
40162         if(!this.disabled && this.menu){
40163             clearTimeout(this.hideTimer);
40164             delete this.hideTimer;
40165             if(!this.menu.isVisible() && !this.showTimer){
40166                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
40167             }else if (this.menu.isVisible() && autoActivate){
40168                 this.menu.tryActivate(0, 1);
40169             }
40170         }
40171     },
40172
40173     // private
40174     deferExpand : function(autoActivate){
40175         delete this.showTimer;
40176         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
40177         if(autoActivate){
40178             this.menu.tryActivate(0, 1);
40179         }
40180     },
40181
40182     // private
40183     hideMenu : function(){
40184         clearTimeout(this.showTimer);
40185         delete this.showTimer;
40186         if(!this.hideTimer && this.menu && this.menu.isVisible()){
40187             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
40188         }
40189     },
40190
40191     // private
40192     deferHide : function(){
40193         delete this.hideTimer;
40194         this.menu.hide();
40195     }
40196 });/*
40197  * Based on:
40198  * Ext JS Library 1.1.1
40199  * Copyright(c) 2006-2007, Ext JS, LLC.
40200  *
40201  * Originally Released Under LGPL - original licence link has changed is not relivant.
40202  *
40203  * Fork - LGPL
40204  * <script type="text/javascript">
40205  */
40206  
40207 /**
40208  * @class Roo.menu.CheckItem
40209  * @extends Roo.menu.Item
40210  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
40211  * @constructor
40212  * Creates a new CheckItem
40213  * @param {Object} config Configuration options
40214  */
40215 Roo.menu.CheckItem = function(config){
40216     Roo.menu.CheckItem.superclass.constructor.call(this, config);
40217     this.addEvents({
40218         /**
40219          * @event beforecheckchange
40220          * Fires before the checked value is set, providing an opportunity to cancel if needed
40221          * @param {Roo.menu.CheckItem} this
40222          * @param {Boolean} checked The new checked value that will be set
40223          */
40224         "beforecheckchange" : true,
40225         /**
40226          * @event checkchange
40227          * Fires after the checked value has been set
40228          * @param {Roo.menu.CheckItem} this
40229          * @param {Boolean} checked The checked value that was set
40230          */
40231         "checkchange" : true
40232     });
40233     if(this.checkHandler){
40234         this.on('checkchange', this.checkHandler, this.scope);
40235     }
40236 };
40237 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
40238     /**
40239      * @cfg {String} group
40240      * All check items with the same group name will automatically be grouped into a single-select
40241      * radio button group (defaults to '')
40242      */
40243     /**
40244      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
40245      */
40246     itemCls : "x-menu-item x-menu-check-item",
40247     /**
40248      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
40249      */
40250     groupClass : "x-menu-group-item",
40251
40252     /**
40253      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
40254      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
40255      * initialized with checked = true will be rendered as checked.
40256      */
40257     checked: false,
40258
40259     // private
40260     ctype: "Roo.menu.CheckItem",
40261
40262     // private
40263     onRender : function(c){
40264         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
40265         if(this.group){
40266             this.el.addClass(this.groupClass);
40267         }
40268         Roo.menu.MenuMgr.registerCheckable(this);
40269         if(this.checked){
40270             this.checked = false;
40271             this.setChecked(true, true);
40272         }
40273     },
40274
40275     // private
40276     destroy : function(){
40277         if(this.rendered){
40278             Roo.menu.MenuMgr.unregisterCheckable(this);
40279         }
40280         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
40281     },
40282
40283     /**
40284      * Set the checked state of this item
40285      * @param {Boolean} checked The new checked value
40286      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
40287      */
40288     setChecked : function(state, suppressEvent){
40289         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
40290             if(this.container){
40291                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
40292             }
40293             this.checked = state;
40294             if(suppressEvent !== true){
40295                 this.fireEvent("checkchange", this, state);
40296             }
40297         }
40298     },
40299
40300     // private
40301     handleClick : function(e){
40302        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
40303            this.setChecked(!this.checked);
40304        }
40305        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
40306     }
40307 });/*
40308  * Based on:
40309  * Ext JS Library 1.1.1
40310  * Copyright(c) 2006-2007, Ext JS, LLC.
40311  *
40312  * Originally Released Under LGPL - original licence link has changed is not relivant.
40313  *
40314  * Fork - LGPL
40315  * <script type="text/javascript">
40316  */
40317  
40318 /**
40319  * @class Roo.menu.DateItem
40320  * @extends Roo.menu.Adapter
40321  * A menu item that wraps the {@link Roo.DatPicker} component.
40322  * @constructor
40323  * Creates a new DateItem
40324  * @param {Object} config Configuration options
40325  */
40326 Roo.menu.DateItem = function(config){
40327     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
40328     /** The Roo.DatePicker object @type Roo.DatePicker */
40329     this.picker = this.component;
40330     this.addEvents({select: true});
40331     
40332     this.picker.on("render", function(picker){
40333         picker.getEl().swallowEvent("click");
40334         picker.container.addClass("x-menu-date-item");
40335     });
40336
40337     this.picker.on("select", this.onSelect, this);
40338 };
40339
40340 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
40341     // private
40342     onSelect : function(picker, date){
40343         this.fireEvent("select", this, date, picker);
40344         Roo.menu.DateItem.superclass.handleClick.call(this);
40345     }
40346 });/*
40347  * Based on:
40348  * Ext JS Library 1.1.1
40349  * Copyright(c) 2006-2007, Ext JS, LLC.
40350  *
40351  * Originally Released Under LGPL - original licence link has changed is not relivant.
40352  *
40353  * Fork - LGPL
40354  * <script type="text/javascript">
40355  */
40356  
40357 /**
40358  * @class Roo.menu.ColorItem
40359  * @extends Roo.menu.Adapter
40360  * A menu item that wraps the {@link Roo.ColorPalette} component.
40361  * @constructor
40362  * Creates a new ColorItem
40363  * @param {Object} config Configuration options
40364  */
40365 Roo.menu.ColorItem = function(config){
40366     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
40367     /** The Roo.ColorPalette object @type Roo.ColorPalette */
40368     this.palette = this.component;
40369     this.relayEvents(this.palette, ["select"]);
40370     if(this.selectHandler){
40371         this.on('select', this.selectHandler, this.scope);
40372     }
40373 };
40374 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
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 /**
40387  * @class Roo.menu.DateMenu
40388  * @extends Roo.menu.Menu
40389  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
40390  * @constructor
40391  * Creates a new DateMenu
40392  * @param {Object} config Configuration options
40393  */
40394 Roo.menu.DateMenu = function(config){
40395     Roo.menu.DateMenu.superclass.constructor.call(this, config);
40396     this.plain = true;
40397     var di = new Roo.menu.DateItem(config);
40398     this.add(di);
40399     /**
40400      * The {@link Roo.DatePicker} instance for this DateMenu
40401      * @type DatePicker
40402      */
40403     this.picker = di.picker;
40404     /**
40405      * @event select
40406      * @param {DatePicker} picker
40407      * @param {Date} date
40408      */
40409     this.relayEvents(di, ["select"]);
40410     this.on('beforeshow', function(){
40411         if(this.picker){
40412             this.picker.hideMonthPicker(false);
40413         }
40414     }, this);
40415 };
40416 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
40417     cls:'x-date-menu'
40418 });/*
40419  * Based on:
40420  * Ext JS Library 1.1.1
40421  * Copyright(c) 2006-2007, Ext JS, LLC.
40422  *
40423  * Originally Released Under LGPL - original licence link has changed is not relivant.
40424  *
40425  * Fork - LGPL
40426  * <script type="text/javascript">
40427  */
40428  
40429
40430 /**
40431  * @class Roo.menu.ColorMenu
40432  * @extends Roo.menu.Menu
40433  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
40434  * @constructor
40435  * Creates a new ColorMenu
40436  * @param {Object} config Configuration options
40437  */
40438 Roo.menu.ColorMenu = function(config){
40439     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
40440     this.plain = true;
40441     var ci = new Roo.menu.ColorItem(config);
40442     this.add(ci);
40443     /**
40444      * The {@link Roo.ColorPalette} instance for this ColorMenu
40445      * @type ColorPalette
40446      */
40447     this.palette = ci.palette;
40448     /**
40449      * @event select
40450      * @param {ColorPalette} palette
40451      * @param {String} color
40452      */
40453     this.relayEvents(ci, ["select"]);
40454 };
40455 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
40456  * Based on:
40457  * Ext JS Library 1.1.1
40458  * Copyright(c) 2006-2007, Ext JS, LLC.
40459  *
40460  * Originally Released Under LGPL - original licence link has changed is not relivant.
40461  *
40462  * Fork - LGPL
40463  * <script type="text/javascript">
40464  */
40465  
40466 /**
40467  * @class Roo.form.TextItem
40468  * @extends Roo.BoxComponent
40469  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
40470  * @constructor
40471  * Creates a new TextItem
40472  * @param {Object} config Configuration options
40473  */
40474 Roo.form.TextItem = function(config){
40475     Roo.form.TextItem.superclass.constructor.call(this, config);
40476 };
40477
40478 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
40479     
40480     /**
40481      * @cfg {String} tag the tag for this item (default div)
40482      */
40483     tag : 'div',
40484     /**
40485      * @cfg {String} html the content for this item
40486      */
40487     html : '',
40488     
40489     getAutoCreate : function()
40490     {
40491         var cfg = {
40492             id: this.id,
40493             tag: this.tag,
40494             html: this.html,
40495             cls: 'x-form-item'
40496         };
40497         
40498         return cfg;
40499         
40500     },
40501     
40502     onRender : function(ct, position)
40503     {
40504         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
40505         
40506         if(!this.el){
40507             var cfg = this.getAutoCreate();
40508             if(!cfg.name){
40509                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
40510             }
40511             if (!cfg.name.length) {
40512                 delete cfg.name;
40513             }
40514             this.el = ct.createChild(cfg, position);
40515         }
40516     },
40517     /*
40518      * setHTML
40519      * @param {String} html update the Contents of the element.
40520      */
40521     setHTML : function(html)
40522     {
40523         this.fieldEl.dom.innerHTML = html;
40524     }
40525     
40526 });/*
40527  * Based on:
40528  * Ext JS Library 1.1.1
40529  * Copyright(c) 2006-2007, Ext JS, LLC.
40530  *
40531  * Originally Released Under LGPL - original licence link has changed is not relivant.
40532  *
40533  * Fork - LGPL
40534  * <script type="text/javascript">
40535  */
40536  
40537 /**
40538  * @class Roo.form.Field
40539  * @extends Roo.BoxComponent
40540  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
40541  * @constructor
40542  * Creates a new Field
40543  * @param {Object} config Configuration options
40544  */
40545 Roo.form.Field = function(config){
40546     Roo.form.Field.superclass.constructor.call(this, config);
40547 };
40548
40549 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
40550     /**
40551      * @cfg {String} fieldLabel Label to use when rendering a form.
40552      */
40553         /**
40554      * @cfg {String} labelSeparator the ':' after a field label (default :)  = set it to empty string to hide the field label.
40555      */
40556        /**
40557      * @cfg {String} qtip Mouse over tip
40558      */
40559      
40560     /**
40561      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
40562      */
40563     invalidClass : "x-form-invalid",
40564     /**
40565      * @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")
40566      */
40567     invalidText : "The value in this field is invalid",
40568     /**
40569      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
40570      */
40571     focusClass : "x-form-focus",
40572     /**
40573      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
40574       automatic validation (defaults to "keyup").
40575      */
40576     validationEvent : "keyup",
40577     /**
40578      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
40579      */
40580     validateOnBlur : true,
40581     /**
40582      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
40583      */
40584     validationDelay : 250,
40585     /**
40586      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40587      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
40588      */
40589     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
40590     /**
40591      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
40592      */
40593     fieldClass : "x-form-field",
40594     /**
40595      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
40596      *<pre>
40597 Value         Description
40598 -----------   ----------------------------------------------------------------------
40599 qtip          Display a quick tip when the user hovers over the field
40600 title         Display a default browser title attribute popup
40601 under         Add a block div beneath the field containing the error text
40602 side          Add an error icon to the right of the field with a popup on hover
40603 [element id]  Add the error text directly to the innerHTML of the specified element
40604 </pre>
40605      */
40606     msgTarget : 'qtip',
40607     /**
40608      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
40609      */
40610     msgFx : 'normal',
40611
40612     /**
40613      * @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.
40614      */
40615     readOnly : false,
40616
40617     /**
40618      * @cfg {Boolean} disabled True to disable the field (defaults to false).
40619      */
40620     disabled : false,
40621
40622     /**
40623      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
40624      */
40625     inputType : undefined,
40626     
40627     /**
40628      * @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).
40629          */
40630         tabIndex : undefined,
40631         
40632     // private
40633     isFormField : true,
40634
40635     // private
40636     hasFocus : false,
40637     /**
40638      * @property {Roo.Element} fieldEl
40639      * Element Containing the rendered Field (with label etc.)
40640      */
40641     /**
40642      * @cfg {Mixed} value A value to initialize this field with.
40643      */
40644     value : undefined,
40645
40646     /**
40647      * @cfg {String} name The field's HTML name attribute.
40648      */
40649     /**
40650      * @cfg {String} cls A CSS class to apply to the field's underlying element.
40651      */
40652     // private
40653     loadedValue : false,
40654      
40655      
40656         // private ??
40657         initComponent : function(){
40658         Roo.form.Field.superclass.initComponent.call(this);
40659         this.addEvents({
40660             /**
40661              * @event focus
40662              * Fires when this field receives input focus.
40663              * @param {Roo.form.Field} this
40664              */
40665             focus : true,
40666             /**
40667              * @event blur
40668              * Fires when this field loses input focus.
40669              * @param {Roo.form.Field} this
40670              */
40671             blur : true,
40672             /**
40673              * @event specialkey
40674              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
40675              * {@link Roo.EventObject#getKey} to determine which key was pressed.
40676              * @param {Roo.form.Field} this
40677              * @param {Roo.EventObject} e The event object
40678              */
40679             specialkey : true,
40680             /**
40681              * @event change
40682              * Fires just before the field blurs if the field value has changed.
40683              * @param {Roo.form.Field} this
40684              * @param {Mixed} newValue The new value
40685              * @param {Mixed} oldValue The original value
40686              */
40687             change : true,
40688             /**
40689              * @event invalid
40690              * Fires after the field has been marked as invalid.
40691              * @param {Roo.form.Field} this
40692              * @param {String} msg The validation message
40693              */
40694             invalid : true,
40695             /**
40696              * @event valid
40697              * Fires after the field has been validated with no errors.
40698              * @param {Roo.form.Field} this
40699              */
40700             valid : true,
40701              /**
40702              * @event keyup
40703              * Fires after the key up
40704              * @param {Roo.form.Field} this
40705              * @param {Roo.EventObject}  e The event Object
40706              */
40707             keyup : true
40708         });
40709     },
40710
40711     /**
40712      * Returns the name attribute of the field if available
40713      * @return {String} name The field name
40714      */
40715     getName: function(){
40716          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
40717     },
40718
40719     // private
40720     onRender : function(ct, position){
40721         Roo.form.Field.superclass.onRender.call(this, ct, position);
40722         if(!this.el){
40723             var cfg = this.getAutoCreate();
40724             if(!cfg.name){
40725                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
40726             }
40727             if (!cfg.name.length) {
40728                 delete cfg.name;
40729             }
40730             if(this.inputType){
40731                 cfg.type = this.inputType;
40732             }
40733             this.el = ct.createChild(cfg, position);
40734         }
40735         var type = this.el.dom.type;
40736         if(type){
40737             if(type == 'password'){
40738                 type = 'text';
40739             }
40740             this.el.addClass('x-form-'+type);
40741         }
40742         if(this.readOnly){
40743             this.el.dom.readOnly = true;
40744         }
40745         if(this.tabIndex !== undefined){
40746             this.el.dom.setAttribute('tabIndex', this.tabIndex);
40747         }
40748
40749         this.el.addClass([this.fieldClass, this.cls]);
40750         this.initValue();
40751     },
40752
40753     /**
40754      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
40755      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
40756      * @return {Roo.form.Field} this
40757      */
40758     applyTo : function(target){
40759         this.allowDomMove = false;
40760         this.el = Roo.get(target);
40761         this.render(this.el.dom.parentNode);
40762         return this;
40763     },
40764
40765     // private
40766     initValue : function(){
40767         if(this.value !== undefined){
40768             this.setValue(this.value);
40769         }else if(this.el.dom.value.length > 0){
40770             this.setValue(this.el.dom.value);
40771         }
40772     },
40773
40774     /**
40775      * Returns true if this field has been changed since it was originally loaded and is not disabled.
40776      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
40777      */
40778     isDirty : function() {
40779         if(this.disabled) {
40780             return false;
40781         }
40782         return String(this.getValue()) !== String(this.originalValue);
40783     },
40784
40785     /**
40786      * stores the current value in loadedValue
40787      */
40788     resetHasChanged : function()
40789     {
40790         this.loadedValue = String(this.getValue());
40791     },
40792     /**
40793      * checks the current value against the 'loaded' value.
40794      * Note - will return false if 'resetHasChanged' has not been called first.
40795      */
40796     hasChanged : function()
40797     {
40798         if(this.disabled || this.readOnly) {
40799             return false;
40800         }
40801         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
40802     },
40803     
40804     
40805     
40806     // private
40807     afterRender : function(){
40808         Roo.form.Field.superclass.afterRender.call(this);
40809         this.initEvents();
40810     },
40811
40812     // private
40813     fireKey : function(e){
40814         //Roo.log('field ' + e.getKey());
40815         if(e.isNavKeyPress()){
40816             this.fireEvent("specialkey", this, e);
40817         }
40818     },
40819
40820     /**
40821      * Resets the current field value to the originally loaded value and clears any validation messages
40822      */
40823     reset : function(){
40824         this.setValue(this.resetValue);
40825         this.originalValue = this.getValue();
40826         this.clearInvalid();
40827     },
40828
40829     // private
40830     initEvents : function(){
40831         // safari killled keypress - so keydown is now used..
40832         this.el.on("keydown" , this.fireKey,  this);
40833         this.el.on("focus", this.onFocus,  this);
40834         this.el.on("blur", this.onBlur,  this);
40835         this.el.relayEvent('keyup', this);
40836
40837         // reference to original value for reset
40838         this.originalValue = this.getValue();
40839         this.resetValue =  this.getValue();
40840     },
40841
40842     // private
40843     onFocus : function(){
40844         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40845             this.el.addClass(this.focusClass);
40846         }
40847         if(!this.hasFocus){
40848             this.hasFocus = true;
40849             this.startValue = this.getValue();
40850             this.fireEvent("focus", this);
40851         }
40852     },
40853
40854     beforeBlur : Roo.emptyFn,
40855
40856     // private
40857     onBlur : function(){
40858         this.beforeBlur();
40859         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40860             this.el.removeClass(this.focusClass);
40861         }
40862         this.hasFocus = false;
40863         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40864             this.validate();
40865         }
40866         var v = this.getValue();
40867         if(String(v) !== String(this.startValue)){
40868             this.fireEvent('change', this, v, this.startValue);
40869         }
40870         this.fireEvent("blur", this);
40871     },
40872
40873     /**
40874      * Returns whether or not the field value is currently valid
40875      * @param {Boolean} preventMark True to disable marking the field invalid
40876      * @return {Boolean} True if the value is valid, else false
40877      */
40878     isValid : function(preventMark){
40879         if(this.disabled){
40880             return true;
40881         }
40882         var restore = this.preventMark;
40883         this.preventMark = preventMark === true;
40884         var v = this.validateValue(this.processValue(this.getRawValue()));
40885         this.preventMark = restore;
40886         return v;
40887     },
40888
40889     /**
40890      * Validates the field value
40891      * @return {Boolean} True if the value is valid, else false
40892      */
40893     validate : function(){
40894         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
40895             this.clearInvalid();
40896             return true;
40897         }
40898         return false;
40899     },
40900
40901     processValue : function(value){
40902         return value;
40903     },
40904
40905     // private
40906     // Subclasses should provide the validation implementation by overriding this
40907     validateValue : function(value){
40908         return true;
40909     },
40910
40911     /**
40912      * Mark this field as invalid
40913      * @param {String} msg The validation message
40914      */
40915     markInvalid : function(msg){
40916         if(!this.rendered || this.preventMark){ // not rendered
40917             return;
40918         }
40919         
40920         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40921         
40922         obj.el.addClass(this.invalidClass);
40923         msg = msg || this.invalidText;
40924         switch(this.msgTarget){
40925             case 'qtip':
40926                 obj.el.dom.qtip = msg;
40927                 obj.el.dom.qclass = 'x-form-invalid-tip';
40928                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
40929                     Roo.QuickTips.enable();
40930                 }
40931                 break;
40932             case 'title':
40933                 this.el.dom.title = msg;
40934                 break;
40935             case 'under':
40936                 if(!this.errorEl){
40937                     var elp = this.el.findParent('.x-form-element', 5, true);
40938                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
40939                     this.errorEl.setWidth(elp.getWidth(true)-20);
40940                 }
40941                 this.errorEl.update(msg);
40942                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
40943                 break;
40944             case 'side':
40945                 if(!this.errorIcon){
40946                     var elp = this.el.findParent('.x-form-element', 5, true);
40947                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
40948                 }
40949                 this.alignErrorIcon();
40950                 this.errorIcon.dom.qtip = msg;
40951                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
40952                 this.errorIcon.show();
40953                 this.on('resize', this.alignErrorIcon, this);
40954                 break;
40955             default:
40956                 var t = Roo.getDom(this.msgTarget);
40957                 t.innerHTML = msg;
40958                 t.style.display = this.msgDisplay;
40959                 break;
40960         }
40961         this.fireEvent('invalid', this, msg);
40962     },
40963
40964     // private
40965     alignErrorIcon : function(){
40966         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
40967     },
40968
40969     /**
40970      * Clear any invalid styles/messages for this field
40971      */
40972     clearInvalid : function(){
40973         if(!this.rendered || this.preventMark){ // not rendered
40974             return;
40975         }
40976         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40977         
40978         obj.el.removeClass(this.invalidClass);
40979         switch(this.msgTarget){
40980             case 'qtip':
40981                 obj.el.dom.qtip = '';
40982                 break;
40983             case 'title':
40984                 this.el.dom.title = '';
40985                 break;
40986             case 'under':
40987                 if(this.errorEl){
40988                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
40989                 }
40990                 break;
40991             case 'side':
40992                 if(this.errorIcon){
40993                     this.errorIcon.dom.qtip = '';
40994                     this.errorIcon.hide();
40995                     this.un('resize', this.alignErrorIcon, this);
40996                 }
40997                 break;
40998             default:
40999                 var t = Roo.getDom(this.msgTarget);
41000                 t.innerHTML = '';
41001                 t.style.display = 'none';
41002                 break;
41003         }
41004         this.fireEvent('valid', this);
41005     },
41006
41007     /**
41008      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
41009      * @return {Mixed} value The field value
41010      */
41011     getRawValue : function(){
41012         var v = this.el.getValue();
41013         
41014         return v;
41015     },
41016
41017     /**
41018      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
41019      * @return {Mixed} value The field value
41020      */
41021     getValue : function(){
41022         var v = this.el.getValue();
41023          
41024         return v;
41025     },
41026
41027     /**
41028      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
41029      * @param {Mixed} value The value to set
41030      */
41031     setRawValue : function(v){
41032         return this.el.dom.value = (v === null || v === undefined ? '' : v);
41033     },
41034
41035     /**
41036      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
41037      * @param {Mixed} value The value to set
41038      */
41039     setValue : function(v){
41040         this.value = v;
41041         if(this.rendered){
41042             this.el.dom.value = (v === null || v === undefined ? '' : v);
41043              this.validate();
41044         }
41045     },
41046
41047     adjustSize : function(w, h){
41048         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
41049         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
41050         return s;
41051     },
41052
41053     adjustWidth : function(tag, w){
41054         tag = tag.toLowerCase();
41055         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
41056             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
41057                 if(tag == 'input'){
41058                     return w + 2;
41059                 }
41060                 if(tag == 'textarea'){
41061                     return w-2;
41062                 }
41063             }else if(Roo.isOpera){
41064                 if(tag == 'input'){
41065                     return w + 2;
41066                 }
41067                 if(tag == 'textarea'){
41068                     return w-2;
41069                 }
41070             }
41071         }
41072         return w;
41073     }
41074 });
41075
41076
41077 // anything other than normal should be considered experimental
41078 Roo.form.Field.msgFx = {
41079     normal : {
41080         show: function(msgEl, f){
41081             msgEl.setDisplayed('block');
41082         },
41083
41084         hide : function(msgEl, f){
41085             msgEl.setDisplayed(false).update('');
41086         }
41087     },
41088
41089     slide : {
41090         show: function(msgEl, f){
41091             msgEl.slideIn('t', {stopFx:true});
41092         },
41093
41094         hide : function(msgEl, f){
41095             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
41096         }
41097     },
41098
41099     slideRight : {
41100         show: function(msgEl, f){
41101             msgEl.fixDisplay();
41102             msgEl.alignTo(f.el, 'tl-tr');
41103             msgEl.slideIn('l', {stopFx:true});
41104         },
41105
41106         hide : function(msgEl, f){
41107             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
41108         }
41109     }
41110 };/*
41111  * Based on:
41112  * Ext JS Library 1.1.1
41113  * Copyright(c) 2006-2007, Ext JS, LLC.
41114  *
41115  * Originally Released Under LGPL - original licence link has changed is not relivant.
41116  *
41117  * Fork - LGPL
41118  * <script type="text/javascript">
41119  */
41120  
41121
41122 /**
41123  * @class Roo.form.TextField
41124  * @extends Roo.form.Field
41125  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
41126  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
41127  * @constructor
41128  * Creates a new TextField
41129  * @param {Object} config Configuration options
41130  */
41131 Roo.form.TextField = function(config){
41132     Roo.form.TextField.superclass.constructor.call(this, config);
41133     this.addEvents({
41134         /**
41135          * @event autosize
41136          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
41137          * according to the default logic, but this event provides a hook for the developer to apply additional
41138          * logic at runtime to resize the field if needed.
41139              * @param {Roo.form.Field} this This text field
41140              * @param {Number} width The new field width
41141              */
41142         autosize : true
41143     });
41144 };
41145
41146 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
41147     /**
41148      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
41149      */
41150     grow : false,
41151     /**
41152      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
41153      */
41154     growMin : 30,
41155     /**
41156      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
41157      */
41158     growMax : 800,
41159     /**
41160      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
41161      */
41162     vtype : null,
41163     /**
41164      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
41165      */
41166     maskRe : null,
41167     /**
41168      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
41169      */
41170     disableKeyFilter : false,
41171     /**
41172      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
41173      */
41174     allowBlank : true,
41175     /**
41176      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
41177      */
41178     minLength : 0,
41179     /**
41180      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
41181      */
41182     maxLength : Number.MAX_VALUE,
41183     /**
41184      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
41185      */
41186     minLengthText : "The minimum length for this field is {0}",
41187     /**
41188      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
41189      */
41190     maxLengthText : "The maximum length for this field is {0}",
41191     /**
41192      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
41193      */
41194     selectOnFocus : false,
41195     /**
41196      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
41197      */    
41198     allowLeadingSpace : false,
41199     /**
41200      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
41201      */
41202     blankText : "This field is required",
41203     /**
41204      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
41205      * If available, this function will be called only after the basic validators all return true, and will be passed the
41206      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
41207      */
41208     validator : null,
41209     /**
41210      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
41211      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
41212      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
41213      */
41214     regex : null,
41215     /**
41216      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
41217      */
41218     regexText : "",
41219     /**
41220      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
41221      */
41222     emptyText : null,
41223    
41224
41225     // private
41226     initEvents : function()
41227     {
41228         if (this.emptyText) {
41229             this.el.attr('placeholder', this.emptyText);
41230         }
41231         
41232         Roo.form.TextField.superclass.initEvents.call(this);
41233         if(this.validationEvent == 'keyup'){
41234             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41235             this.el.on('keyup', this.filterValidation, this);
41236         }
41237         else if(this.validationEvent !== false){
41238             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41239         }
41240         
41241         if(this.selectOnFocus){
41242             this.on("focus", this.preFocus, this);
41243         }
41244         if (!this.allowLeadingSpace) {
41245             this.on('blur', this.cleanLeadingSpace, this);
41246         }
41247         
41248         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41249             this.el.on("keypress", this.filterKeys, this);
41250         }
41251         if(this.grow){
41252             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
41253             this.el.on("click", this.autoSize,  this);
41254         }
41255         if(this.el.is('input[type=password]') && Roo.isSafari){
41256             this.el.on('keydown', this.SafariOnKeyDown, this);
41257         }
41258     },
41259
41260     processValue : function(value){
41261         if(this.stripCharsRe){
41262             var newValue = value.replace(this.stripCharsRe, '');
41263             if(newValue !== value){
41264                 this.setRawValue(newValue);
41265                 return newValue;
41266             }
41267         }
41268         return value;
41269     },
41270
41271     filterValidation : function(e){
41272         if(!e.isNavKeyPress()){
41273             this.validationTask.delay(this.validationDelay);
41274         }
41275     },
41276
41277     // private
41278     onKeyUp : function(e){
41279         if(!e.isNavKeyPress()){
41280             this.autoSize();
41281         }
41282     },
41283     // private - clean the leading white space
41284     cleanLeadingSpace : function(e)
41285     {
41286         if ( this.inputType == 'file') {
41287             return;
41288         }
41289         
41290         this.setValue((this.getValue() + '').replace(/^\s+/,''));
41291     },
41292     /**
41293      * Resets the current field value to the originally-loaded value and clears any validation messages.
41294      *  
41295      */
41296     reset : function(){
41297         Roo.form.TextField.superclass.reset.call(this);
41298        
41299     }, 
41300     // private
41301     preFocus : function(){
41302         
41303         if(this.selectOnFocus){
41304             this.el.dom.select();
41305         }
41306     },
41307
41308     
41309     // private
41310     filterKeys : function(e){
41311         var k = e.getKey();
41312         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
41313             return;
41314         }
41315         var c = e.getCharCode(), cc = String.fromCharCode(c);
41316         if(Roo.isIE && (e.isSpecialKey() || !cc)){
41317             return;
41318         }
41319         if(!this.maskRe.test(cc)){
41320             e.stopEvent();
41321         }
41322     },
41323
41324     setValue : function(v){
41325         
41326         Roo.form.TextField.superclass.setValue.apply(this, arguments);
41327         
41328         this.autoSize();
41329     },
41330
41331     /**
41332      * Validates a value according to the field's validation rules and marks the field as invalid
41333      * if the validation fails
41334      * @param {Mixed} value The value to validate
41335      * @return {Boolean} True if the value is valid, else false
41336      */
41337     validateValue : function(value){
41338         if(value.length < 1)  { // if it's blank
41339              if(this.allowBlank){
41340                 this.clearInvalid();
41341                 return true;
41342              }else{
41343                 this.markInvalid(this.blankText);
41344                 return false;
41345              }
41346         }
41347         if(value.length < this.minLength){
41348             this.markInvalid(String.format(this.minLengthText, this.minLength));
41349             return false;
41350         }
41351         if(value.length > this.maxLength){
41352             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
41353             return false;
41354         }
41355         if(this.vtype){
41356             var vt = Roo.form.VTypes;
41357                         if (value.trim() != value) { // trim before checking email (and other stuf??)
41358                                 value = value.trim();
41359                                 this.el.dom.value  = value;
41360                         }
41361                         
41362             if(!vt[this.vtype](value, this)){
41363                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
41364                 return false;
41365             }
41366         }
41367         if(typeof this.validator == "function"){
41368             var msg = this.validator(value);
41369             if(msg !== true){
41370                 this.markInvalid(msg);
41371                 return false;
41372             }
41373         }
41374         if(this.regex && !this.regex.test(value)){
41375             this.markInvalid(this.regexText);
41376             return false;
41377         }
41378         return true;
41379     },
41380
41381     /**
41382      * Selects text in this field
41383      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
41384      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
41385      */
41386     selectText : function(start, end){
41387         var v = this.getRawValue();
41388         if(v.length > 0){
41389             start = start === undefined ? 0 : start;
41390             end = end === undefined ? v.length : end;
41391             var d = this.el.dom;
41392             if(d.setSelectionRange){
41393                 d.setSelectionRange(start, end);
41394             }else if(d.createTextRange){
41395                 var range = d.createTextRange();
41396                 range.moveStart("character", start);
41397                 range.moveEnd("character", v.length-end);
41398                 range.select();
41399             }
41400         }
41401     },
41402
41403     /**
41404      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
41405      * This only takes effect if grow = true, and fires the autosize event.
41406      */
41407     autoSize : function(){
41408         if(!this.grow || !this.rendered){
41409             return;
41410         }
41411         if(!this.metrics){
41412             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
41413         }
41414         var el = this.el;
41415         var v = el.dom.value;
41416         var d = document.createElement('div');
41417         d.appendChild(document.createTextNode(v));
41418         v = d.innerHTML;
41419         d = null;
41420         v += "&#160;";
41421         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
41422         this.el.setWidth(w);
41423         this.fireEvent("autosize", this, w);
41424     },
41425     
41426     // private
41427     SafariOnKeyDown : function(event)
41428     {
41429         // this is a workaround for a password hang bug on chrome/ webkit.
41430         
41431         var isSelectAll = false;
41432         
41433         if(this.el.dom.selectionEnd > 0){
41434             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
41435         }
41436         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
41437             event.preventDefault();
41438             this.setValue('');
41439             return;
41440         }
41441         
41442         // skip handling paste
41443         if(isSelectAll && event.getCharCode() > 31 && !(event.ctrlKey && event.getCharCode() == 86)){ // backspace and delete key
41444             
41445             event.preventDefault();
41446             // this is very hacky as keydown always get's upper case.
41447             
41448             var cc = String.fromCharCode(event.getCharCode());
41449             
41450             
41451             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
41452             
41453         }
41454         
41455         
41456     }
41457 });Roo.form.Password = function(config){
41458     Roo.form.Password.superclass.constructor.call(this, config);
41459
41460     this.inputType = 'password';
41461 };
41462
41463 Roo.extend(Roo.form.Password, Roo.form.TextField,  {
41464     onRender : function(ct, position)
41465     {
41466         Roo.form.Password.superclass.onRender.call(this, ct, position);
41467
41468         this.parentEl().addClass('form-password');
41469
41470         this.wrap = this.el.wrap({
41471             cls : 'password-wrap'
41472         });
41473
41474         this.toggle = this.wrap.createChild({
41475             tag : 'Button',
41476             cls : 'password-toggle'
41477         });
41478
41479
41480         this.toggleEl().addClass('password-hidden');
41481
41482         this.toggleEl().on('click', this.onToggleClick, this);;
41483     },
41484     
41485     parentEl : function()
41486     {
41487         return this.el.findParent('.x-form-element', 5, true);
41488     },
41489
41490     toggleEl: function()
41491     {
41492         return this.parentEl().select('button.password-toggle',true).first();
41493     },
41494
41495     onToggleClick : function(e) 
41496     {
41497         var input = this.el;
41498         var toggle = this.toggleEl();
41499
41500         toggle.removeClass(['password-visible', 'password-hidden']);
41501
41502         if(input.attr('type') == 'password') {
41503             input.attr('type', 'text');
41504             toggle.addClass('password-visible');
41505         }
41506         else {
41507             input.attr('type', 'password');
41508             toggle.addClass('password-hidden');
41509         }
41510     }
41511 });/*
41512  * Based on:
41513  * Ext JS Library 1.1.1
41514  * Copyright(c) 2006-2007, Ext JS, LLC.
41515  *
41516  * Originally Released Under LGPL - original licence link has changed is not relivant.
41517  *
41518  * Fork - LGPL
41519  * <script type="text/javascript">
41520  */
41521  
41522 /**
41523  * @class Roo.form.Hidden
41524  * @extends Roo.form.TextField
41525  * Simple Hidden element used on forms 
41526  * 
41527  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
41528  * 
41529  * @constructor
41530  * Creates a new Hidden form element.
41531  * @param {Object} config Configuration options
41532  */
41533
41534
41535
41536 // easy hidden field...
41537 Roo.form.Hidden = function(config){
41538     Roo.form.Hidden.superclass.constructor.call(this, config);
41539 };
41540   
41541 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
41542     fieldLabel:      '',
41543     inputType:      'hidden',
41544     width:          50,
41545     allowBlank:     true,
41546     labelSeparator: '',
41547     hidden:         true,
41548     itemCls :       'x-form-item-display-none'
41549
41550
41551 });
41552
41553
41554 /*
41555  * Based on:
41556  * Ext JS Library 1.1.1
41557  * Copyright(c) 2006-2007, Ext JS, LLC.
41558  *
41559  * Originally Released Under LGPL - original licence link has changed is not relivant.
41560  *
41561  * Fork - LGPL
41562  * <script type="text/javascript">
41563  */
41564  
41565 /**
41566  * @class Roo.form.TriggerField
41567  * @extends Roo.form.TextField
41568  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
41569  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
41570  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
41571  * for which you can provide a custom implementation.  For example:
41572  * <pre><code>
41573 var trigger = new Roo.form.TriggerField();
41574 trigger.onTriggerClick = myTriggerFn;
41575 trigger.applyTo('my-field');
41576 </code></pre>
41577  *
41578  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
41579  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
41580  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
41581  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
41582  * @constructor
41583  * Create a new TriggerField.
41584  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
41585  * to the base TextField)
41586  */
41587 Roo.form.TriggerField = function(config){
41588     this.mimicing = false;
41589     Roo.form.TriggerField.superclass.constructor.call(this, config);
41590 };
41591
41592 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
41593     /**
41594      * @cfg {String} triggerClass A CSS class to apply to the trigger
41595      */
41596     /**
41597      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41598      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
41599      */
41600     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
41601     /**
41602      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
41603      */
41604     hideTrigger:false,
41605
41606     /** @cfg {Boolean} grow @hide */
41607     /** @cfg {Number} growMin @hide */
41608     /** @cfg {Number} growMax @hide */
41609
41610     /**
41611      * @hide 
41612      * @method
41613      */
41614     autoSize: Roo.emptyFn,
41615     // private
41616     monitorTab : true,
41617     // private
41618     deferHeight : true,
41619
41620     
41621     actionMode : 'wrap',
41622     // private
41623     onResize : function(w, h){
41624         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
41625         if(typeof w == 'number'){
41626             var x = w - this.trigger.getWidth();
41627             this.el.setWidth(this.adjustWidth('input', x));
41628             this.trigger.setStyle('left', x+'px');
41629         }
41630     },
41631
41632     // private
41633     adjustSize : Roo.BoxComponent.prototype.adjustSize,
41634
41635     // private
41636     getResizeEl : function(){
41637         return this.wrap;
41638     },
41639
41640     // private
41641     getPositionEl : function(){
41642         return this.wrap;
41643     },
41644
41645     // private
41646     alignErrorIcon : function(){
41647         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
41648     },
41649
41650     // private
41651     onRender : function(ct, position){
41652         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
41653         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
41654         this.trigger = this.wrap.createChild(this.triggerConfig ||
41655                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
41656         if(this.hideTrigger){
41657             this.trigger.setDisplayed(false);
41658         }
41659         this.initTrigger();
41660         if(!this.width){
41661             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
41662         }
41663     },
41664
41665     // private
41666     initTrigger : function(){
41667         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
41668         this.trigger.addClassOnOver('x-form-trigger-over');
41669         this.trigger.addClassOnClick('x-form-trigger-click');
41670     },
41671
41672     // private
41673     onDestroy : function(){
41674         if(this.trigger){
41675             this.trigger.removeAllListeners();
41676             this.trigger.remove();
41677         }
41678         if(this.wrap){
41679             this.wrap.remove();
41680         }
41681         Roo.form.TriggerField.superclass.onDestroy.call(this);
41682     },
41683
41684     // private
41685     onFocus : function(){
41686         Roo.form.TriggerField.superclass.onFocus.call(this);
41687         if(!this.mimicing){
41688             this.wrap.addClass('x-trigger-wrap-focus');
41689             this.mimicing = true;
41690             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
41691             if(this.monitorTab){
41692                 this.el.on("keydown", this.checkTab, this);
41693             }
41694         }
41695     },
41696
41697     // private
41698     checkTab : function(e){
41699         if(e.getKey() == e.TAB){
41700             this.triggerBlur();
41701         }
41702     },
41703
41704     // private
41705     onBlur : function(){
41706         // do nothing
41707     },
41708
41709     // private
41710     mimicBlur : function(e, t){
41711         if(!this.wrap.contains(t) && this.validateBlur()){
41712             this.triggerBlur();
41713         }
41714     },
41715
41716     // private
41717     triggerBlur : function(){
41718         this.mimicing = false;
41719         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
41720         if(this.monitorTab){
41721             this.el.un("keydown", this.checkTab, this);
41722         }
41723         this.wrap.removeClass('x-trigger-wrap-focus');
41724         Roo.form.TriggerField.superclass.onBlur.call(this);
41725     },
41726
41727     // private
41728     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
41729     validateBlur : function(e, t){
41730         return true;
41731     },
41732
41733     // private
41734     onDisable : function(){
41735         Roo.form.TriggerField.superclass.onDisable.call(this);
41736         if(this.wrap){
41737             this.wrap.addClass('x-item-disabled');
41738         }
41739     },
41740
41741     // private
41742     onEnable : function(){
41743         Roo.form.TriggerField.superclass.onEnable.call(this);
41744         if(this.wrap){
41745             this.wrap.removeClass('x-item-disabled');
41746         }
41747     },
41748
41749     // private
41750     onShow : function(){
41751         var ae = this.getActionEl();
41752         
41753         if(ae){
41754             ae.dom.style.display = '';
41755             ae.dom.style.visibility = 'visible';
41756         }
41757     },
41758
41759     // private
41760     
41761     onHide : function(){
41762         var ae = this.getActionEl();
41763         ae.dom.style.display = 'none';
41764     },
41765
41766     /**
41767      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
41768      * by an implementing function.
41769      * @method
41770      * @param {EventObject} e
41771      */
41772     onTriggerClick : Roo.emptyFn
41773 });
41774
41775 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
41776 // to be extended by an implementing class.  For an example of implementing this class, see the custom
41777 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
41778 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
41779     initComponent : function(){
41780         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
41781
41782         this.triggerConfig = {
41783             tag:'span', cls:'x-form-twin-triggers', cn:[
41784             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
41785             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
41786         ]};
41787     },
41788
41789     getTrigger : function(index){
41790         return this.triggers[index];
41791     },
41792
41793     initTrigger : function(){
41794         var ts = this.trigger.select('.x-form-trigger', true);
41795         this.wrap.setStyle('overflow', 'hidden');
41796         var triggerField = this;
41797         ts.each(function(t, all, index){
41798             t.hide = function(){
41799                 var w = triggerField.wrap.getWidth();
41800                 this.dom.style.display = 'none';
41801                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41802             };
41803             t.show = function(){
41804                 var w = triggerField.wrap.getWidth();
41805                 this.dom.style.display = '';
41806                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41807             };
41808             var triggerIndex = 'Trigger'+(index+1);
41809
41810             if(this['hide'+triggerIndex]){
41811                 t.dom.style.display = 'none';
41812             }
41813             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
41814             t.addClassOnOver('x-form-trigger-over');
41815             t.addClassOnClick('x-form-trigger-click');
41816         }, this);
41817         this.triggers = ts.elements;
41818     },
41819
41820     onTrigger1Click : Roo.emptyFn,
41821     onTrigger2Click : Roo.emptyFn
41822 });/*
41823  * Based on:
41824  * Ext JS Library 1.1.1
41825  * Copyright(c) 2006-2007, Ext JS, LLC.
41826  *
41827  * Originally Released Under LGPL - original licence link has changed is not relivant.
41828  *
41829  * Fork - LGPL
41830  * <script type="text/javascript">
41831  */
41832  
41833 /**
41834  * @class Roo.form.TextArea
41835  * @extends Roo.form.TextField
41836  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
41837  * support for auto-sizing.
41838  * @constructor
41839  * Creates a new TextArea
41840  * @param {Object} config Configuration options
41841  */
41842 Roo.form.TextArea = function(config){
41843     Roo.form.TextArea.superclass.constructor.call(this, config);
41844     // these are provided exchanges for backwards compat
41845     // minHeight/maxHeight were replaced by growMin/growMax to be
41846     // compatible with TextField growing config values
41847     if(this.minHeight !== undefined){
41848         this.growMin = this.minHeight;
41849     }
41850     if(this.maxHeight !== undefined){
41851         this.growMax = this.maxHeight;
41852     }
41853 };
41854
41855 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
41856     /**
41857      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
41858      */
41859     growMin : 60,
41860     /**
41861      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
41862      */
41863     growMax: 1000,
41864     /**
41865      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
41866      * in the field (equivalent to setting overflow: hidden, defaults to false)
41867      */
41868     preventScrollbars: false,
41869     /**
41870      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41871      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
41872      */
41873
41874     // private
41875     onRender : function(ct, position){
41876         if(!this.el){
41877             this.defaultAutoCreate = {
41878                 tag: "textarea",
41879                 style:"width:300px;height:60px;",
41880                 autocomplete: "new-password"
41881             };
41882         }
41883         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
41884         if(this.grow){
41885             this.textSizeEl = Roo.DomHelper.append(document.body, {
41886                 tag: "pre", cls: "x-form-grow-sizer"
41887             });
41888             if(this.preventScrollbars){
41889                 this.el.setStyle("overflow", "hidden");
41890             }
41891             this.el.setHeight(this.growMin);
41892         }
41893     },
41894
41895     onDestroy : function(){
41896         if(this.textSizeEl){
41897             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
41898         }
41899         Roo.form.TextArea.superclass.onDestroy.call(this);
41900     },
41901
41902     // private
41903     onKeyUp : function(e){
41904         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
41905             this.autoSize();
41906         }
41907     },
41908
41909     /**
41910      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
41911      * This only takes effect if grow = true, and fires the autosize event if the height changes.
41912      */
41913     autoSize : function(){
41914         if(!this.grow || !this.textSizeEl){
41915             return;
41916         }
41917         var el = this.el;
41918         var v = el.dom.value;
41919         var ts = this.textSizeEl;
41920
41921         ts.innerHTML = '';
41922         ts.appendChild(document.createTextNode(v));
41923         v = ts.innerHTML;
41924
41925         Roo.fly(ts).setWidth(this.el.getWidth());
41926         if(v.length < 1){
41927             v = "&#160;&#160;";
41928         }else{
41929             if(Roo.isIE){
41930                 v = v.replace(/\n/g, '<p>&#160;</p>');
41931             }
41932             v += "&#160;\n&#160;";
41933         }
41934         ts.innerHTML = v;
41935         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
41936         if(h != this.lastHeight){
41937             this.lastHeight = h;
41938             this.el.setHeight(h);
41939             this.fireEvent("autosize", this, h);
41940         }
41941     }
41942 });/*
41943  * Based on:
41944  * Ext JS Library 1.1.1
41945  * Copyright(c) 2006-2007, Ext JS, LLC.
41946  *
41947  * Originally Released Under LGPL - original licence link has changed is not relivant.
41948  *
41949  * Fork - LGPL
41950  * <script type="text/javascript">
41951  */
41952  
41953
41954 /**
41955  * @class Roo.form.NumberField
41956  * @extends Roo.form.TextField
41957  * Numeric text field that provides automatic keystroke filtering and numeric validation.
41958  * @constructor
41959  * Creates a new NumberField
41960  * @param {Object} config Configuration options
41961  */
41962 Roo.form.NumberField = function(config){
41963     Roo.form.NumberField.superclass.constructor.call(this, config);
41964 };
41965
41966 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
41967     /**
41968      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
41969      */
41970     fieldClass: "x-form-field x-form-num-field",
41971     /**
41972      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41973      */
41974     allowDecimals : true,
41975     /**
41976      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41977      */
41978     decimalSeparator : ".",
41979     /**
41980      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41981      */
41982     decimalPrecision : 2,
41983     /**
41984      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41985      */
41986     allowNegative : true,
41987     /**
41988      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41989      */
41990     minValue : Number.NEGATIVE_INFINITY,
41991     /**
41992      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41993      */
41994     maxValue : Number.MAX_VALUE,
41995     /**
41996      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41997      */
41998     minText : "The minimum value for this field is {0}",
41999     /**
42000      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
42001      */
42002     maxText : "The maximum value for this field is {0}",
42003     /**
42004      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
42005      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
42006      */
42007     nanText : "{0} is not a valid number",
42008
42009     // private
42010     initEvents : function(){
42011         Roo.form.NumberField.superclass.initEvents.call(this);
42012         var allowed = "0123456789";
42013         if(this.allowDecimals){
42014             allowed += this.decimalSeparator;
42015         }
42016         if(this.allowNegative){
42017             allowed += "-";
42018         }
42019         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
42020         var keyPress = function(e){
42021             var k = e.getKey();
42022             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
42023                 return;
42024             }
42025             var c = e.getCharCode();
42026             if(allowed.indexOf(String.fromCharCode(c)) === -1){
42027                 e.stopEvent();
42028             }
42029         };
42030         this.el.on("keypress", keyPress, this);
42031     },
42032
42033     // private
42034     validateValue : function(value){
42035         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
42036             return false;
42037         }
42038         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
42039              return true;
42040         }
42041         var num = this.parseValue(value);
42042         if(isNaN(num)){
42043             this.markInvalid(String.format(this.nanText, value));
42044             return false;
42045         }
42046         if(num < this.minValue){
42047             this.markInvalid(String.format(this.minText, this.minValue));
42048             return false;
42049         }
42050         if(num > this.maxValue){
42051             this.markInvalid(String.format(this.maxText, this.maxValue));
42052             return false;
42053         }
42054         return true;
42055     },
42056
42057     getValue : function(){
42058         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
42059     },
42060
42061     // private
42062     parseValue : function(value){
42063         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
42064         return isNaN(value) ? '' : value;
42065     },
42066
42067     // private
42068     fixPrecision : function(value){
42069         var nan = isNaN(value);
42070         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
42071             return nan ? '' : value;
42072         }
42073         return parseFloat(value).toFixed(this.decimalPrecision);
42074     },
42075
42076     setValue : function(v){
42077         v = this.fixPrecision(v);
42078         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
42079     },
42080
42081     // private
42082     decimalPrecisionFcn : function(v){
42083         return Math.floor(v);
42084     },
42085
42086     beforeBlur : function(){
42087         var v = this.parseValue(this.getRawValue());
42088         if(v){
42089             this.setValue(v);
42090         }
42091     }
42092 });/*
42093  * Based on:
42094  * Ext JS Library 1.1.1
42095  * Copyright(c) 2006-2007, Ext JS, LLC.
42096  *
42097  * Originally Released Under LGPL - original licence link has changed is not relivant.
42098  *
42099  * Fork - LGPL
42100  * <script type="text/javascript">
42101  */
42102  
42103 /**
42104  * @class Roo.form.DateField
42105  * @extends Roo.form.TriggerField
42106  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
42107 * @constructor
42108 * Create a new DateField
42109 * @param {Object} config
42110  */
42111 Roo.form.DateField = function(config)
42112 {
42113     Roo.form.DateField.superclass.constructor.call(this, config);
42114     
42115       this.addEvents({
42116          
42117         /**
42118          * @event select
42119          * Fires when a date is selected
42120              * @param {Roo.form.DateField} combo This combo box
42121              * @param {Date} date The date selected
42122              */
42123         'select' : true
42124          
42125     });
42126     
42127     
42128     if(typeof this.minValue == "string") {
42129         this.minValue = this.parseDate(this.minValue);
42130     }
42131     if(typeof this.maxValue == "string") {
42132         this.maxValue = this.parseDate(this.maxValue);
42133     }
42134     this.ddMatch = null;
42135     if(this.disabledDates){
42136         var dd = this.disabledDates;
42137         var re = "(?:";
42138         for(var i = 0; i < dd.length; i++){
42139             re += dd[i];
42140             if(i != dd.length-1) {
42141                 re += "|";
42142             }
42143         }
42144         this.ddMatch = new RegExp(re + ")");
42145     }
42146 };
42147
42148 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
42149     /**
42150      * @cfg {String} format
42151      * The default date format string which can be overriden for localization support.  The format must be
42152      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
42153      */
42154     format : "m/d/y",
42155     /**
42156      * @cfg {String} altFormats
42157      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
42158      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
42159      */
42160     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
42161     /**
42162      * @cfg {Array} disabledDays
42163      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
42164      */
42165     disabledDays : null,
42166     /**
42167      * @cfg {String} disabledDaysText
42168      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
42169      */
42170     disabledDaysText : "Disabled",
42171     /**
42172      * @cfg {Array} disabledDates
42173      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
42174      * expression so they are very powerful. Some examples:
42175      * <ul>
42176      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
42177      * <li>["03/08", "09/16"] would disable those days for every year</li>
42178      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
42179      * <li>["03/../2006"] would disable every day in March 2006</li>
42180      * <li>["^03"] would disable every day in every March</li>
42181      * </ul>
42182      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
42183      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
42184      */
42185     disabledDates : null,
42186     /**
42187      * @cfg {String} disabledDatesText
42188      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
42189      */
42190     disabledDatesText : "Disabled",
42191         
42192         
42193         /**
42194      * @cfg {Date/String} zeroValue
42195      * if the date is less that this number, then the field is rendered as empty
42196      * default is 1800
42197      */
42198         zeroValue : '1800-01-01',
42199         
42200         
42201     /**
42202      * @cfg {Date/String} minValue
42203      * The minimum allowed date. Can be either a Javascript date object or a string date in a
42204      * valid format (defaults to null).
42205      */
42206     minValue : null,
42207     /**
42208      * @cfg {Date/String} maxValue
42209      * The maximum allowed date. Can be either a Javascript date object or a string date in a
42210      * valid format (defaults to null).
42211      */
42212     maxValue : null,
42213     /**
42214      * @cfg {String} minText
42215      * The error text to display when the date in the cell is before minValue (defaults to
42216      * 'The date in this field must be after {minValue}').
42217      */
42218     minText : "The date in this field must be equal to or after {0}",
42219     /**
42220      * @cfg {String} maxText
42221      * The error text to display when the date in the cell is after maxValue (defaults to
42222      * 'The date in this field must be before {maxValue}').
42223      */
42224     maxText : "The date in this field must be equal to or before {0}",
42225     /**
42226      * @cfg {String} invalidText
42227      * The error text to display when the date in the field is invalid (defaults to
42228      * '{value} is not a valid date - it must be in the format {format}').
42229      */
42230     invalidText : "{0} is not a valid date - it must be in the format {1}",
42231     /**
42232      * @cfg {String} triggerClass
42233      * An additional CSS class used to style the trigger button.  The trigger will always get the
42234      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
42235      * which displays a calendar icon).
42236      */
42237     triggerClass : 'x-form-date-trigger',
42238     
42239
42240     /**
42241      * @cfg {Boolean} useIso
42242      * if enabled, then the date field will use a hidden field to store the 
42243      * real value as iso formated date. default (false)
42244      */ 
42245     useIso : false,
42246     /**
42247      * @cfg {String/Object} autoCreate
42248      * A DomHelper element spec, or true for a default element spec (defaults to
42249      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
42250      */ 
42251     // private
42252     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
42253     
42254     // private
42255     hiddenField: false,
42256     
42257     onRender : function(ct, position)
42258     {
42259         Roo.form.DateField.superclass.onRender.call(this, ct, position);
42260         if (this.useIso) {
42261             //this.el.dom.removeAttribute('name'); 
42262             Roo.log("Changing name?");
42263             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
42264             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
42265                     'before', true);
42266             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
42267             // prevent input submission
42268             this.hiddenName = this.name;
42269         }
42270             
42271             
42272     },
42273     
42274     // private
42275     validateValue : function(value)
42276     {
42277         value = this.formatDate(value);
42278         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
42279             Roo.log('super failed');
42280             return false;
42281         }
42282         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
42283              return true;
42284         }
42285         var svalue = value;
42286         value = this.parseDate(value);
42287         if(!value){
42288             Roo.log('parse date failed' + svalue);
42289             this.markInvalid(String.format(this.invalidText, svalue, this.format));
42290             return false;
42291         }
42292         var time = value.getTime();
42293         if(this.minValue && time < this.minValue.getTime()){
42294             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
42295             return false;
42296         }
42297         if(this.maxValue && time > this.maxValue.getTime()){
42298             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
42299             return false;
42300         }
42301         if(this.disabledDays){
42302             var day = value.getDay();
42303             for(var i = 0; i < this.disabledDays.length; i++) {
42304                 if(day === this.disabledDays[i]){
42305                     this.markInvalid(this.disabledDaysText);
42306                     return false;
42307                 }
42308             }
42309         }
42310         var fvalue = this.formatDate(value);
42311         if(this.ddMatch && this.ddMatch.test(fvalue)){
42312             this.markInvalid(String.format(this.disabledDatesText, fvalue));
42313             return false;
42314         }
42315         return true;
42316     },
42317
42318     // private
42319     // Provides logic to override the default TriggerField.validateBlur which just returns true
42320     validateBlur : function(){
42321         return !this.menu || !this.menu.isVisible();
42322     },
42323     
42324     getName: function()
42325     {
42326         // returns hidden if it's set..
42327         if (!this.rendered) {return ''};
42328         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
42329         
42330     },
42331
42332     /**
42333      * Returns the current date value of the date field.
42334      * @return {Date} The date value
42335      */
42336     getValue : function(){
42337         
42338         return  this.hiddenField ?
42339                 this.hiddenField.value :
42340                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
42341     },
42342
42343     /**
42344      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
42345      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
42346      * (the default format used is "m/d/y").
42347      * <br />Usage:
42348      * <pre><code>
42349 //All of these calls set the same date value (May 4, 2006)
42350
42351 //Pass a date object:
42352 var dt = new Date('5/4/06');
42353 dateField.setValue(dt);
42354
42355 //Pass a date string (default format):
42356 dateField.setValue('5/4/06');
42357
42358 //Pass a date string (custom format):
42359 dateField.format = 'Y-m-d';
42360 dateField.setValue('2006-5-4');
42361 </code></pre>
42362      * @param {String/Date} date The date or valid date string
42363      */
42364     setValue : function(date){
42365         if (this.hiddenField) {
42366             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
42367         }
42368         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
42369         // make sure the value field is always stored as a date..
42370         this.value = this.parseDate(date);
42371         
42372         
42373     },
42374
42375     // private
42376     parseDate : function(value){
42377                 
42378                 if (value instanceof Date) {
42379                         if (value < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
42380                                 return  '';
42381                         }
42382                         return value;
42383                 }
42384                 
42385                 
42386         if(!value || value instanceof Date){
42387             return value;
42388         }
42389         var v = Date.parseDate(value, this.format);
42390          if (!v && this.useIso) {
42391             v = Date.parseDate(value, 'Y-m-d');
42392         }
42393         if(!v && this.altFormats){
42394             if(!this.altFormatsArray){
42395                 this.altFormatsArray = this.altFormats.split("|");
42396             }
42397             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
42398                 v = Date.parseDate(value, this.altFormatsArray[i]);
42399             }
42400         }
42401                 if (v < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
42402                         v = '';
42403                 }
42404         return v;
42405     },
42406
42407     // private
42408     formatDate : function(date, fmt){
42409         return (!date || !(date instanceof Date)) ?
42410                date : date.dateFormat(fmt || this.format);
42411     },
42412
42413     // private
42414     menuListeners : {
42415         select: function(m, d){
42416             
42417             this.setValue(d);
42418             this.fireEvent('select', this, d);
42419         },
42420         show : function(){ // retain focus styling
42421             this.onFocus();
42422         },
42423         hide : function(){
42424             this.focus.defer(10, this);
42425             var ml = this.menuListeners;
42426             this.menu.un("select", ml.select,  this);
42427             this.menu.un("show", ml.show,  this);
42428             this.menu.un("hide", ml.hide,  this);
42429         }
42430     },
42431
42432     // private
42433     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
42434     onTriggerClick : function(){
42435         if(this.disabled){
42436             return;
42437         }
42438         if(this.menu == null){
42439             this.menu = new Roo.menu.DateMenu();
42440         }
42441         Roo.apply(this.menu.picker,  {
42442             showClear: this.allowBlank,
42443             minDate : this.minValue,
42444             maxDate : this.maxValue,
42445             disabledDatesRE : this.ddMatch,
42446             disabledDatesText : this.disabledDatesText,
42447             disabledDays : this.disabledDays,
42448             disabledDaysText : this.disabledDaysText,
42449             format : this.useIso ? 'Y-m-d' : this.format,
42450             minText : String.format(this.minText, this.formatDate(this.minValue)),
42451             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
42452         });
42453         this.menu.on(Roo.apply({}, this.menuListeners, {
42454             scope:this
42455         }));
42456         this.menu.picker.setValue(this.getValue() || new Date());
42457         this.menu.show(this.el, "tl-bl?");
42458     },
42459
42460     beforeBlur : function(){
42461         var v = this.parseDate(this.getRawValue());
42462         if(v){
42463             this.setValue(v);
42464         }
42465     },
42466
42467     /*@
42468      * overide
42469      * 
42470      */
42471     isDirty : function() {
42472         if(this.disabled) {
42473             return false;
42474         }
42475         
42476         if(typeof(this.startValue) === 'undefined'){
42477             return false;
42478         }
42479         
42480         return String(this.getValue()) !== String(this.startValue);
42481         
42482     },
42483     // @overide
42484     cleanLeadingSpace : function(e)
42485     {
42486        return;
42487     }
42488     
42489 });/*
42490  * Based on:
42491  * Ext JS Library 1.1.1
42492  * Copyright(c) 2006-2007, Ext JS, LLC.
42493  *
42494  * Originally Released Under LGPL - original licence link has changed is not relivant.
42495  *
42496  * Fork - LGPL
42497  * <script type="text/javascript">
42498  */
42499  
42500 /**
42501  * @class Roo.form.MonthField
42502  * @extends Roo.form.TriggerField
42503  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
42504 * @constructor
42505 * Create a new MonthField
42506 * @param {Object} config
42507  */
42508 Roo.form.MonthField = function(config){
42509     
42510     Roo.form.MonthField.superclass.constructor.call(this, config);
42511     
42512       this.addEvents({
42513          
42514         /**
42515          * @event select
42516          * Fires when a date is selected
42517              * @param {Roo.form.MonthFieeld} combo This combo box
42518              * @param {Date} date The date selected
42519              */
42520         'select' : true
42521          
42522     });
42523     
42524     
42525     if(typeof this.minValue == "string") {
42526         this.minValue = this.parseDate(this.minValue);
42527     }
42528     if(typeof this.maxValue == "string") {
42529         this.maxValue = this.parseDate(this.maxValue);
42530     }
42531     this.ddMatch = null;
42532     if(this.disabledDates){
42533         var dd = this.disabledDates;
42534         var re = "(?:";
42535         for(var i = 0; i < dd.length; i++){
42536             re += dd[i];
42537             if(i != dd.length-1) {
42538                 re += "|";
42539             }
42540         }
42541         this.ddMatch = new RegExp(re + ")");
42542     }
42543 };
42544
42545 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
42546     /**
42547      * @cfg {String} format
42548      * The default date format string which can be overriden for localization support.  The format must be
42549      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
42550      */
42551     format : "M Y",
42552     /**
42553      * @cfg {String} altFormats
42554      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
42555      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
42556      */
42557     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
42558     /**
42559      * @cfg {Array} disabledDays
42560      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
42561      */
42562     disabledDays : [0,1,2,3,4,5,6],
42563     /**
42564      * @cfg {String} disabledDaysText
42565      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
42566      */
42567     disabledDaysText : "Disabled",
42568     /**
42569      * @cfg {Array} disabledDates
42570      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
42571      * expression so they are very powerful. Some examples:
42572      * <ul>
42573      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
42574      * <li>["03/08", "09/16"] would disable those days for every year</li>
42575      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
42576      * <li>["03/../2006"] would disable every day in March 2006</li>
42577      * <li>["^03"] would disable every day in every March</li>
42578      * </ul>
42579      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
42580      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
42581      */
42582     disabledDates : null,
42583     /**
42584      * @cfg {String} disabledDatesText
42585      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
42586      */
42587     disabledDatesText : "Disabled",
42588     /**
42589      * @cfg {Date/String} minValue
42590      * The minimum allowed date. Can be either a Javascript date object or a string date in a
42591      * valid format (defaults to null).
42592      */
42593     minValue : null,
42594     /**
42595      * @cfg {Date/String} maxValue
42596      * The maximum allowed date. Can be either a Javascript date object or a string date in a
42597      * valid format (defaults to null).
42598      */
42599     maxValue : null,
42600     /**
42601      * @cfg {String} minText
42602      * The error text to display when the date in the cell is before minValue (defaults to
42603      * 'The date in this field must be after {minValue}').
42604      */
42605     minText : "The date in this field must be equal to or after {0}",
42606     /**
42607      * @cfg {String} maxTextf
42608      * The error text to display when the date in the cell is after maxValue (defaults to
42609      * 'The date in this field must be before {maxValue}').
42610      */
42611     maxText : "The date in this field must be equal to or before {0}",
42612     /**
42613      * @cfg {String} invalidText
42614      * The error text to display when the date in the field is invalid (defaults to
42615      * '{value} is not a valid date - it must be in the format {format}').
42616      */
42617     invalidText : "{0} is not a valid date - it must be in the format {1}",
42618     /**
42619      * @cfg {String} triggerClass
42620      * An additional CSS class used to style the trigger button.  The trigger will always get the
42621      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
42622      * which displays a calendar icon).
42623      */
42624     triggerClass : 'x-form-date-trigger',
42625     
42626
42627     /**
42628      * @cfg {Boolean} useIso
42629      * if enabled, then the date field will use a hidden field to store the 
42630      * real value as iso formated date. default (true)
42631      */ 
42632     useIso : true,
42633     /**
42634      * @cfg {String/Object} autoCreate
42635      * A DomHelper element spec, or true for a default element spec (defaults to
42636      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
42637      */ 
42638     // private
42639     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
42640     
42641     // private
42642     hiddenField: false,
42643     
42644     hideMonthPicker : false,
42645     
42646     onRender : function(ct, position)
42647     {
42648         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
42649         if (this.useIso) {
42650             this.el.dom.removeAttribute('name'); 
42651             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
42652                     'before', true);
42653             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
42654             // prevent input submission
42655             this.hiddenName = this.name;
42656         }
42657             
42658             
42659     },
42660     
42661     // private
42662     validateValue : function(value)
42663     {
42664         value = this.formatDate(value);
42665         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
42666             return false;
42667         }
42668         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
42669              return true;
42670         }
42671         var svalue = value;
42672         value = this.parseDate(value);
42673         if(!value){
42674             this.markInvalid(String.format(this.invalidText, svalue, this.format));
42675             return false;
42676         }
42677         var time = value.getTime();
42678         if(this.minValue && time < this.minValue.getTime()){
42679             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
42680             return false;
42681         }
42682         if(this.maxValue && time > this.maxValue.getTime()){
42683             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
42684             return false;
42685         }
42686         /*if(this.disabledDays){
42687             var day = value.getDay();
42688             for(var i = 0; i < this.disabledDays.length; i++) {
42689                 if(day === this.disabledDays[i]){
42690                     this.markInvalid(this.disabledDaysText);
42691                     return false;
42692                 }
42693             }
42694         }
42695         */
42696         var fvalue = this.formatDate(value);
42697         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
42698             this.markInvalid(String.format(this.disabledDatesText, fvalue));
42699             return false;
42700         }
42701         */
42702         return true;
42703     },
42704
42705     // private
42706     // Provides logic to override the default TriggerField.validateBlur which just returns true
42707     validateBlur : function(){
42708         return !this.menu || !this.menu.isVisible();
42709     },
42710
42711     /**
42712      * Returns the current date value of the date field.
42713      * @return {Date} The date value
42714      */
42715     getValue : function(){
42716         
42717         
42718         
42719         return  this.hiddenField ?
42720                 this.hiddenField.value :
42721                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
42722     },
42723
42724     /**
42725      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
42726      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
42727      * (the default format used is "m/d/y").
42728      * <br />Usage:
42729      * <pre><code>
42730 //All of these calls set the same date value (May 4, 2006)
42731
42732 //Pass a date object:
42733 var dt = new Date('5/4/06');
42734 monthField.setValue(dt);
42735
42736 //Pass a date string (default format):
42737 monthField.setValue('5/4/06');
42738
42739 //Pass a date string (custom format):
42740 monthField.format = 'Y-m-d';
42741 monthField.setValue('2006-5-4');
42742 </code></pre>
42743      * @param {String/Date} date The date or valid date string
42744      */
42745     setValue : function(date){
42746         Roo.log('month setValue' + date);
42747         // can only be first of month..
42748         
42749         var val = this.parseDate(date);
42750         
42751         if (this.hiddenField) {
42752             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
42753         }
42754         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
42755         this.value = this.parseDate(date);
42756     },
42757
42758     // private
42759     parseDate : function(value){
42760         if(!value || value instanceof Date){
42761             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
42762             return value;
42763         }
42764         var v = Date.parseDate(value, this.format);
42765         if (!v && this.useIso) {
42766             v = Date.parseDate(value, 'Y-m-d');
42767         }
42768         if (v) {
42769             // 
42770             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
42771         }
42772         
42773         
42774         if(!v && this.altFormats){
42775             if(!this.altFormatsArray){
42776                 this.altFormatsArray = this.altFormats.split("|");
42777             }
42778             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
42779                 v = Date.parseDate(value, this.altFormatsArray[i]);
42780             }
42781         }
42782         return v;
42783     },
42784
42785     // private
42786     formatDate : function(date, fmt){
42787         return (!date || !(date instanceof Date)) ?
42788                date : date.dateFormat(fmt || this.format);
42789     },
42790
42791     // private
42792     menuListeners : {
42793         select: function(m, d){
42794             this.setValue(d);
42795             this.fireEvent('select', this, d);
42796         },
42797         show : function(){ // retain focus styling
42798             this.onFocus();
42799         },
42800         hide : function(){
42801             this.focus.defer(10, this);
42802             var ml = this.menuListeners;
42803             this.menu.un("select", ml.select,  this);
42804             this.menu.un("show", ml.show,  this);
42805             this.menu.un("hide", ml.hide,  this);
42806         }
42807     },
42808     // private
42809     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
42810     onTriggerClick : function(){
42811         if(this.disabled){
42812             return;
42813         }
42814         if(this.menu == null){
42815             this.menu = new Roo.menu.DateMenu();
42816            
42817         }
42818         
42819         Roo.apply(this.menu.picker,  {
42820             
42821             showClear: this.allowBlank,
42822             minDate : this.minValue,
42823             maxDate : this.maxValue,
42824             disabledDatesRE : this.ddMatch,
42825             disabledDatesText : this.disabledDatesText,
42826             
42827             format : this.useIso ? 'Y-m-d' : this.format,
42828             minText : String.format(this.minText, this.formatDate(this.minValue)),
42829             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
42830             
42831         });
42832          this.menu.on(Roo.apply({}, this.menuListeners, {
42833             scope:this
42834         }));
42835        
42836         
42837         var m = this.menu;
42838         var p = m.picker;
42839         
42840         // hide month picker get's called when we called by 'before hide';
42841         
42842         var ignorehide = true;
42843         p.hideMonthPicker  = function(disableAnim){
42844             if (ignorehide) {
42845                 return;
42846             }
42847              if(this.monthPicker){
42848                 Roo.log("hideMonthPicker called");
42849                 if(disableAnim === true){
42850                     this.monthPicker.hide();
42851                 }else{
42852                     this.monthPicker.slideOut('t', {duration:.2});
42853                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
42854                     p.fireEvent("select", this, this.value);
42855                     m.hide();
42856                 }
42857             }
42858         }
42859         
42860         Roo.log('picker set value');
42861         Roo.log(this.getValue());
42862         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
42863         m.show(this.el, 'tl-bl?');
42864         ignorehide  = false;
42865         // this will trigger hideMonthPicker..
42866         
42867         
42868         // hidden the day picker
42869         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
42870         
42871         
42872         
42873       
42874         
42875         p.showMonthPicker.defer(100, p);
42876     
42877         
42878        
42879     },
42880
42881     beforeBlur : function(){
42882         var v = this.parseDate(this.getRawValue());
42883         if(v){
42884             this.setValue(v);
42885         }
42886     }
42887
42888     /** @cfg {Boolean} grow @hide */
42889     /** @cfg {Number} growMin @hide */
42890     /** @cfg {Number} growMax @hide */
42891     /**
42892      * @hide
42893      * @method autoSize
42894      */
42895 });/*
42896  * Based on:
42897  * Ext JS Library 1.1.1
42898  * Copyright(c) 2006-2007, Ext JS, LLC.
42899  *
42900  * Originally Released Under LGPL - original licence link has changed is not relivant.
42901  *
42902  * Fork - LGPL
42903  * <script type="text/javascript">
42904  */
42905  
42906
42907 /**
42908  * @class Roo.form.ComboBox
42909  * @extends Roo.form.TriggerField
42910  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
42911  * @constructor
42912  * Create a new ComboBox.
42913  * @param {Object} config Configuration options
42914  */
42915 Roo.form.ComboBox = function(config){
42916     Roo.form.ComboBox.superclass.constructor.call(this, config);
42917     this.addEvents({
42918         /**
42919          * @event expand
42920          * Fires when the dropdown list is expanded
42921              * @param {Roo.form.ComboBox} combo This combo box
42922              */
42923         'expand' : true,
42924         /**
42925          * @event collapse
42926          * Fires when the dropdown list is collapsed
42927              * @param {Roo.form.ComboBox} combo This combo box
42928              */
42929         'collapse' : true,
42930         /**
42931          * @event beforeselect
42932          * Fires before a list item is selected. Return false to cancel the selection.
42933              * @param {Roo.form.ComboBox} combo This combo box
42934              * @param {Roo.data.Record} record The data record returned from the underlying store
42935              * @param {Number} index The index of the selected item in the dropdown list
42936              */
42937         'beforeselect' : true,
42938         /**
42939          * @event select
42940          * Fires when a list item is selected
42941              * @param {Roo.form.ComboBox} combo This combo box
42942              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
42943              * @param {Number} index The index of the selected item in the dropdown list
42944              */
42945         'select' : true,
42946         /**
42947          * @event beforequery
42948          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
42949          * The event object passed has these properties:
42950              * @param {Roo.form.ComboBox} combo This combo box
42951              * @param {String} query The query
42952              * @param {Boolean} forceAll true to force "all" query
42953              * @param {Boolean} cancel true to cancel the query
42954              * @param {Object} e The query event object
42955              */
42956         'beforequery': true,
42957          /**
42958          * @event add
42959          * Fires when the 'add' icon is pressed (add a listener to enable add button)
42960              * @param {Roo.form.ComboBox} combo This combo box
42961              */
42962         'add' : true,
42963         /**
42964          * @event edit
42965          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
42966              * @param {Roo.form.ComboBox} combo This combo box
42967              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
42968              */
42969         'edit' : true
42970         
42971         
42972     });
42973     if(this.transform){
42974         this.allowDomMove = false;
42975         var s = Roo.getDom(this.transform);
42976         if(!this.hiddenName){
42977             this.hiddenName = s.name;
42978         }
42979         if(!this.store){
42980             this.mode = 'local';
42981             var d = [], opts = s.options;
42982             for(var i = 0, len = opts.length;i < len; i++){
42983                 var o = opts[i];
42984                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
42985                 if(o.selected) {
42986                     this.value = value;
42987                 }
42988                 d.push([value, o.text]);
42989             }
42990             this.store = new Roo.data.SimpleStore({
42991                 'id': 0,
42992                 fields: ['value', 'text'],
42993                 data : d
42994             });
42995             this.valueField = 'value';
42996             this.displayField = 'text';
42997         }
42998         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
42999         if(!this.lazyRender){
43000             this.target = true;
43001             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
43002             s.parentNode.removeChild(s); // remove it
43003             this.render(this.el.parentNode);
43004         }else{
43005             s.parentNode.removeChild(s); // remove it
43006         }
43007
43008     }
43009     if (this.store) {
43010         this.store = Roo.factory(this.store, Roo.data);
43011     }
43012     
43013     this.selectedIndex = -1;
43014     if(this.mode == 'local'){
43015         if(config.queryDelay === undefined){
43016             this.queryDelay = 10;
43017         }
43018         if(config.minChars === undefined){
43019             this.minChars = 0;
43020         }
43021     }
43022 };
43023
43024 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
43025     /**
43026      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
43027      */
43028     /**
43029      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
43030      * rendering into an Roo.Editor, defaults to false)
43031      */
43032     /**
43033      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
43034      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
43035      */
43036     /**
43037      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
43038      */
43039     /**
43040      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
43041      * the dropdown list (defaults to undefined, with no header element)
43042      */
43043
43044      /**
43045      * @cfg {String/Roo.Template} tpl The template to use to render the output
43046      */
43047      
43048     // private
43049     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
43050     /**
43051      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
43052      */
43053     listWidth: undefined,
43054     /**
43055      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
43056      * mode = 'remote' or 'text' if mode = 'local')
43057      */
43058     displayField: undefined,
43059     /**
43060      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
43061      * mode = 'remote' or 'value' if mode = 'local'). 
43062      * Note: use of a valueField requires the user make a selection
43063      * in order for a value to be mapped.
43064      */
43065     valueField: undefined,
43066     
43067     
43068     /**
43069      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
43070      * field's data value (defaults to the underlying DOM element's name)
43071      */
43072     hiddenName: undefined,
43073     /**
43074      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
43075      */
43076     listClass: '',
43077     /**
43078      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
43079      */
43080     selectedClass: 'x-combo-selected',
43081     /**
43082      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
43083      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
43084      * which displays a downward arrow icon).
43085      */
43086     triggerClass : 'x-form-arrow-trigger',
43087     /**
43088      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
43089      */
43090     shadow:'sides',
43091     /**
43092      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
43093      * anchor positions (defaults to 'tl-bl')
43094      */
43095     listAlign: 'tl-bl?',
43096     /**
43097      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
43098      */
43099     maxHeight: 300,
43100     /**
43101      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
43102      * query specified by the allQuery config option (defaults to 'query')
43103      */
43104     triggerAction: 'query',
43105     /**
43106      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
43107      * (defaults to 4, does not apply if editable = false)
43108      */
43109     minChars : 4,
43110     /**
43111      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
43112      * delay (typeAheadDelay) if it matches a known value (defaults to false)
43113      */
43114     typeAhead: false,
43115     /**
43116      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
43117      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
43118      */
43119     queryDelay: 500,
43120     /**
43121      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
43122      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
43123      */
43124     pageSize: 0,
43125     /**
43126      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
43127      * when editable = true (defaults to false)
43128      */
43129     selectOnFocus:false,
43130     /**
43131      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
43132      */
43133     queryParam: 'query',
43134     /**
43135      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
43136      * when mode = 'remote' (defaults to 'Loading...')
43137      */
43138     loadingText: 'Loading...',
43139     /**
43140      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
43141      */
43142     resizable: false,
43143     /**
43144      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
43145      */
43146     handleHeight : 8,
43147     /**
43148      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
43149      * traditional select (defaults to true)
43150      */
43151     editable: true,
43152     /**
43153      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
43154      */
43155     allQuery: '',
43156     /**
43157      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
43158      */
43159     mode: 'remote',
43160     /**
43161      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
43162      * listWidth has a higher value)
43163      */
43164     minListWidth : 70,
43165     /**
43166      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
43167      * allow the user to set arbitrary text into the field (defaults to false)
43168      */
43169     forceSelection:false,
43170     /**
43171      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
43172      * if typeAhead = true (defaults to 250)
43173      */
43174     typeAheadDelay : 250,
43175     /**
43176      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
43177      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
43178      */
43179     valueNotFoundText : undefined,
43180     /**
43181      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
43182      */
43183     blockFocus : false,
43184     
43185     /**
43186      * @cfg {Boolean} disableClear Disable showing of clear button.
43187      */
43188     disableClear : false,
43189     /**
43190      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
43191      */
43192     alwaysQuery : false,
43193     
43194     //private
43195     addicon : false,
43196     editicon: false,
43197     
43198     // element that contains real text value.. (when hidden is used..)
43199      
43200     // private
43201     onRender : function(ct, position)
43202     {
43203         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
43204         
43205                 if(this.hiddenName){
43206             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
43207                     'before', true);
43208             this.hiddenField.value =
43209                 this.hiddenValue !== undefined ? this.hiddenValue :
43210                 this.value !== undefined ? this.value : '';
43211
43212             // prevent input submission
43213             this.el.dom.removeAttribute('name');
43214              
43215              
43216         }
43217         
43218         if(Roo.isGecko){
43219             this.el.dom.setAttribute('autocomplete', 'off');
43220         }
43221
43222         var cls = 'x-combo-list';
43223
43224         this.list = new Roo.Layer({
43225             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
43226         });
43227
43228         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
43229         this.list.setWidth(lw);
43230         this.list.swallowEvent('mousewheel');
43231         this.assetHeight = 0;
43232
43233         if(this.title){
43234             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
43235             this.assetHeight += this.header.getHeight();
43236         }
43237
43238         this.innerList = this.list.createChild({cls:cls+'-inner'});
43239         this.innerList.on('mouseover', this.onViewOver, this);
43240         this.innerList.on('mousemove', this.onViewMove, this);
43241         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
43242         
43243         if(this.allowBlank && !this.pageSize && !this.disableClear){
43244             this.footer = this.list.createChild({cls:cls+'-ft'});
43245             this.pageTb = new Roo.Toolbar(this.footer);
43246            
43247         }
43248         if(this.pageSize){
43249             this.footer = this.list.createChild({cls:cls+'-ft'});
43250             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
43251                     {pageSize: this.pageSize});
43252             
43253         }
43254         
43255         if (this.pageTb && this.allowBlank && !this.disableClear) {
43256             var _this = this;
43257             this.pageTb.add(new Roo.Toolbar.Fill(), {
43258                 cls: 'x-btn-icon x-btn-clear',
43259                 text: '&#160;',
43260                 handler: function()
43261                 {
43262                     _this.collapse();
43263                     _this.clearValue();
43264                     _this.onSelect(false, -1);
43265                 }
43266             });
43267         }
43268         if (this.footer) {
43269             this.assetHeight += this.footer.getHeight();
43270         }
43271         
43272
43273         if(!this.tpl){
43274             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
43275         }
43276
43277         this.view = new Roo.View(this.innerList, this.tpl, {
43278             singleSelect:true,
43279             store: this.store,
43280             selectedClass: this.selectedClass
43281         });
43282
43283         this.view.on('click', this.onViewClick, this);
43284
43285         this.store.on('beforeload', this.onBeforeLoad, this);
43286         this.store.on('load', this.onLoad, this);
43287         this.store.on('loadexception', this.onLoadException, this);
43288
43289         if(this.resizable){
43290             this.resizer = new Roo.Resizable(this.list,  {
43291                pinned:true, handles:'se'
43292             });
43293             this.resizer.on('resize', function(r, w, h){
43294                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
43295                 this.listWidth = w;
43296                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
43297                 this.restrictHeight();
43298             }, this);
43299             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
43300         }
43301         if(!this.editable){
43302             this.editable = true;
43303             this.setEditable(false);
43304         }  
43305         
43306         
43307         if (typeof(this.events.add.listeners) != 'undefined') {
43308             
43309             this.addicon = this.wrap.createChild(
43310                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
43311        
43312             this.addicon.on('click', function(e) {
43313                 this.fireEvent('add', this);
43314             }, this);
43315         }
43316         if (typeof(this.events.edit.listeners) != 'undefined') {
43317             
43318             this.editicon = this.wrap.createChild(
43319                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
43320             if (this.addicon) {
43321                 this.editicon.setStyle('margin-left', '40px');
43322             }
43323             this.editicon.on('click', function(e) {
43324                 
43325                 // we fire even  if inothing is selected..
43326                 this.fireEvent('edit', this, this.lastData );
43327                 
43328             }, this);
43329         }
43330         
43331         
43332         
43333     },
43334
43335     // private
43336     initEvents : function(){
43337         Roo.form.ComboBox.superclass.initEvents.call(this);
43338
43339         this.keyNav = new Roo.KeyNav(this.el, {
43340             "up" : function(e){
43341                 this.inKeyMode = true;
43342                 this.selectPrev();
43343             },
43344
43345             "down" : function(e){
43346                 if(!this.isExpanded()){
43347                     this.onTriggerClick();
43348                 }else{
43349                     this.inKeyMode = true;
43350                     this.selectNext();
43351                 }
43352             },
43353
43354             "enter" : function(e){
43355                 this.onViewClick();
43356                 //return true;
43357             },
43358
43359             "esc" : function(e){
43360                 this.collapse();
43361             },
43362
43363             "tab" : function(e){
43364                 this.onViewClick(false);
43365                 this.fireEvent("specialkey", this, e);
43366                 return true;
43367             },
43368
43369             scope : this,
43370
43371             doRelay : function(foo, bar, hname){
43372                 if(hname == 'down' || this.scope.isExpanded()){
43373                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43374                 }
43375                 return true;
43376             },
43377
43378             forceKeyDown: true
43379         });
43380         this.queryDelay = Math.max(this.queryDelay || 10,
43381                 this.mode == 'local' ? 10 : 250);
43382         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
43383         if(this.typeAhead){
43384             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
43385         }
43386         if(this.editable !== false){
43387             this.el.on("keyup", this.onKeyUp, this);
43388         }
43389         if(this.forceSelection){
43390             this.on('blur', this.doForce, this);
43391         }
43392     },
43393
43394     onDestroy : function(){
43395         if(this.view){
43396             this.view.setStore(null);
43397             this.view.el.removeAllListeners();
43398             this.view.el.remove();
43399             this.view.purgeListeners();
43400         }
43401         if(this.list){
43402             this.list.destroy();
43403         }
43404         if(this.store){
43405             this.store.un('beforeload', this.onBeforeLoad, this);
43406             this.store.un('load', this.onLoad, this);
43407             this.store.un('loadexception', this.onLoadException, this);
43408         }
43409         Roo.form.ComboBox.superclass.onDestroy.call(this);
43410     },
43411
43412     // private
43413     fireKey : function(e){
43414         if(e.isNavKeyPress() && !this.list.isVisible()){
43415             this.fireEvent("specialkey", this, e);
43416         }
43417     },
43418
43419     // private
43420     onResize: function(w, h){
43421         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
43422         
43423         if(typeof w != 'number'){
43424             // we do not handle it!?!?
43425             return;
43426         }
43427         var tw = this.trigger.getWidth();
43428         tw += this.addicon ? this.addicon.getWidth() : 0;
43429         tw += this.editicon ? this.editicon.getWidth() : 0;
43430         var x = w - tw;
43431         this.el.setWidth( this.adjustWidth('input', x));
43432             
43433         this.trigger.setStyle('left', x+'px');
43434         
43435         if(this.list && this.listWidth === undefined){
43436             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
43437             this.list.setWidth(lw);
43438             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
43439         }
43440         
43441     
43442         
43443     },
43444
43445     /**
43446      * Allow or prevent the user from directly editing the field text.  If false is passed,
43447      * the user will only be able to select from the items defined in the dropdown list.  This method
43448      * is the runtime equivalent of setting the 'editable' config option at config time.
43449      * @param {Boolean} value True to allow the user to directly edit the field text
43450      */
43451     setEditable : function(value){
43452         if(value == this.editable){
43453             return;
43454         }
43455         this.editable = value;
43456         if(!value){
43457             this.el.dom.setAttribute('readOnly', true);
43458             this.el.on('mousedown', this.onTriggerClick,  this);
43459             this.el.addClass('x-combo-noedit');
43460         }else{
43461             this.el.dom.setAttribute('readOnly', false);
43462             this.el.un('mousedown', this.onTriggerClick,  this);
43463             this.el.removeClass('x-combo-noedit');
43464         }
43465     },
43466
43467     // private
43468     onBeforeLoad : function(){
43469         if(!this.hasFocus){
43470             return;
43471         }
43472         this.innerList.update(this.loadingText ?
43473                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
43474         this.restrictHeight();
43475         this.selectedIndex = -1;
43476     },
43477
43478     // private
43479     onLoad : function(){
43480         if(!this.hasFocus){
43481             return;
43482         }
43483         if(this.store.getCount() > 0){
43484             this.expand();
43485             this.restrictHeight();
43486             if(this.lastQuery == this.allQuery){
43487                 if(this.editable){
43488                     this.el.dom.select();
43489                 }
43490                 if(!this.selectByValue(this.value, true)){
43491                     this.select(0, true);
43492                 }
43493             }else{
43494                 this.selectNext();
43495                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
43496                     this.taTask.delay(this.typeAheadDelay);
43497                 }
43498             }
43499         }else{
43500             this.onEmptyResults();
43501         }
43502         //this.el.focus();
43503     },
43504     // private
43505     onLoadException : function()
43506     {
43507         this.collapse();
43508         Roo.log(this.store.reader.jsonData);
43509         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
43510             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
43511         }
43512         
43513         
43514     },
43515     // private
43516     onTypeAhead : function(){
43517         if(this.store.getCount() > 0){
43518             var r = this.store.getAt(0);
43519             var newValue = r.data[this.displayField];
43520             var len = newValue.length;
43521             var selStart = this.getRawValue().length;
43522             if(selStart != len){
43523                 this.setRawValue(newValue);
43524                 this.selectText(selStart, newValue.length);
43525             }
43526         }
43527     },
43528
43529     // private
43530     onSelect : function(record, index){
43531         if(this.fireEvent('beforeselect', this, record, index) !== false){
43532             this.setFromData(index > -1 ? record.data : false);
43533             this.collapse();
43534             this.fireEvent('select', this, record, index);
43535         }
43536     },
43537
43538     /**
43539      * Returns the currently selected field value or empty string if no value is set.
43540      * @return {String} value The selected value
43541      */
43542     getValue : function(){
43543         if(this.valueField){
43544             return typeof this.value != 'undefined' ? this.value : '';
43545         }
43546         return Roo.form.ComboBox.superclass.getValue.call(this);
43547     },
43548
43549     /**
43550      * Clears any text/value currently set in the field
43551      */
43552     clearValue : function(){
43553         if(this.hiddenField){
43554             this.hiddenField.value = '';
43555         }
43556         this.value = '';
43557         this.setRawValue('');
43558         this.lastSelectionText = '';
43559         
43560     },
43561
43562     /**
43563      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
43564      * will be displayed in the field.  If the value does not match the data value of an existing item,
43565      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
43566      * Otherwise the field will be blank (although the value will still be set).
43567      * @param {String} value The value to match
43568      */
43569     setValue : function(v){
43570         var text = v;
43571         if(this.valueField){
43572             var r = this.findRecord(this.valueField, v);
43573             if(r){
43574                 text = r.data[this.displayField];
43575             }else if(this.valueNotFoundText !== undefined){
43576                 text = this.valueNotFoundText;
43577             }
43578         }
43579         this.lastSelectionText = text;
43580         if(this.hiddenField){
43581             this.hiddenField.value = v;
43582         }
43583         Roo.form.ComboBox.superclass.setValue.call(this, text);
43584         this.value = v;
43585     },
43586     /**
43587      * @property {Object} the last set data for the element
43588      */
43589     
43590     lastData : false,
43591     /**
43592      * Sets the value of the field based on a object which is related to the record format for the store.
43593      * @param {Object} value the value to set as. or false on reset?
43594      */
43595     setFromData : function(o){
43596         var dv = ''; // display value
43597         var vv = ''; // value value..
43598         this.lastData = o;
43599         if (this.displayField) {
43600             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
43601         } else {
43602             // this is an error condition!!!
43603             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
43604         }
43605         
43606         if(this.valueField){
43607             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
43608         }
43609         if(this.hiddenField){
43610             this.hiddenField.value = vv;
43611             
43612             this.lastSelectionText = dv;
43613             Roo.form.ComboBox.superclass.setValue.call(this, dv);
43614             this.value = vv;
43615             return;
43616         }
43617         // no hidden field.. - we store the value in 'value', but still display
43618         // display field!!!!
43619         this.lastSelectionText = dv;
43620         Roo.form.ComboBox.superclass.setValue.call(this, dv);
43621         this.value = vv;
43622         
43623         
43624     },
43625     // private
43626     reset : function(){
43627         // overridden so that last data is reset..
43628         this.setValue(this.resetValue);
43629         this.originalValue = this.getValue();
43630         this.clearInvalid();
43631         this.lastData = false;
43632         if (this.view) {
43633             this.view.clearSelections();
43634         }
43635     },
43636     // private
43637     findRecord : function(prop, value){
43638         var record;
43639         if(this.store.getCount() > 0){
43640             this.store.each(function(r){
43641                 if(r.data[prop] == value){
43642                     record = r;
43643                     return false;
43644                 }
43645                 return true;
43646             });
43647         }
43648         return record;
43649     },
43650     
43651     getName: function()
43652     {
43653         // returns hidden if it's set..
43654         if (!this.rendered) {return ''};
43655         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
43656         
43657     },
43658     // private
43659     onViewMove : function(e, t){
43660         this.inKeyMode = false;
43661     },
43662
43663     // private
43664     onViewOver : function(e, t){
43665         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
43666             return;
43667         }
43668         var item = this.view.findItemFromChild(t);
43669         if(item){
43670             var index = this.view.indexOf(item);
43671             this.select(index, false);
43672         }
43673     },
43674
43675     // private
43676     onViewClick : function(doFocus)
43677     {
43678         var index = this.view.getSelectedIndexes()[0];
43679         var r = this.store.getAt(index);
43680         if(r){
43681             this.onSelect(r, index);
43682         }
43683         if(doFocus !== false && !this.blockFocus){
43684             this.el.focus();
43685         }
43686     },
43687
43688     // private
43689     restrictHeight : function(){
43690         this.innerList.dom.style.height = '';
43691         var inner = this.innerList.dom;
43692         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
43693         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43694         this.list.beginUpdate();
43695         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
43696         this.list.alignTo(this.el, this.listAlign);
43697         this.list.endUpdate();
43698     },
43699
43700     // private
43701     onEmptyResults : function(){
43702         this.collapse();
43703     },
43704
43705     /**
43706      * Returns true if the dropdown list is expanded, else false.
43707      */
43708     isExpanded : function(){
43709         return this.list.isVisible();
43710     },
43711
43712     /**
43713      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
43714      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
43715      * @param {String} value The data value of the item to select
43716      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
43717      * selected item if it is not currently in view (defaults to true)
43718      * @return {Boolean} True if the value matched an item in the list, else false
43719      */
43720     selectByValue : function(v, scrollIntoView){
43721         if(v !== undefined && v !== null){
43722             var r = this.findRecord(this.valueField || this.displayField, v);
43723             if(r){
43724                 this.select(this.store.indexOf(r), scrollIntoView);
43725                 return true;
43726             }
43727         }
43728         return false;
43729     },
43730
43731     /**
43732      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
43733      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
43734      * @param {Number} index The zero-based index of the list item to select
43735      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
43736      * selected item if it is not currently in view (defaults to true)
43737      */
43738     select : function(index, scrollIntoView){
43739         this.selectedIndex = index;
43740         this.view.select(index);
43741         if(scrollIntoView !== false){
43742             var el = this.view.getNode(index);
43743             if(el){
43744                 this.innerList.scrollChildIntoView(el, false);
43745             }
43746         }
43747     },
43748
43749     // private
43750     selectNext : function(){
43751         var ct = this.store.getCount();
43752         if(ct > 0){
43753             if(this.selectedIndex == -1){
43754                 this.select(0);
43755             }else if(this.selectedIndex < ct-1){
43756                 this.select(this.selectedIndex+1);
43757             }
43758         }
43759     },
43760
43761     // private
43762     selectPrev : function(){
43763         var ct = this.store.getCount();
43764         if(ct > 0){
43765             if(this.selectedIndex == -1){
43766                 this.select(0);
43767             }else if(this.selectedIndex != 0){
43768                 this.select(this.selectedIndex-1);
43769             }
43770         }
43771     },
43772
43773     // private
43774     onKeyUp : function(e){
43775         if(this.editable !== false && !e.isSpecialKey()){
43776             this.lastKey = e.getKey();
43777             this.dqTask.delay(this.queryDelay);
43778         }
43779     },
43780
43781     // private
43782     validateBlur : function(){
43783         return !this.list || !this.list.isVisible();   
43784     },
43785
43786     // private
43787     initQuery : function(){
43788         this.doQuery(this.getRawValue());
43789     },
43790
43791     // private
43792     doForce : function(){
43793         if(this.el.dom.value.length > 0){
43794             this.el.dom.value =
43795                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
43796              
43797         }
43798     },
43799
43800     /**
43801      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
43802      * query allowing the query action to be canceled if needed.
43803      * @param {String} query The SQL query to execute
43804      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
43805      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
43806      * saved in the current store (defaults to false)
43807      */
43808     doQuery : function(q, forceAll){
43809         if(q === undefined || q === null){
43810             q = '';
43811         }
43812         var qe = {
43813             query: q,
43814             forceAll: forceAll,
43815             combo: this,
43816             cancel:false
43817         };
43818         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
43819             return false;
43820         }
43821         q = qe.query;
43822         forceAll = qe.forceAll;
43823         if(forceAll === true || (q.length >= this.minChars)){
43824             if(this.lastQuery != q || this.alwaysQuery){
43825                 this.lastQuery = q;
43826                 if(this.mode == 'local'){
43827                     this.selectedIndex = -1;
43828                     if(forceAll){
43829                         this.store.clearFilter();
43830                     }else{
43831                         this.store.filter(this.displayField, q);
43832                     }
43833                     this.onLoad();
43834                 }else{
43835                     this.store.baseParams[this.queryParam] = q;
43836                     this.store.load({
43837                         params: this.getParams(q)
43838                     });
43839                     this.expand();
43840                 }
43841             }else{
43842                 this.selectedIndex = -1;
43843                 this.onLoad();   
43844             }
43845         }
43846     },
43847
43848     // private
43849     getParams : function(q){
43850         var p = {};
43851         //p[this.queryParam] = q;
43852         if(this.pageSize){
43853             p.start = 0;
43854             p.limit = this.pageSize;
43855         }
43856         return p;
43857     },
43858
43859     /**
43860      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
43861      */
43862     collapse : function(){
43863         if(!this.isExpanded()){
43864             return;
43865         }
43866         this.list.hide();
43867         Roo.get(document).un('mousedown', this.collapseIf, this);
43868         Roo.get(document).un('mousewheel', this.collapseIf, this);
43869         if (!this.editable) {
43870             Roo.get(document).un('keydown', this.listKeyPress, this);
43871         }
43872         this.fireEvent('collapse', this);
43873     },
43874
43875     // private
43876     collapseIf : function(e){
43877         if(!e.within(this.wrap) && !e.within(this.list)){
43878             this.collapse();
43879         }
43880     },
43881
43882     /**
43883      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
43884      */
43885     expand : function(){
43886         if(this.isExpanded() || !this.hasFocus){
43887             return;
43888         }
43889         this.list.alignTo(this.el, this.listAlign);
43890         this.list.show();
43891         Roo.get(document).on('mousedown', this.collapseIf, this);
43892         Roo.get(document).on('mousewheel', this.collapseIf, this);
43893         if (!this.editable) {
43894             Roo.get(document).on('keydown', this.listKeyPress, this);
43895         }
43896         
43897         this.fireEvent('expand', this);
43898     },
43899
43900     // private
43901     // Implements the default empty TriggerField.onTriggerClick function
43902     onTriggerClick : function(){
43903         if(this.disabled){
43904             return;
43905         }
43906         if(this.isExpanded()){
43907             this.collapse();
43908             if (!this.blockFocus) {
43909                 this.el.focus();
43910             }
43911             
43912         }else {
43913             this.hasFocus = true;
43914             if(this.triggerAction == 'all') {
43915                 this.doQuery(this.allQuery, true);
43916             } else {
43917                 this.doQuery(this.getRawValue());
43918             }
43919             if (!this.blockFocus) {
43920                 this.el.focus();
43921             }
43922         }
43923     },
43924     listKeyPress : function(e)
43925     {
43926         //Roo.log('listkeypress');
43927         // scroll to first matching element based on key pres..
43928         if (e.isSpecialKey()) {
43929             return false;
43930         }
43931         var k = String.fromCharCode(e.getKey()).toUpperCase();
43932         //Roo.log(k);
43933         var match  = false;
43934         var csel = this.view.getSelectedNodes();
43935         var cselitem = false;
43936         if (csel.length) {
43937             var ix = this.view.indexOf(csel[0]);
43938             cselitem  = this.store.getAt(ix);
43939             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
43940                 cselitem = false;
43941             }
43942             
43943         }
43944         
43945         this.store.each(function(v) { 
43946             if (cselitem) {
43947                 // start at existing selection.
43948                 if (cselitem.id == v.id) {
43949                     cselitem = false;
43950                 }
43951                 return;
43952             }
43953                 
43954             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
43955                 match = this.store.indexOf(v);
43956                 return false;
43957             }
43958         }, this);
43959         
43960         if (match === false) {
43961             return true; // no more action?
43962         }
43963         // scroll to?
43964         this.view.select(match);
43965         var sn = Roo.get(this.view.getSelectedNodes()[0]);
43966         sn.scrollIntoView(sn.dom.parentNode, false);
43967     },
43968         cleanLeadingSpace : function()
43969         {
43970                 // override textfield strip white space (trigers set on blur)
43971         }
43972
43973     /** 
43974     * @cfg {Boolean} grow 
43975     * @hide 
43976     */
43977     /** 
43978     * @cfg {Number} growMin 
43979     * @hide 
43980     */
43981     /** 
43982     * @cfg {Number} growMax 
43983     * @hide 
43984     */
43985     /**
43986      * @hide
43987      * @method autoSize
43988      */
43989 });/*
43990  * Copyright(c) 2010-2012, Roo J Solutions Limited
43991  *
43992  * Licence LGPL
43993  *
43994  */
43995
43996 /**
43997  * @class Roo.form.ComboBoxArray
43998  * @extends Roo.form.TextField
43999  * A facebook style adder... for lists of email / people / countries  etc...
44000  * pick multiple items from a combo box, and shows each one.
44001  *
44002  *  Fred [x]  Brian [x]  [Pick another |v]
44003  *
44004  *
44005  *  For this to work: it needs various extra information
44006  *    - normal combo problay has
44007  *      name, hiddenName
44008  *    + displayField, valueField
44009  *
44010  *    For our purpose...
44011  *
44012  *
44013  *   If we change from 'extends' to wrapping...
44014  *   
44015  *  
44016  *
44017  
44018  
44019  * @constructor
44020  * Create a new ComboBoxArray.
44021  * @param {Object} config Configuration options
44022  */
44023  
44024
44025 Roo.form.ComboBoxArray = function(config)
44026 {
44027     this.addEvents({
44028         /**
44029          * @event beforeremove
44030          * Fires before remove the value from the list
44031              * @param {Roo.form.ComboBoxArray} _self This combo box array
44032              * @param {Roo.form.ComboBoxArray.Item} item removed item
44033              */
44034         'beforeremove' : true,
44035         /**
44036          * @event remove
44037          * Fires when remove the value from the list
44038              * @param {Roo.form.ComboBoxArray} _self This combo box array
44039              * @param {Roo.form.ComboBoxArray.Item} item removed item
44040              */
44041         'remove' : true
44042         
44043         
44044     });
44045     
44046     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
44047     
44048     this.items = new Roo.util.MixedCollection(false);
44049     
44050     // construct the child combo...
44051     
44052     
44053     
44054     
44055    
44056     
44057 }
44058
44059  
44060 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
44061
44062     /**
44063      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
44064      */
44065     
44066     lastData : false,
44067     
44068     // behavies liek a hiddne field
44069     inputType:      'hidden',
44070     /**
44071      * @cfg {Number} width The width of the box that displays the selected element
44072      */ 
44073     width:          300,
44074
44075     
44076     
44077     /**
44078      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
44079      */
44080     name : false,
44081     /**
44082      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
44083      */
44084     hiddenName : false,
44085       /**
44086      * @cfg {String} seperator    The value seperator normally ',' 
44087      */
44088     seperator : ',',
44089     
44090     
44091         // private the array of items that are displayed..
44092     items  : false,
44093     // private - the hidden field el.
44094     hiddenEl : false,
44095     // private - the filed el..
44096     el : false,
44097     
44098     //validateValue : function() { return true; }, // all values are ok!
44099     //onAddClick: function() { },
44100     
44101     onRender : function(ct, position) 
44102     {
44103         
44104         // create the standard hidden element
44105         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
44106         
44107         
44108         // give fake names to child combo;
44109         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
44110         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
44111         
44112         this.combo = Roo.factory(this.combo, Roo.form);
44113         this.combo.onRender(ct, position);
44114         if (typeof(this.combo.width) != 'undefined') {
44115             this.combo.onResize(this.combo.width,0);
44116         }
44117         
44118         this.combo.initEvents();
44119         
44120         // assigned so form know we need to do this..
44121         this.store          = this.combo.store;
44122         this.valueField     = this.combo.valueField;
44123         this.displayField   = this.combo.displayField ;
44124         
44125         
44126         this.combo.wrap.addClass('x-cbarray-grp');
44127         
44128         var cbwrap = this.combo.wrap.createChild(
44129             {tag: 'div', cls: 'x-cbarray-cb'},
44130             this.combo.el.dom
44131         );
44132         
44133              
44134         this.hiddenEl = this.combo.wrap.createChild({
44135             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
44136         });
44137         this.el = this.combo.wrap.createChild({
44138             tag: 'input',  type:'hidden' , name: this.name, value : ''
44139         });
44140          //   this.el.dom.removeAttribute("name");
44141         
44142         
44143         this.outerWrap = this.combo.wrap;
44144         this.wrap = cbwrap;
44145         
44146         this.outerWrap.setWidth(this.width);
44147         this.outerWrap.dom.removeChild(this.el.dom);
44148         
44149         this.wrap.dom.appendChild(this.el.dom);
44150         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
44151         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
44152         
44153         this.combo.trigger.setStyle('position','relative');
44154         this.combo.trigger.setStyle('left', '0px');
44155         this.combo.trigger.setStyle('top', '2px');
44156         
44157         this.combo.el.setStyle('vertical-align', 'text-bottom');
44158         
44159         //this.trigger.setStyle('vertical-align', 'top');
44160         
44161         // this should use the code from combo really... on('add' ....)
44162         if (this.adder) {
44163             
44164         
44165             this.adder = this.outerWrap.createChild(
44166                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
44167             var _t = this;
44168             this.adder.on('click', function(e) {
44169                 _t.fireEvent('adderclick', this, e);
44170             }, _t);
44171         }
44172         //var _t = this;
44173         //this.adder.on('click', this.onAddClick, _t);
44174         
44175         
44176         this.combo.on('select', function(cb, rec, ix) {
44177             this.addItem(rec.data);
44178             
44179             cb.setValue('');
44180             cb.el.dom.value = '';
44181             //cb.lastData = rec.data;
44182             // add to list
44183             
44184         }, this);
44185          
44186         
44187         
44188             
44189     },
44190     
44191     
44192     getName: function()
44193     {
44194         // returns hidden if it's set..
44195         if (!this.rendered) {return ''};
44196         return  this.hiddenName ? this.hiddenName : this.name;
44197         
44198     },
44199     
44200     
44201     onResize: function(w, h){
44202         
44203         return;
44204         // not sure if this is needed..
44205         //this.combo.onResize(w,h);
44206         
44207         if(typeof w != 'number'){
44208             // we do not handle it!?!?
44209             return;
44210         }
44211         var tw = this.combo.trigger.getWidth();
44212         tw += this.addicon ? this.addicon.getWidth() : 0;
44213         tw += this.editicon ? this.editicon.getWidth() : 0;
44214         var x = w - tw;
44215         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
44216             
44217         this.combo.trigger.setStyle('left', '0px');
44218         
44219         if(this.list && this.listWidth === undefined){
44220             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
44221             this.list.setWidth(lw);
44222             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
44223         }
44224         
44225     
44226         
44227     },
44228     
44229     addItem: function(rec)
44230     {
44231         var valueField = this.combo.valueField;
44232         var displayField = this.combo.displayField;
44233         
44234         if (this.items.indexOfKey(rec[valueField]) > -1) {
44235             //console.log("GOT " + rec.data.id);
44236             return;
44237         }
44238         
44239         var x = new Roo.form.ComboBoxArray.Item({
44240             //id : rec[this.idField],
44241             data : rec,
44242             displayField : displayField ,
44243             tipField : displayField ,
44244             cb : this
44245         });
44246         // use the 
44247         this.items.add(rec[valueField],x);
44248         // add it before the element..
44249         this.updateHiddenEl();
44250         x.render(this.outerWrap, this.wrap.dom);
44251         // add the image handler..
44252     },
44253     
44254     updateHiddenEl : function()
44255     {
44256         this.validate();
44257         if (!this.hiddenEl) {
44258             return;
44259         }
44260         var ar = [];
44261         var idField = this.combo.valueField;
44262         
44263         this.items.each(function(f) {
44264             ar.push(f.data[idField]);
44265         });
44266         this.hiddenEl.dom.value = ar.join(this.seperator);
44267         this.validate();
44268     },
44269     
44270     reset : function()
44271     {
44272         this.items.clear();
44273         
44274         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
44275            el.remove();
44276         });
44277         
44278         this.el.dom.value = '';
44279         if (this.hiddenEl) {
44280             this.hiddenEl.dom.value = '';
44281         }
44282         
44283     },
44284     getValue: function()
44285     {
44286         return this.hiddenEl ? this.hiddenEl.dom.value : '';
44287     },
44288     setValue: function(v) // not a valid action - must use addItems..
44289     {
44290         
44291         this.reset();
44292          
44293         if (this.store.isLocal && (typeof(v) == 'string')) {
44294             // then we can use the store to find the values..
44295             // comma seperated at present.. this needs to allow JSON based encoding..
44296             this.hiddenEl.value  = v;
44297             var v_ar = [];
44298             Roo.each(v.split(this.seperator), function(k) {
44299                 Roo.log("CHECK " + this.valueField + ',' + k);
44300                 var li = this.store.query(this.valueField, k);
44301                 if (!li.length) {
44302                     return;
44303                 }
44304                 var add = {};
44305                 add[this.valueField] = k;
44306                 add[this.displayField] = li.item(0).data[this.displayField];
44307                 
44308                 this.addItem(add);
44309             }, this) 
44310              
44311         }
44312         if (typeof(v) == 'object' ) {
44313             // then let's assume it's an array of objects..
44314             Roo.each(v, function(l) {
44315                 var add = l;
44316                 if (typeof(l) == 'string') {
44317                     add = {};
44318                     add[this.valueField] = l;
44319                     add[this.displayField] = l
44320                 }
44321                 this.addItem(add);
44322             }, this);
44323              
44324         }
44325         
44326         
44327     },
44328     setFromData: function(v)
44329     {
44330         // this recieves an object, if setValues is called.
44331         this.reset();
44332         this.el.dom.value = v[this.displayField];
44333         this.hiddenEl.dom.value = v[this.valueField];
44334         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
44335             return;
44336         }
44337         var kv = v[this.valueField];
44338         var dv = v[this.displayField];
44339         kv = typeof(kv) != 'string' ? '' : kv;
44340         dv = typeof(dv) != 'string' ? '' : dv;
44341         
44342         
44343         var keys = kv.split(this.seperator);
44344         var display = dv.split(this.seperator);
44345         for (var i = 0 ; i < keys.length; i++) {
44346             add = {};
44347             add[this.valueField] = keys[i];
44348             add[this.displayField] = display[i];
44349             this.addItem(add);
44350         }
44351       
44352         
44353     },
44354     
44355     /**
44356      * Validates the combox array value
44357      * @return {Boolean} True if the value is valid, else false
44358      */
44359     validate : function(){
44360         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
44361             this.clearInvalid();
44362             return true;
44363         }
44364         return false;
44365     },
44366     
44367     validateValue : function(value){
44368         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
44369         
44370     },
44371     
44372     /*@
44373      * overide
44374      * 
44375      */
44376     isDirty : function() {
44377         if(this.disabled) {
44378             return false;
44379         }
44380         
44381         try {
44382             var d = Roo.decode(String(this.originalValue));
44383         } catch (e) {
44384             return String(this.getValue()) !== String(this.originalValue);
44385         }
44386         
44387         var originalValue = [];
44388         
44389         for (var i = 0; i < d.length; i++){
44390             originalValue.push(d[i][this.valueField]);
44391         }
44392         
44393         return String(this.getValue()) !== String(originalValue.join(this.seperator));
44394         
44395     }
44396     
44397 });
44398
44399
44400
44401 /**
44402  * @class Roo.form.ComboBoxArray.Item
44403  * @extends Roo.BoxComponent
44404  * A selected item in the list
44405  *  Fred [x]  Brian [x]  [Pick another |v]
44406  * 
44407  * @constructor
44408  * Create a new item.
44409  * @param {Object} config Configuration options
44410  */
44411  
44412 Roo.form.ComboBoxArray.Item = function(config) {
44413     config.id = Roo.id();
44414     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
44415 }
44416
44417 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
44418     data : {},
44419     cb: false,
44420     displayField : false,
44421     tipField : false,
44422      
44423     
44424     defaultAutoCreate : {
44425         tag: 'div',
44426         cls: 'x-cbarray-item',
44427         cn : [ 
44428             { tag: 'div' },
44429             {
44430                 tag: 'img',
44431                 width:16,
44432                 height : 16,
44433                 src : Roo.BLANK_IMAGE_URL ,
44434                 align: 'center'
44435             }
44436         ]
44437         
44438     },
44439     
44440  
44441     onRender : function(ct, position)
44442     {
44443         Roo.form.Field.superclass.onRender.call(this, ct, position);
44444         
44445         if(!this.el){
44446             var cfg = this.getAutoCreate();
44447             this.el = ct.createChild(cfg, position);
44448         }
44449         
44450         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
44451         
44452         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
44453             this.cb.renderer(this.data) :
44454             String.format('{0}',this.data[this.displayField]);
44455         
44456             
44457         this.el.child('div').dom.setAttribute('qtip',
44458                         String.format('{0}',this.data[this.tipField])
44459         );
44460         
44461         this.el.child('img').on('click', this.remove, this);
44462         
44463     },
44464    
44465     remove : function()
44466     {
44467         if(this.cb.disabled){
44468             return;
44469         }
44470         
44471         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
44472             this.cb.items.remove(this);
44473             this.el.child('img').un('click', this.remove, this);
44474             this.el.remove();
44475             this.cb.updateHiddenEl();
44476
44477             this.cb.fireEvent('remove', this.cb, this);
44478         }
44479         
44480     }
44481 });/*
44482  * RooJS Library 1.1.1
44483  * Copyright(c) 2008-2011  Alan Knowles
44484  *
44485  * License - LGPL
44486  */
44487  
44488
44489 /**
44490  * @class Roo.form.ComboNested
44491  * @extends Roo.form.ComboBox
44492  * A combobox for that allows selection of nested items in a list,
44493  * eg.
44494  *
44495  *  Book
44496  *    -> red
44497  *    -> green
44498  *  Table
44499  *    -> square
44500  *      ->red
44501  *      ->green
44502  *    -> rectangle
44503  *      ->green
44504  *      
44505  * 
44506  * @constructor
44507  * Create a new ComboNested
44508  * @param {Object} config Configuration options
44509  */
44510 Roo.form.ComboNested = function(config){
44511     Roo.form.ComboCheck.superclass.constructor.call(this, config);
44512     // should verify some data...
44513     // like
44514     // hiddenName = required..
44515     // displayField = required
44516     // valudField == required
44517     var req= [ 'hiddenName', 'displayField', 'valueField' ];
44518     var _t = this;
44519     Roo.each(req, function(e) {
44520         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
44521             throw "Roo.form.ComboNested : missing value for: " + e;
44522         }
44523     });
44524      
44525     
44526 };
44527
44528 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
44529    
44530     /*
44531      * @config {Number} max Number of columns to show
44532      */
44533     
44534     maxColumns : 3,
44535    
44536     list : null, // the outermost div..
44537     innerLists : null, // the
44538     views : null,
44539     stores : null,
44540     // private
44541     loadingChildren : false,
44542     
44543     onRender : function(ct, position)
44544     {
44545         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
44546         
44547         if(this.hiddenName){
44548             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
44549                     'before', true);
44550             this.hiddenField.value =
44551                 this.hiddenValue !== undefined ? this.hiddenValue :
44552                 this.value !== undefined ? this.value : '';
44553
44554             // prevent input submission
44555             this.el.dom.removeAttribute('name');
44556              
44557              
44558         }
44559         
44560         if(Roo.isGecko){
44561             this.el.dom.setAttribute('autocomplete', 'off');
44562         }
44563
44564         var cls = 'x-combo-list';
44565
44566         this.list = new Roo.Layer({
44567             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
44568         });
44569
44570         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
44571         this.list.setWidth(lw);
44572         this.list.swallowEvent('mousewheel');
44573         this.assetHeight = 0;
44574
44575         if(this.title){
44576             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
44577             this.assetHeight += this.header.getHeight();
44578         }
44579         this.innerLists = [];
44580         this.views = [];
44581         this.stores = [];
44582         for (var i =0 ; i < this.maxColumns; i++) {
44583             this.onRenderList( cls, i);
44584         }
44585         
44586         // always needs footer, as we are going to have an 'OK' button.
44587         this.footer = this.list.createChild({cls:cls+'-ft'});
44588         this.pageTb = new Roo.Toolbar(this.footer);  
44589         var _this = this;
44590         this.pageTb.add(  {
44591             
44592             text: 'Done',
44593             handler: function()
44594             {
44595                 _this.collapse();
44596             }
44597         });
44598         
44599         if ( this.allowBlank && !this.disableClear) {
44600             
44601             this.pageTb.add(new Roo.Toolbar.Fill(), {
44602                 cls: 'x-btn-icon x-btn-clear',
44603                 text: '&#160;',
44604                 handler: function()
44605                 {
44606                     _this.collapse();
44607                     _this.clearValue();
44608                     _this.onSelect(false, -1);
44609                 }
44610             });
44611         }
44612         if (this.footer) {
44613             this.assetHeight += this.footer.getHeight();
44614         }
44615         
44616     },
44617     onRenderList : function (  cls, i)
44618     {
44619         
44620         var lw = Math.floor(
44621                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
44622         );
44623         
44624         this.list.setWidth(lw); // default to '1'
44625
44626         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
44627         //il.on('mouseover', this.onViewOver, this, { list:  i });
44628         //il.on('mousemove', this.onViewMove, this, { list:  i });
44629         il.setWidth(lw);
44630         il.setStyle({ 'overflow-x' : 'hidden'});
44631
44632         if(!this.tpl){
44633             this.tpl = new Roo.Template({
44634                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
44635                 isEmpty: function (value, allValues) {
44636                     //Roo.log(value);
44637                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
44638                     return dl ? 'has-children' : 'no-children'
44639                 }
44640             });
44641         }
44642         
44643         var store  = this.store;
44644         if (i > 0) {
44645             store  = new Roo.data.SimpleStore({
44646                 //fields : this.store.reader.meta.fields,
44647                 reader : this.store.reader,
44648                 data : [ ]
44649             });
44650         }
44651         this.stores[i]  = store;
44652                   
44653         var view = this.views[i] = new Roo.View(
44654             il,
44655             this.tpl,
44656             {
44657                 singleSelect:true,
44658                 store: store,
44659                 selectedClass: this.selectedClass
44660             }
44661         );
44662         view.getEl().setWidth(lw);
44663         view.getEl().setStyle({
44664             position: i < 1 ? 'relative' : 'absolute',
44665             top: 0,
44666             left: (i * lw ) + 'px',
44667             display : i > 0 ? 'none' : 'block'
44668         });
44669         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
44670         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
44671         //view.on('click', this.onViewClick, this, { list : i });
44672
44673         store.on('beforeload', this.onBeforeLoad, this);
44674         store.on('load',  this.onLoad, this, { list  : i});
44675         store.on('loadexception', this.onLoadException, this);
44676
44677         // hide the other vies..
44678         
44679         
44680         
44681     },
44682       
44683     restrictHeight : function()
44684     {
44685         var mh = 0;
44686         Roo.each(this.innerLists, function(il,i) {
44687             var el = this.views[i].getEl();
44688             el.dom.style.height = '';
44689             var inner = el.dom;
44690             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
44691             // only adjust heights on other ones..
44692             mh = Math.max(h, mh);
44693             if (i < 1) {
44694                 
44695                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44696                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44697                
44698             }
44699             
44700             
44701         }, this);
44702         
44703         this.list.beginUpdate();
44704         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
44705         this.list.alignTo(this.el, this.listAlign);
44706         this.list.endUpdate();
44707         
44708     },
44709      
44710     
44711     // -- store handlers..
44712     // private
44713     onBeforeLoad : function()
44714     {
44715         if(!this.hasFocus){
44716             return;
44717         }
44718         this.innerLists[0].update(this.loadingText ?
44719                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
44720         this.restrictHeight();
44721         this.selectedIndex = -1;
44722     },
44723     // private
44724     onLoad : function(a,b,c,d)
44725     {
44726         if (!this.loadingChildren) {
44727             // then we are loading the top level. - hide the children
44728             for (var i = 1;i < this.views.length; i++) {
44729                 this.views[i].getEl().setStyle({ display : 'none' });
44730             }
44731             var lw = Math.floor(
44732                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
44733             );
44734         
44735              this.list.setWidth(lw); // default to '1'
44736
44737             
44738         }
44739         if(!this.hasFocus){
44740             return;
44741         }
44742         
44743         if(this.store.getCount() > 0) {
44744             this.expand();
44745             this.restrictHeight();   
44746         } else {
44747             this.onEmptyResults();
44748         }
44749         
44750         if (!this.loadingChildren) {
44751             this.selectActive();
44752         }
44753         /*
44754         this.stores[1].loadData([]);
44755         this.stores[2].loadData([]);
44756         this.views
44757         */    
44758     
44759         //this.el.focus();
44760     },
44761     
44762     
44763     // private
44764     onLoadException : function()
44765     {
44766         this.collapse();
44767         Roo.log(this.store.reader.jsonData);
44768         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
44769             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
44770         }
44771         
44772         
44773     },
44774     // no cleaning of leading spaces on blur here.
44775     cleanLeadingSpace : function(e) { },
44776     
44777
44778     onSelectChange : function (view, sels, opts )
44779     {
44780         var ix = view.getSelectedIndexes();
44781          
44782         if (opts.list > this.maxColumns - 2) {
44783             if (view.store.getCount()<  1) {
44784                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
44785
44786             } else  {
44787                 if (ix.length) {
44788                     // used to clear ?? but if we are loading unselected 
44789                     this.setFromData(view.store.getAt(ix[0]).data);
44790                 }
44791                 
44792             }
44793             
44794             return;
44795         }
44796         
44797         if (!ix.length) {
44798             // this get's fired when trigger opens..
44799            // this.setFromData({});
44800             var str = this.stores[opts.list+1];
44801             str.data.clear(); // removeall wihtout the fire events..
44802             return;
44803         }
44804         
44805         var rec = view.store.getAt(ix[0]);
44806          
44807         this.setFromData(rec.data);
44808         this.fireEvent('select', this, rec, ix[0]);
44809         
44810         var lw = Math.floor(
44811              (
44812                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
44813              ) / this.maxColumns
44814         );
44815         this.loadingChildren = true;
44816         this.stores[opts.list+1].loadDataFromChildren( rec );
44817         this.loadingChildren = false;
44818         var dl = this.stores[opts.list+1]. getTotalCount();
44819         
44820         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
44821         
44822         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
44823         for (var i = opts.list+2; i < this.views.length;i++) {
44824             this.views[i].getEl().setStyle({ display : 'none' });
44825         }
44826         
44827         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
44828         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
44829         
44830         if (this.isLoading) {
44831            // this.selectActive(opts.list);
44832         }
44833          
44834     },
44835     
44836     
44837     
44838     
44839     onDoubleClick : function()
44840     {
44841         this.collapse(); //??
44842     },
44843     
44844      
44845     
44846     
44847     
44848     // private
44849     recordToStack : function(store, prop, value, stack)
44850     {
44851         var cstore = new Roo.data.SimpleStore({
44852             //fields : this.store.reader.meta.fields, // we need array reader.. for
44853             reader : this.store.reader,
44854             data : [ ]
44855         });
44856         var _this = this;
44857         var record  = false;
44858         var srec = false;
44859         if(store.getCount() < 1){
44860             return false;
44861         }
44862         store.each(function(r){
44863             if(r.data[prop] == value){
44864                 record = r;
44865             srec = r;
44866                 return false;
44867             }
44868             if (r.data.cn && r.data.cn.length) {
44869                 cstore.loadDataFromChildren( r);
44870                 var cret = _this.recordToStack(cstore, prop, value, stack);
44871                 if (cret !== false) {
44872                     record = cret;
44873                     srec = r;
44874                     return false;
44875                 }
44876             }
44877              
44878             return true;
44879         });
44880         if (record == false) {
44881             return false
44882         }
44883         stack.unshift(srec);
44884         return record;
44885     },
44886     
44887     /*
44888      * find the stack of stores that match our value.
44889      *
44890      * 
44891      */
44892     
44893     selectActive : function ()
44894     {
44895         // if store is not loaded, then we will need to wait for that to happen first.
44896         var stack = [];
44897         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
44898         for (var i = 0; i < stack.length; i++ ) {
44899             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
44900         }
44901         
44902     }
44903         
44904          
44905     
44906     
44907     
44908     
44909 });/*
44910  * Based on:
44911  * Ext JS Library 1.1.1
44912  * Copyright(c) 2006-2007, Ext JS, LLC.
44913  *
44914  * Originally Released Under LGPL - original licence link has changed is not relivant.
44915  *
44916  * Fork - LGPL
44917  * <script type="text/javascript">
44918  */
44919 /**
44920  * @class Roo.form.Checkbox
44921  * @extends Roo.form.Field
44922  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
44923  * @constructor
44924  * Creates a new Checkbox
44925  * @param {Object} config Configuration options
44926  */
44927 Roo.form.Checkbox = function(config){
44928     Roo.form.Checkbox.superclass.constructor.call(this, config);
44929     this.addEvents({
44930         /**
44931          * @event check
44932          * Fires when the checkbox is checked or unchecked.
44933              * @param {Roo.form.Checkbox} this This checkbox
44934              * @param {Boolean} checked The new checked value
44935              */
44936         check : true
44937     });
44938 };
44939
44940 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
44941     /**
44942      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
44943      */
44944     focusClass : undefined,
44945     /**
44946      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
44947      */
44948     fieldClass: "x-form-field",
44949     /**
44950      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
44951      */
44952     checked: false,
44953     /**
44954      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
44955      * {tag: "input", type: "checkbox", autocomplete: "off"})
44956      */
44957     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
44958     /**
44959      * @cfg {String} boxLabel The text that appears beside the checkbox
44960      */
44961     boxLabel : "",
44962     /**
44963      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
44964      */  
44965     inputValue : '1',
44966     /**
44967      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
44968      */
44969      valueOff: '0', // value when not checked..
44970
44971     actionMode : 'viewEl', 
44972     //
44973     // private
44974     itemCls : 'x-menu-check-item x-form-item',
44975     groupClass : 'x-menu-group-item',
44976     inputType : 'hidden',
44977     
44978     
44979     inSetChecked: false, // check that we are not calling self...
44980     
44981     inputElement: false, // real input element?
44982     basedOn: false, // ????
44983     
44984     isFormField: true, // not sure where this is needed!!!!
44985
44986     onResize : function(){
44987         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
44988         if(!this.boxLabel){
44989             this.el.alignTo(this.wrap, 'c-c');
44990         }
44991     },
44992
44993     initEvents : function(){
44994         Roo.form.Checkbox.superclass.initEvents.call(this);
44995         this.el.on("click", this.onClick,  this);
44996         this.el.on("change", this.onClick,  this);
44997     },
44998
44999
45000     getResizeEl : function(){
45001         return this.wrap;
45002     },
45003
45004     getPositionEl : function(){
45005         return this.wrap;
45006     },
45007
45008     // private
45009     onRender : function(ct, position){
45010         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
45011         /*
45012         if(this.inputValue !== undefined){
45013             this.el.dom.value = this.inputValue;
45014         }
45015         */
45016         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
45017         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
45018         var viewEl = this.wrap.createChild({ 
45019             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
45020         this.viewEl = viewEl;   
45021         this.wrap.on('click', this.onClick,  this); 
45022         
45023         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
45024         this.el.on('propertychange', this.setFromHidden,  this);  //ie
45025         
45026         
45027         
45028         if(this.boxLabel){
45029             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
45030         //    viewEl.on('click', this.onClick,  this); 
45031         }
45032         //if(this.checked){
45033             this.setChecked(this.checked);
45034         //}else{
45035             //this.checked = this.el.dom;
45036         //}
45037
45038     },
45039
45040     // private
45041     initValue : Roo.emptyFn,
45042
45043     /**
45044      * Returns the checked state of the checkbox.
45045      * @return {Boolean} True if checked, else false
45046      */
45047     getValue : function(){
45048         if(this.el){
45049             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
45050         }
45051         return this.valueOff;
45052         
45053     },
45054
45055         // private
45056     onClick : function(){ 
45057         if (this.disabled) {
45058             return;
45059         }
45060         this.setChecked(!this.checked);
45061
45062         //if(this.el.dom.checked != this.checked){
45063         //    this.setValue(this.el.dom.checked);
45064        // }
45065     },
45066
45067     /**
45068      * Sets the checked state of the checkbox.
45069      * On is always based on a string comparison between inputValue and the param.
45070      * @param {Boolean/String} value - the value to set 
45071      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
45072      */
45073     setValue : function(v,suppressEvent){
45074         
45075         
45076         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
45077         //if(this.el && this.el.dom){
45078         //    this.el.dom.checked = this.checked;
45079         //    this.el.dom.defaultChecked = this.checked;
45080         //}
45081         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
45082         //this.fireEvent("check", this, this.checked);
45083     },
45084     // private..
45085     setChecked : function(state,suppressEvent)
45086     {
45087         if (this.inSetChecked) {
45088             this.checked = state;
45089             return;
45090         }
45091         
45092     
45093         if(this.wrap){
45094             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
45095         }
45096         this.checked = state;
45097         if(suppressEvent !== true){
45098             this.fireEvent('check', this, state);
45099         }
45100         this.inSetChecked = true;
45101                  
45102                 this.el.dom.value = state ? this.inputValue : this.valueOff;
45103                  
45104         this.inSetChecked = false;
45105         
45106     },
45107     // handle setting of hidden value by some other method!!?!?
45108     setFromHidden: function()
45109     {
45110         if(!this.el){
45111             return;
45112         }
45113         //console.log("SET FROM HIDDEN");
45114         //alert('setFrom hidden');
45115         this.setValue(this.el.dom.value);
45116     },
45117     
45118     onDestroy : function()
45119     {
45120         if(this.viewEl){
45121             Roo.get(this.viewEl).remove();
45122         }
45123          
45124         Roo.form.Checkbox.superclass.onDestroy.call(this);
45125     },
45126     
45127     setBoxLabel : function(str)
45128     {
45129         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
45130     }
45131
45132 });/*
45133  * Based on:
45134  * Ext JS Library 1.1.1
45135  * Copyright(c) 2006-2007, Ext JS, LLC.
45136  *
45137  * Originally Released Under LGPL - original licence link has changed is not relivant.
45138  *
45139  * Fork - LGPL
45140  * <script type="text/javascript">
45141  */
45142  
45143 /**
45144  * @class Roo.form.Radio
45145  * @extends Roo.form.Checkbox
45146  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
45147  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
45148  * @constructor
45149  * Creates a new Radio
45150  * @param {Object} config Configuration options
45151  */
45152 Roo.form.Radio = function(){
45153     Roo.form.Radio.superclass.constructor.apply(this, arguments);
45154 };
45155 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
45156     inputType: 'radio',
45157
45158     /**
45159      * If this radio is part of a group, it will return the selected value
45160      * @return {String}
45161      */
45162     getGroupValue : function(){
45163         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
45164     },
45165     
45166     
45167     onRender : function(ct, position){
45168         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
45169         
45170         if(this.inputValue !== undefined){
45171             this.el.dom.value = this.inputValue;
45172         }
45173          
45174         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
45175         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
45176         //var viewEl = this.wrap.createChild({ 
45177         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
45178         //this.viewEl = viewEl;   
45179         //this.wrap.on('click', this.onClick,  this); 
45180         
45181         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
45182         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
45183         
45184         
45185         
45186         if(this.boxLabel){
45187             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
45188         //    viewEl.on('click', this.onClick,  this); 
45189         }
45190          if(this.checked){
45191             this.el.dom.checked =   'checked' ;
45192         }
45193          
45194     },
45195     /**
45196      * Sets the checked state of the checkbox.
45197      * On is always based on a string comparison between inputValue and the param.
45198      * @param {Boolean/String} value - the value to set 
45199      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
45200      */
45201     setValue : function(v,suppressEvent){
45202         
45203         
45204         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
45205         //if(this.el && this.el.dom){
45206         //    this.el.dom.checked = this.checked;
45207         //    this.el.dom.defaultChecked = this.checked;
45208         //}
45209         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
45210         
45211         this.el.dom.form[this.name].value = v;
45212      
45213         //this.fireEvent("check", this, this.checked);
45214     },
45215     // private..
45216     setChecked : function(state,suppressEvent)
45217     {
45218          
45219         if(this.wrap){
45220             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
45221         }
45222         this.checked = state;
45223         if(suppressEvent !== true){
45224             this.fireEvent('check', this, state);
45225         }
45226                  
45227                   
45228        
45229         
45230     },
45231     reset : function(){
45232         // this.setValue(this.resetValue);
45233         //this.originalValue = this.getValue();
45234         this.clearInvalid();
45235     } 
45236     
45237 });Roo.rtf = {}; // namespace
45238 Roo.rtf.Hex = function(hex)
45239 {
45240     this.hexstr = hex;
45241 };
45242 Roo.rtf.Paragraph = function(opts)
45243 {
45244     this.content = []; ///??? is that used?
45245 };Roo.rtf.Span = function(opts)
45246 {
45247     this.value = opts.value;
45248 };
45249
45250 Roo.rtf.Group = function(parent)
45251 {
45252     // we dont want to acutally store parent - it will make debug a nightmare..
45253     this.content = [];
45254     this.cn  = [];
45255      
45256        
45257     
45258 };
45259
45260 Roo.rtf.Group.prototype = {
45261     ignorable : false,
45262     content: false,
45263     cn: false,
45264     addContent : function(node) {
45265         // could set styles...
45266         this.content.push(node);
45267     },
45268     addChild : function(cn)
45269     {
45270         this.cn.push(cn);
45271     },
45272     // only for images really...
45273     toDataURL : function()
45274     {
45275         var mimetype = false;
45276         switch(true) {
45277             case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0: 
45278                 mimetype = "image/png";
45279                 break;
45280              case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
45281                 mimetype = "image/jpeg";
45282                 break;
45283             default :
45284                 return 'about:blank'; // ?? error?
45285         }
45286         
45287         
45288         var hexstring = this.content[this.content.length-1].value;
45289         
45290         return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
45291             return String.fromCharCode(parseInt(a, 16));
45292         }).join(""));
45293     }
45294     
45295 };
45296 // this looks like it's normally the {rtf{ .... }}
45297 Roo.rtf.Document = function()
45298 {
45299     // we dont want to acutally store parent - it will make debug a nightmare..
45300     this.rtlch  = [];
45301     this.content = [];
45302     this.cn = [];
45303     
45304 };
45305 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, { 
45306     addChild : function(cn)
45307     {
45308         this.cn.push(cn);
45309         switch(cn.type) {
45310             case 'rtlch': // most content seems to be inside this??
45311             case 'listtext':
45312             case 'shpinst':
45313                 this.rtlch.push(cn);
45314                 return;
45315             default:
45316                 this[cn.type] = cn;
45317         }
45318         
45319     },
45320     
45321     getElementsByType : function(type)
45322     {
45323         var ret =  [];
45324         this._getElementsByType(type, ret, this.cn, 'rtf');
45325         return ret;
45326     },
45327     _getElementsByType : function (type, ret, search_array, path)
45328     {
45329         search_array.forEach(function(n,i) {
45330             if (n.type == type) {
45331                 n.path = path + '/' + n.type + ':' + i;
45332                 ret.push(n);
45333             }
45334             if (n.cn.length > 0) {
45335                 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
45336             }
45337         },this);
45338     }
45339     
45340 });
45341  
45342 Roo.rtf.Ctrl = function(opts)
45343 {
45344     this.value = opts.value;
45345     this.param = opts.param;
45346 };
45347 /**
45348  *
45349  *
45350  * based on this https://github.com/iarna/rtf-parser
45351  * it's really only designed to extract pict from pasted RTF 
45352  *
45353  * usage:
45354  *
45355  *  var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
45356  *  
45357  *
45358  */
45359
45360  
45361
45362
45363
45364 Roo.rtf.Parser = function(text) {
45365     //super({objectMode: true})
45366     this.text = '';
45367     this.parserState = this.parseText;
45368     
45369     // these are for interpeter...
45370     this.doc = {};
45371     ///this.parserState = this.parseTop
45372     this.groupStack = [];
45373     this.hexStore = [];
45374     this.doc = false;
45375     
45376     this.groups = []; // where we put the return.
45377     
45378     for (var ii = 0; ii < text.length; ++ii) {
45379         ++this.cpos;
45380         
45381         if (text[ii] === '\n') {
45382             ++this.row;
45383             this.col = 1;
45384         } else {
45385             ++this.col;
45386         }
45387         this.parserState(text[ii]);
45388     }
45389     
45390     
45391     
45392 };
45393 Roo.rtf.Parser.prototype = {
45394     text : '', // string being parsed..
45395     controlWord : '',
45396     controlWordParam :  '',
45397     hexChar : '',
45398     doc : false,
45399     group: false,
45400     groupStack : false,
45401     hexStore : false,
45402     
45403     
45404     cpos : 0, 
45405     row : 1, // reportin?
45406     col : 1, //
45407
45408      
45409     push : function (el)
45410     {
45411         var m = 'cmd'+ el.type;
45412         if (typeof(this[m]) == 'undefined') {
45413             Roo.log('invalid cmd:' + el.type);
45414             return;
45415         }
45416         this[m](el);
45417         //Roo.log(el);
45418     },
45419     flushHexStore : function()
45420     {
45421         if (this.hexStore.length < 1) {
45422             return;
45423         }
45424         var hexstr = this.hexStore.map(
45425             function(cmd) {
45426                 return cmd.value;
45427         }).join('');
45428         
45429         this.group.addContent( new Roo.rtf.Hex( hexstr ));
45430               
45431             
45432         this.hexStore.splice(0)
45433         
45434     },
45435     
45436     cmdgroupstart : function()
45437     {
45438         this.flushHexStore();
45439         if (this.group) {
45440             this.groupStack.push(this.group);
45441         }
45442          // parent..
45443         if (this.doc === false) {
45444             this.group = this.doc = new Roo.rtf.Document();
45445             return;
45446             
45447         }
45448         this.group = new Roo.rtf.Group(this.group);
45449     },
45450     cmdignorable : function()
45451     {
45452         this.flushHexStore();
45453         this.group.ignorable = true;
45454     },
45455     cmdendparagraph : function()
45456     {
45457         this.flushHexStore();
45458         this.group.addContent(new Roo.rtf.Paragraph());
45459     },
45460     cmdgroupend : function ()
45461     {
45462         this.flushHexStore();
45463         var endingGroup = this.group;
45464         
45465         
45466         this.group = this.groupStack.pop();
45467         if (this.group) {
45468             this.group.addChild(endingGroup);
45469         }
45470         
45471         
45472         
45473         var doc = this.group || this.doc;
45474         //if (endingGroup instanceof FontTable) {
45475         //  doc.fonts = endingGroup.table
45476         //} else if (endingGroup instanceof ColorTable) {
45477         //  doc.colors = endingGroup.table
45478         //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
45479         if (endingGroup.ignorable === false) {
45480             //code
45481             this.groups.push(endingGroup);
45482            // Roo.log( endingGroup );
45483         }
45484             //Roo.each(endingGroup.content, function(item)) {
45485             //    doc.addContent(item);
45486             //}
45487             //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
45488         //}
45489     },
45490     cmdtext : function (cmd)
45491     {
45492         this.flushHexStore();
45493         if (!this.group) { // an RTF fragment, missing the {\rtf1 header
45494             //this.group = this.doc
45495             return;  // we really don't care about stray text...
45496         }
45497         this.group.addContent(new Roo.rtf.Span(cmd));
45498     },
45499     cmdcontrolword : function (cmd)
45500     {
45501         this.flushHexStore();
45502         if (!this.group.type) {
45503             this.group.type = cmd.value;
45504             return;
45505         }
45506         this.group.addContent(new Roo.rtf.Ctrl(cmd));
45507         // we actually don't care about ctrl words...
45508         return ;
45509         /*
45510         var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
45511         if (this[method]) {
45512             this[method](cmd.param)
45513         } else {
45514             if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
45515         }
45516         */
45517     },
45518     cmdhexchar : function(cmd) {
45519         this.hexStore.push(cmd);
45520     },
45521     cmderror : function(cmd) {
45522         throw cmd.value;
45523     },
45524     
45525     /*
45526       _flush (done) {
45527         if (this.text !== '\u0000') this.emitText()
45528         done()
45529       }
45530       */
45531       
45532       
45533     parseText : function(c)
45534     {
45535         if (c === '\\') {
45536             this.parserState = this.parseEscapes;
45537         } else if (c === '{') {
45538             this.emitStartGroup();
45539         } else if (c === '}') {
45540             this.emitEndGroup();
45541         } else if (c === '\x0A' || c === '\x0D') {
45542             // cr/lf are noise chars
45543         } else {
45544             this.text += c;
45545         }
45546     },
45547     
45548     parseEscapes: function (c)
45549     {
45550         if (c === '\\' || c === '{' || c === '}') {
45551             this.text += c;
45552             this.parserState = this.parseText;
45553         } else {
45554             this.parserState = this.parseControlSymbol;
45555             this.parseControlSymbol(c);
45556         }
45557     },
45558     parseControlSymbol: function(c)
45559     {
45560         if (c === '~') {
45561             this.text += '\u00a0'; // nbsp
45562             this.parserState = this.parseText
45563         } else if (c === '-') {
45564              this.text += '\u00ad'; // soft hyphen
45565         } else if (c === '_') {
45566             this.text += '\u2011'; // non-breaking hyphen
45567         } else if (c === '*') {
45568             this.emitIgnorable();
45569             this.parserState = this.parseText;
45570         } else if (c === "'") {
45571             this.parserState = this.parseHexChar;
45572         } else if (c === '|') { // formula cacter
45573             this.emitFormula();
45574             this.parserState = this.parseText;
45575         } else if (c === ':') { // subentry in an index entry
45576             this.emitIndexSubEntry();
45577             this.parserState = this.parseText;
45578         } else if (c === '\x0a') {
45579             this.emitEndParagraph();
45580             this.parserState = this.parseText;
45581         } else if (c === '\x0d') {
45582             this.emitEndParagraph();
45583             this.parserState = this.parseText;
45584         } else {
45585             this.parserState = this.parseControlWord;
45586             this.parseControlWord(c);
45587         }
45588     },
45589     parseHexChar: function (c)
45590     {
45591         if (/^[A-Fa-f0-9]$/.test(c)) {
45592             this.hexChar += c;
45593             if (this.hexChar.length >= 2) {
45594               this.emitHexChar();
45595               this.parserState = this.parseText;
45596             }
45597             return;
45598         }
45599         this.emitError("Invalid character \"" + c + "\" in hex literal.");
45600         this.parserState = this.parseText;
45601         
45602     },
45603     parseControlWord : function(c)
45604     {
45605         if (c === ' ') {
45606             this.emitControlWord();
45607             this.parserState = this.parseText;
45608         } else if (/^[-\d]$/.test(c)) {
45609             this.parserState = this.parseControlWordParam;
45610             this.controlWordParam += c;
45611         } else if (/^[A-Za-z]$/.test(c)) {
45612           this.controlWord += c;
45613         } else {
45614           this.emitControlWord();
45615           this.parserState = this.parseText;
45616           this.parseText(c);
45617         }
45618     },
45619     parseControlWordParam : function (c) {
45620         if (/^\d$/.test(c)) {
45621           this.controlWordParam += c;
45622         } else if (c === ' ') {
45623           this.emitControlWord();
45624           this.parserState = this.parseText;
45625         } else {
45626           this.emitControlWord();
45627           this.parserState = this.parseText;
45628           this.parseText(c);
45629         }
45630     },
45631     
45632     
45633     
45634     
45635     emitText : function () {
45636         if (this.text === '') {
45637             return;
45638         }
45639         this.push({
45640             type: 'text',
45641             value: this.text,
45642             pos: this.cpos,
45643             row: this.row,
45644             col: this.col
45645         });
45646         this.text = ''
45647     },
45648     emitControlWord : function ()
45649     {
45650         this.emitText();
45651         if (this.controlWord === '') {
45652             // do we want to track this - it seems just to cause problems.
45653             //this.emitError('empty control word');
45654         } else {
45655             this.push({
45656                   type: 'controlword',
45657                   value: this.controlWord,
45658                   param: this.controlWordParam !== '' && Number(this.controlWordParam),
45659                   pos: this.cpos,
45660                   row: this.row,
45661                   col: this.col
45662             });
45663         }
45664         this.controlWord = '';
45665         this.controlWordParam = '';
45666     },
45667     emitStartGroup : function ()
45668     {
45669         this.emitText();
45670         this.push({
45671             type: 'groupstart',
45672             pos: this.cpos,
45673             row: this.row,
45674             col: this.col
45675         });
45676     },
45677     emitEndGroup : function ()
45678     {
45679         this.emitText();
45680         this.push({
45681             type: 'groupend',
45682             pos: this.cpos,
45683             row: this.row,
45684             col: this.col
45685         });
45686     },
45687     emitIgnorable : function ()
45688     {
45689         this.emitText();
45690         this.push({
45691             type: 'ignorable',
45692             pos: this.cpos,
45693             row: this.row,
45694             col: this.col
45695         });
45696     },
45697     emitHexChar : function ()
45698     {
45699         this.emitText();
45700         this.push({
45701             type: 'hexchar',
45702             value: this.hexChar,
45703             pos: this.cpos,
45704             row: this.row,
45705             col: this.col
45706         });
45707         this.hexChar = ''
45708     },
45709     emitError : function (message)
45710     {
45711       this.emitText();
45712       this.push({
45713             type: 'error',
45714             value: message,
45715             row: this.row,
45716             col: this.col,
45717             char: this.cpos //,
45718             //stack: new Error().stack
45719         });
45720     },
45721     emitEndParagraph : function () {
45722         this.emitText();
45723         this.push({
45724             type: 'endparagraph',
45725             pos: this.cpos,
45726             row: this.row,
45727             col: this.col
45728         });
45729     }
45730      
45731 } ;
45732 Roo.htmleditor = {};
45733  
45734 /**
45735  * @class Roo.htmleditor.Filter
45736  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
45737  * @cfg {DomElement} node The node to iterate and filter
45738  * @cfg {boolean|String|Array} tag Tags to replace 
45739  * @constructor
45740  * Create a new Filter.
45741  * @param {Object} config Configuration options
45742  */
45743
45744
45745
45746 Roo.htmleditor.Filter = function(cfg) {
45747     Roo.apply(this.cfg);
45748     // this does not actually call walk as it's really just a abstract class
45749 }
45750
45751
45752 Roo.htmleditor.Filter.prototype = {
45753     
45754     node: false,
45755     
45756     tag: false,
45757
45758     // overrride to do replace comments.
45759     replaceComment : false,
45760     
45761     // overrride to do replace or do stuff with tags..
45762     replaceTag : false,
45763     
45764     walk : function(dom)
45765     {
45766         Roo.each( Array.from(dom.childNodes), function( e ) {
45767             switch(true) {
45768                 
45769                 case e.nodeType == 8 &&  this.replaceComment  !== false: // comment
45770                     this.replaceComment(e);
45771                     return;
45772                 
45773                 case e.nodeType != 1: //not a node.
45774                     return;
45775                 
45776                 case this.tag === true: // everything
45777                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1:
45778                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":":
45779                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
45780                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
45781                     if (this.replaceTag && false === this.replaceTag(e)) {
45782                         return;
45783                     }
45784                     if (e.hasChildNodes()) {
45785                         this.walk(e);
45786                     }
45787                     return;
45788                 
45789                 default:    // tags .. that do not match.
45790                     if (e.hasChildNodes()) {
45791                         this.walk(e);
45792                     }
45793             }
45794             
45795         }, this);
45796         
45797     },
45798     
45799     
45800     removeNodeKeepChildren : function( node)
45801     {
45802     
45803         ar = Array.from(node.childNodes);
45804         for (var i = 0; i < ar.length; i++) {
45805          
45806             node.removeChild(ar[i]);
45807             // what if we need to walk these???
45808             node.parentNode.insertBefore(ar[i], node);
45809            
45810         }
45811         node.parentNode.removeChild(node);
45812     },
45813
45814     searchTag : function(dom)
45815     {
45816         if(this.tag === false) {
45817             return;
45818         }
45819
45820         var els = dom.getElementsByTagName(this.tag);
45821
45822         Roo.each(Array.from(els), function(e){
45823             if(e.parentNode == null) {
45824                 return;
45825             }
45826             if(this.replaceTag) {
45827                 this.replaceTag(e);
45828             }
45829         }, this);
45830     }
45831 }; 
45832
45833 /**
45834  * @class Roo.htmleditor.FilterAttributes
45835  * clean attributes and  styles including http:// etc.. in attribute
45836  * @constructor
45837 * Run a new Attribute Filter
45838 * @param {Object} config Configuration options
45839  */
45840 Roo.htmleditor.FilterAttributes = function(cfg)
45841 {
45842     Roo.apply(this, cfg);
45843     this.attrib_black = this.attrib_black || [];
45844     this.attrib_white = this.attrib_white || [];
45845
45846     this.attrib_clean = this.attrib_clean || [];
45847     this.style_white = this.style_white || [];
45848     this.style_black = this.style_black || [];
45849     this.walk(cfg.node);
45850 }
45851
45852 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
45853 {
45854     tag: true, // all tags
45855     
45856     attrib_black : false, // array
45857     attrib_clean : false,
45858     attrib_white : false,
45859
45860     style_white : false,
45861     style_black : false,
45862      
45863      
45864     replaceTag : function(node)
45865     {
45866         if (!node.attributes || !node.attributes.length) {
45867             return true;
45868         }
45869         
45870         for (var i = node.attributes.length-1; i > -1 ; i--) {
45871             var a = node.attributes[i];
45872             //console.log(a);
45873             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
45874                 node.removeAttribute(a.name);
45875                 continue;
45876             }
45877             
45878             
45879             
45880             if (a.name.toLowerCase().substr(0,2)=='on')  {
45881                 node.removeAttribute(a.name);
45882                 continue;
45883             }
45884             
45885             
45886             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
45887                 node.removeAttribute(a.name);
45888                 continue;
45889             }
45890             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
45891                 this.cleanAttr(node,a.name,a.value); // fixme..
45892                 continue;
45893             }
45894             if (a.name == 'style') {
45895                 this.cleanStyle(node,a.name,a.value);
45896                 continue;
45897             }
45898             /// clean up MS crap..
45899             // tecnically this should be a list of valid class'es..
45900             
45901             
45902             if (a.name == 'class') {
45903                 if (a.value.match(/^Mso/)) {
45904                     node.removeAttribute('class');
45905                 }
45906                 
45907                 if (a.value.match(/^body$/)) {
45908                     node.removeAttribute('class');
45909                 }
45910                 continue;
45911             }
45912             
45913             
45914             // style cleanup!?
45915             // class cleanup?
45916             
45917         }
45918         return true; // clean children
45919     },
45920         
45921     cleanAttr: function(node, n,v)
45922     {
45923         
45924         if (v.match(/^\./) || v.match(/^\//)) {
45925             return;
45926         }
45927         if (v.match(/^(http|https):\/\//)
45928             || v.match(/^mailto:/) 
45929             || v.match(/^ftp:/)
45930             || v.match(/^data:/)
45931             ) {
45932             return;
45933         }
45934         if (v.match(/^#/)) {
45935             return;
45936         }
45937         if (v.match(/^\{/)) { // allow template editing.
45938             return;
45939         }
45940 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
45941         node.removeAttribute(n);
45942         
45943     },
45944     cleanStyle : function(node,  n,v)
45945     {
45946         if (v.match(/expression/)) { //XSS?? should we even bother..
45947             node.removeAttribute(n);
45948             return;
45949         }
45950         
45951         var parts = v.split(/;/);
45952         var clean = [];
45953         
45954         Roo.each(parts, function(p) {
45955             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
45956             if (!p.length) {
45957                 return true;
45958             }
45959             var l = p.split(':').shift().replace(/\s+/g,'');
45960             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
45961             
45962             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
45963                 return true;
45964             }
45965             //Roo.log()
45966             // only allow 'c whitelisted system attributes'
45967             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
45968                 return true;
45969             }
45970             
45971             
45972             clean.push(p);
45973             return true;
45974         },this);
45975         if (clean.length) { 
45976             node.setAttribute(n, clean.join(';'));
45977         } else {
45978             node.removeAttribute(n);
45979         }
45980         
45981     }
45982         
45983         
45984         
45985     
45986 });/**
45987  * @class Roo.htmleditor.FilterBlack
45988  * remove blacklisted elements.
45989  * @constructor
45990  * Run a new Blacklisted Filter
45991  * @param {Object} config Configuration options
45992  */
45993
45994 Roo.htmleditor.FilterBlack = function(cfg)
45995 {
45996     Roo.apply(this, cfg);
45997     this.walk(cfg.node);
45998 }
45999
46000 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
46001 {
46002     tag : true, // all elements.
46003    
46004     replaceTag : function(n)
46005     {
46006         n.parentNode.removeChild(n);
46007     }
46008 });
46009 /**
46010  * @class Roo.htmleditor.FilterComment
46011  * remove comments.
46012  * @constructor
46013 * Run a new Comments Filter
46014 * @param {Object} config Configuration options
46015  */
46016 Roo.htmleditor.FilterComment = function(cfg)
46017 {
46018     this.walk(cfg.node);
46019 }
46020
46021 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
46022 {
46023   
46024     replaceComment : function(n)
46025     {
46026         n.parentNode.removeChild(n);
46027     }
46028 });/**
46029  * @class Roo.htmleditor.FilterKeepChildren
46030  * remove tags but keep children
46031  * @constructor
46032  * Run a new Keep Children Filter
46033  * @param {Object} config Configuration options
46034  */
46035
46036 Roo.htmleditor.FilterKeepChildren = function(cfg)
46037 {
46038     Roo.apply(this, cfg);
46039     if (this.tag === false) {
46040         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
46041     }
46042     // hacky?
46043     if ((typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)) {
46044         this.cleanNamespace = true;
46045     }
46046         
46047     this.walk(cfg.node);
46048 }
46049
46050 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
46051 {
46052     cleanNamespace : false, // should really be an option, rather than using ':' inside of this tag.
46053   
46054     replaceTag : function(node)
46055     {
46056         // walk children...
46057         //Roo.log(node.tagName);
46058         var ar = Array.from(node.childNodes);
46059         //remove first..
46060         
46061         for (var i = 0; i < ar.length; i++) {
46062             var e = ar[i];
46063             if (e.nodeType == 1) {
46064                 if (
46065                     (typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1)
46066                     || // array and it matches
46067                     (typeof(this.tag) == 'string' && this.tag == e.tagName)
46068                     ||
46069                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)
46070                     ||
46071                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":")
46072                 ) {
46073                     this.replaceTag(ar[i]); // child is blacklisted as well...
46074                     continue;
46075                 }
46076             }
46077         }  
46078         ar = Array.from(node.childNodes);
46079         for (var i = 0; i < ar.length; i++) {
46080          
46081             node.removeChild(ar[i]);
46082             // what if we need to walk these???
46083             node.parentNode.insertBefore(ar[i], node);
46084             if (this.tag !== false) {
46085                 this.walk(ar[i]);
46086                 
46087             }
46088         }
46089         //Roo.log("REMOVE:" + node.tagName);
46090         node.parentNode.removeChild(node);
46091         return false; // don't walk children
46092         
46093         
46094     }
46095 });/**
46096  * @class Roo.htmleditor.FilterParagraph
46097  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
46098  * like on 'push' to remove the <p> tags and replace them with line breaks.
46099  * @constructor
46100  * Run a new Paragraph Filter
46101  * @param {Object} config Configuration options
46102  */
46103
46104 Roo.htmleditor.FilterParagraph = function(cfg)
46105 {
46106     // no need to apply config.
46107     this.searchTag(cfg.node);
46108 }
46109
46110 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
46111 {
46112     
46113      
46114     tag : 'P',
46115     
46116      
46117     replaceTag : function(node)
46118     {
46119         
46120         if (node.childNodes.length == 1 &&
46121             node.childNodes[0].nodeType == 3 &&
46122             node.childNodes[0].textContent.trim().length < 1
46123             ) {
46124             // remove and replace with '<BR>';
46125             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
46126             return false; // no need to walk..
46127         }
46128
46129         var ar = Array.from(node.childNodes);
46130         for (var i = 0; i < ar.length; i++) {
46131             node.removeChild(ar[i]);
46132             // what if we need to walk these???
46133             node.parentNode.insertBefore(ar[i], node);
46134         }
46135         // now what about this?
46136         // <p> &nbsp; </p>
46137         
46138         // double BR.
46139         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
46140         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
46141         node.parentNode.removeChild(node);
46142         
46143         return false;
46144
46145     }
46146     
46147 });/**
46148  * @class Roo.htmleditor.FilterHashLink
46149  * remove hash link
46150  * @constructor
46151  * Run a new Hash Link Filter
46152  * @param {Object} config Configuration options
46153  */
46154
46155  Roo.htmleditor.FilterHashLink = function(cfg)
46156  {
46157      // no need to apply config.
46158     //  this.walk(cfg.node);
46159     this.searchTag(cfg.node);
46160  }
46161  
46162  Roo.extend(Roo.htmleditor.FilterHashLink, Roo.htmleditor.Filter,
46163  {
46164       
46165      tag : 'A',
46166      
46167       
46168      replaceTag : function(node)
46169      {
46170          for(var i = 0; i < node.attributes.length; i ++) {
46171              var a = node.attributes[i];
46172
46173              if(a.name.toLowerCase() == 'href' && a.value.startsWith('#')) {
46174                  this.removeNodeKeepChildren(node);
46175              }
46176          }
46177          
46178          return false;
46179  
46180      }
46181      
46182  });/**
46183  * @class Roo.htmleditor.FilterSpan
46184  * filter span's with no attributes out..
46185  * @constructor
46186  * Run a new Span Filter
46187  * @param {Object} config Configuration options
46188  */
46189
46190 Roo.htmleditor.FilterSpan = function(cfg)
46191 {
46192     // no need to apply config.
46193     this.searchTag(cfg.node);
46194 }
46195
46196 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
46197 {
46198      
46199     tag : 'SPAN',
46200      
46201  
46202     replaceTag : function(node)
46203     {
46204         if (node.attributes && node.attributes.length > 0) {
46205             return true; // walk if there are any.
46206         }
46207         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
46208         return false;
46209      
46210     }
46211     
46212 });/**
46213  * @class Roo.htmleditor.FilterTableWidth
46214   try and remove table width data - as that frequently messes up other stuff.
46215  * 
46216  *      was cleanTableWidths.
46217  *
46218  * Quite often pasting from word etc.. results in tables with column and widths.
46219  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
46220  *
46221  * @constructor
46222  * Run a new Table Filter
46223  * @param {Object} config Configuration options
46224  */
46225
46226 Roo.htmleditor.FilterTableWidth = function(cfg)
46227 {
46228     // no need to apply config.
46229     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
46230     this.walk(cfg.node);
46231 }
46232
46233 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
46234 {
46235      
46236      
46237     
46238     replaceTag: function(node) {
46239         
46240         
46241       
46242         if (node.hasAttribute('width')) {
46243             node.removeAttribute('width');
46244         }
46245         
46246          
46247         if (node.hasAttribute("style")) {
46248             // pretty basic...
46249             
46250             var styles = node.getAttribute("style").split(";");
46251             var nstyle = [];
46252             Roo.each(styles, function(s) {
46253                 if (!s.match(/:/)) {
46254                     return;
46255                 }
46256                 var kv = s.split(":");
46257                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
46258                     return;
46259                 }
46260                 // what ever is left... we allow.
46261                 nstyle.push(s);
46262             });
46263             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
46264             if (!nstyle.length) {
46265                 node.removeAttribute('style');
46266             }
46267         }
46268         
46269         return true; // continue doing children..
46270     }
46271 });/**
46272  * @class Roo.htmleditor.FilterWord
46273  * try and clean up all the mess that Word generates.
46274  * 
46275  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
46276  
46277  * @constructor
46278  * Run a new Span Filter
46279  * @param {Object} config Configuration options
46280  */
46281
46282 Roo.htmleditor.FilterWord = function(cfg)
46283 {
46284     // no need to apply config.
46285     this.replaceDocBullets(cfg.node);
46286     
46287     this.replaceAname(cfg.node);
46288     // this is disabled as the removal is done by other filters;
46289    // this.walk(cfg.node);
46290     this.replaceImageTable(cfg.node);
46291     
46292 }
46293
46294 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
46295 {
46296     tag: true,
46297      
46298     
46299     /**
46300      * Clean up MS wordisms...
46301      */
46302     replaceTag : function(node)
46303     {
46304          
46305         // no idea what this does - span with text, replaceds with just text.
46306         if(
46307                 node.nodeName == 'SPAN' &&
46308                 !node.hasAttributes() &&
46309                 node.childNodes.length == 1 &&
46310                 node.firstChild.nodeName == "#text"  
46311         ) {
46312             var textNode = node.firstChild;
46313             node.removeChild(textNode);
46314             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
46315                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
46316             }
46317             node.parentNode.insertBefore(textNode, node);
46318             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
46319                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
46320             }
46321             
46322             node.parentNode.removeChild(node);
46323             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
46324         }
46325         
46326    
46327         
46328         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
46329             node.parentNode.removeChild(node);
46330             return false; // dont do chidlren
46331         }
46332         //Roo.log(node.tagName);
46333         // remove - but keep children..
46334         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
46335             //Roo.log('-- removed');
46336             while (node.childNodes.length) {
46337                 var cn = node.childNodes[0];
46338                 node.removeChild(cn);
46339                 node.parentNode.insertBefore(cn, node);
46340                 // move node to parent - and clean it..
46341                 if (cn.nodeType == 1) {
46342                     this.replaceTag(cn);
46343                 }
46344                 
46345             }
46346             node.parentNode.removeChild(node);
46347             /// no need to iterate chidlren = it's got none..
46348             //this.iterateChildren(node, this.cleanWord);
46349             return false; // no need to iterate children.
46350         }
46351         // clean styles
46352         if (node.className.length) {
46353             
46354             var cn = node.className.split(/\W+/);
46355             var cna = [];
46356             Roo.each(cn, function(cls) {
46357                 if (cls.match(/Mso[a-zA-Z]+/)) {
46358                     return;
46359                 }
46360                 cna.push(cls);
46361             });
46362             node.className = cna.length ? cna.join(' ') : '';
46363             if (!cna.length) {
46364                 node.removeAttribute("class");
46365             }
46366         }
46367         
46368         if (node.hasAttribute("lang")) {
46369             node.removeAttribute("lang");
46370         }
46371         
46372         if (node.hasAttribute("style")) {
46373             
46374             var styles = node.getAttribute("style").split(";");
46375             var nstyle = [];
46376             Roo.each(styles, function(s) {
46377                 if (!s.match(/:/)) {
46378                     return;
46379                 }
46380                 var kv = s.split(":");
46381                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
46382                     return;
46383                 }
46384                 // what ever is left... we allow.
46385                 nstyle.push(s);
46386             });
46387             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
46388             if (!nstyle.length) {
46389                 node.removeAttribute('style');
46390             }
46391         }
46392         return true; // do children
46393         
46394         
46395         
46396     },
46397     
46398     styleToObject: function(node)
46399     {
46400         var styles = (node.getAttribute("style") || '').split(";");
46401         var ret = {};
46402         Roo.each(styles, function(s) {
46403             if (!s.match(/:/)) {
46404                 return;
46405             }
46406             var kv = s.split(":");
46407              
46408             // what ever is left... we allow.
46409             ret[kv[0].trim()] = kv[1];
46410         });
46411         return ret;
46412     },
46413     
46414     
46415     replaceAname : function (doc)
46416     {
46417         // replace all the a/name without..
46418         var aa = Array.from(doc.getElementsByTagName('a'));
46419         for (var i = 0; i  < aa.length; i++) {
46420             var a = aa[i];
46421             if (a.hasAttribute("name")) {
46422                 a.removeAttribute("name");
46423             }
46424             if (a.hasAttribute("href")) {
46425                 continue;
46426             }
46427             // reparent children.
46428             this.removeNodeKeepChildren(a);
46429             
46430         }
46431         
46432         
46433         
46434     },
46435
46436     
46437     
46438     replaceDocBullets : function(doc)
46439     {
46440         // this is a bit odd - but it appears some indents use ql-indent-1
46441          //Roo.log(doc.innerHTML);
46442         
46443         var listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpFirst'));
46444         for( var i = 0; i < listpara.length; i ++) {
46445             listpara[i].className = "MsoListParagraph";
46446         }
46447         
46448         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpMiddle'));
46449         for( var i = 0; i < listpara.length; i ++) {
46450             listpara[i].className = "MsoListParagraph";
46451         }
46452         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpLast'));
46453         for( var i = 0; i < listpara.length; i ++) {
46454             listpara[i].className = "MsoListParagraph";
46455         }
46456         listpara =  Array.from(doc.getElementsByClassName('ql-indent-1'));
46457         for( var i = 0; i < listpara.length; i ++) {
46458             listpara[i].className = "MsoListParagraph";
46459         }
46460         
46461         // this is a bit hacky - we had one word document where h2 had a miso-list attribute.
46462         var htwo =  Array.from(doc.getElementsByTagName('h2'));
46463         for( var i = 0; i < htwo.length; i ++) {
46464             if (htwo[i].hasAttribute('style') && htwo[i].getAttribute('style').match(/mso-list:/)) {
46465                 htwo[i].className = "MsoListParagraph";
46466             }
46467         }
46468         listpara =  Array.from(doc.getElementsByClassName('MsoNormal'));
46469         for( var i = 0; i < listpara.length; i ++) {
46470             if (listpara[i].hasAttribute('style') && listpara[i].getAttribute('style').match(/mso-list:/)) {
46471                 listpara[i].className = "MsoListParagraph";
46472             } else {
46473                 listpara[i].className = "MsoNormalx";
46474             }
46475         }
46476        
46477         listpara = doc.getElementsByClassName('MsoListParagraph');
46478         // Roo.log(doc.innerHTML);
46479         
46480         
46481         
46482         while(listpara.length) {
46483             
46484             this.replaceDocBullet(listpara.item(0));
46485         }
46486       
46487     },
46488     
46489      
46490     
46491     replaceDocBullet : function(p)
46492     {
46493         // gather all the siblings.
46494         var ns = p,
46495             parent = p.parentNode,
46496             doc = parent.ownerDocument,
46497             items = [];
46498          
46499         //Roo.log("Parsing: " + p.innerText)    ;
46500         var listtype = 'ul';   
46501         while (ns) {
46502             if (ns.nodeType != 1) {
46503                 ns = ns.nextSibling;
46504                 continue;
46505             }
46506             if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
46507                 //Roo.log("Missing para r q1indent - got:" + ns.className);
46508                 break;
46509             }
46510             var spans = ns.getElementsByTagName('span');
46511             
46512             if (ns.hasAttribute('style') && ns.getAttribute('style').match(/mso-list/)) {
46513                 items.push(ns);
46514                 ns = ns.nextSibling;
46515                 has_list = true;
46516                 if (!spans.length) {
46517                     continue;
46518                 }
46519                 var ff = '';
46520                 var se = spans[0];
46521                 for (var i = 0; i < spans.length;i++) {
46522                     se = spans[i];
46523                     if (se.hasAttribute('style')  && se.hasAttribute('style') && se.style.fontFamily != '') {
46524                         ff = se.style.fontFamily;
46525                         break;
46526                     }
46527                 }
46528                  
46529                     
46530                 //Roo.log("got font family: " + ff);
46531                 if (typeof(ff) != 'undefined' && !ff.match(/(Symbol|Wingdings)/) && "·o".indexOf(se.innerText.trim()) < 0) {
46532                     listtype = 'ol';
46533                 }
46534                 
46535                 continue;
46536             }
46537             //Roo.log("no mso-list?");
46538             
46539             var spans = ns.getElementsByTagName('span');
46540             if (!spans.length) {
46541                 break;
46542             }
46543             var has_list  = false;
46544             for(var i = 0; i < spans.length; i++) {
46545                 if (spans[i].hasAttribute('style') && spans[i].getAttribute('style').match(/mso-list/)) {
46546                     has_list = true;
46547                     break;
46548                 }
46549             }
46550             if (!has_list) {
46551                 break;
46552             }
46553             items.push(ns);
46554             ns = ns.nextSibling;
46555             
46556             
46557         }
46558         if (!items.length) {
46559             ns.className = "";
46560             return;
46561         }
46562         
46563         var ul = parent.ownerDocument.createElement(listtype); // what about number lists...
46564         parent.insertBefore(ul, p);
46565         var lvl = 0;
46566         var stack = [ ul ];
46567         var last_li = false;
46568         
46569         var margin_to_depth = {};
46570         max_margins = -1;
46571         
46572         items.forEach(function(n, ipos) {
46573             //Roo.log("got innertHMLT=" + n.innerHTML);
46574             
46575             var spans = n.getElementsByTagName('span');
46576             if (!spans.length) {
46577                 //Roo.log("No spans found");
46578                  
46579                 parent.removeChild(n);
46580                 
46581                 
46582                 return; // skip it...
46583             }
46584            
46585                 
46586             var num = 1;
46587             var style = {};
46588             for(var i = 0; i < spans.length; i++) {
46589             
46590                 style = this.styleToObject(spans[i]);
46591                 if (typeof(style['mso-list']) == 'undefined') {
46592                     continue;
46593                 }
46594                 if (listtype == 'ol') {
46595                    num = spans[i].innerText.replace(/[^0-9]+]/g,'')  * 1;
46596                 }
46597                 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
46598                 break;
46599             }
46600             //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
46601             style = this.styleToObject(n); // mo-list is from the parent node.
46602             if (typeof(style['mso-list']) == 'undefined') {
46603                 //Roo.log("parent is missing level");
46604                   
46605                 parent.removeChild(n);
46606                  
46607                 return;
46608             }
46609             
46610             var margin = style['margin-left'];
46611             if (typeof(margin_to_depth[margin]) == 'undefined') {
46612                 max_margins++;
46613                 margin_to_depth[margin] = max_margins;
46614             }
46615             nlvl = margin_to_depth[margin] ;
46616              
46617             if (nlvl > lvl) {
46618                 //new indent
46619                 var nul = doc.createElement(listtype); // what about number lists...
46620                 if (!last_li) {
46621                     last_li = doc.createElement('li');
46622                     stack[lvl].appendChild(last_li);
46623                 }
46624                 last_li.appendChild(nul);
46625                 stack[nlvl] = nul;
46626                 
46627             }
46628             lvl = nlvl;
46629             
46630             // not starting at 1..
46631             if (!stack[nlvl].hasAttribute("start") && listtype == "ol") {
46632                 stack[nlvl].setAttribute("start", num);
46633             }
46634             
46635             var nli = stack[nlvl].appendChild(doc.createElement('li'));
46636             last_li = nli;
46637             nli.innerHTML = n.innerHTML;
46638             //Roo.log("innerHTML = " + n.innerHTML);
46639             parent.removeChild(n);
46640             
46641              
46642              
46643             
46644         },this);
46645         
46646         
46647         
46648         
46649     },
46650     
46651     replaceImageTable : function(doc)
46652     {
46653          /*
46654           <table cellpadding=0 cellspacing=0 align=left>
46655   <tr>
46656    <td width=423 height=0></td>
46657   </tr>
46658   <tr>
46659    <td></td>
46660    <td><img width=601 height=401
46661    src="file:///C:/Users/Alan/AppData/Local/Temp/msohtmlclip1/01/clip_image002.jpg"
46662    v:shapes="Picture_x0020_2"></td>
46663   </tr>
46664  </table>
46665  */
46666         var imgs = Array.from(doc.getElementsByTagName('img'));
46667         Roo.each(imgs, function(img) {
46668             var td = img.parentNode;
46669             if (td.nodeName !=  'TD') {
46670                 return;
46671             }
46672             var tr = td.parentNode;
46673             if (tr.nodeName !=  'TR') {
46674                 return;
46675             }
46676             var tbody = tr.parentNode;
46677             if (tbody.nodeName !=  'TBODY') {
46678                 return;
46679             }
46680             var table = tbody.parentNode;
46681             if (table.nodeName !=  'TABLE') {
46682                 return;
46683             }
46684             // first row..
46685             
46686             if (table.getElementsByTagName('tr').length != 2) {
46687                 return;
46688             }
46689             if (table.getElementsByTagName('td').length != 3) {
46690                 return;
46691             }
46692             if (table.innerText.trim() != '') {
46693                 return;
46694             }
46695             var p = table.parentNode;
46696             img.parentNode.removeChild(img);
46697             p.insertBefore(img, table);
46698             p.removeChild(table);
46699             
46700             
46701             
46702         });
46703         
46704       
46705     }
46706     
46707 });
46708 /**
46709  * @class Roo.htmleditor.FilterStyleToTag
46710  * part of the word stuff... - certain 'styles' should be converted to tags.
46711  * eg.
46712  *   font-weight: bold -> bold
46713  *   ?? super / subscrit etc..
46714  * 
46715  * @constructor
46716 * Run a new style to tag filter.
46717 * @param {Object} config Configuration options
46718  */
46719 Roo.htmleditor.FilterStyleToTag = function(cfg)
46720 {
46721     
46722     this.tags = {
46723         B  : [ 'fontWeight' , 'bold'],
46724         I :  [ 'fontStyle' , 'italic'],
46725         //pre :  [ 'font-style' , 'italic'],
46726         // h1.. h6 ?? font-size?
46727         SUP : [ 'verticalAlign' , 'super' ],
46728         SUB : [ 'verticalAlign' , 'sub' ]
46729         
46730         
46731     };
46732     
46733     Roo.apply(this, cfg);
46734      
46735     
46736     this.walk(cfg.node);
46737     
46738     
46739     
46740 }
46741
46742
46743 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
46744 {
46745     tag: true, // all tags
46746     
46747     tags : false,
46748     
46749     
46750     replaceTag : function(node)
46751     {
46752         
46753         
46754         if (node.getAttribute("style") === null) {
46755             return true;
46756         }
46757         var inject = [];
46758         for (var k in this.tags) {
46759             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
46760                 inject.push(k);
46761                 node.style.removeProperty(this.tags[k][0]);
46762             }
46763         }
46764         if (!inject.length) {
46765             return true; 
46766         }
46767         var cn = Array.from(node.childNodes);
46768         var nn = node;
46769         Roo.each(inject, function(t) {
46770             var nc = node.ownerDocument.createElement(t);
46771             nn.appendChild(nc);
46772             nn = nc;
46773         });
46774         for(var i = 0;i < cn.length;cn++) {
46775             node.removeChild(cn[i]);
46776             nn.appendChild(cn[i]);
46777         }
46778         return true /// iterate thru
46779     }
46780     
46781 })/**
46782  * @class Roo.htmleditor.FilterLongBr
46783  * BR/BR/BR - keep a maximum of 2...
46784  * @constructor
46785  * Run a new Long BR Filter
46786  * @param {Object} config Configuration options
46787  */
46788
46789 Roo.htmleditor.FilterLongBr = function(cfg)
46790 {
46791     // no need to apply config.
46792     this.searchTag(cfg.node);
46793 }
46794
46795 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
46796 {
46797     
46798      
46799     tag : 'BR',
46800     
46801      
46802     replaceTag : function(node)
46803     {
46804         
46805         var ps = node.nextSibling;
46806         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
46807             ps = ps.nextSibling;
46808         }
46809         
46810         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
46811             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
46812             return false;
46813         }
46814         
46815         if (!ps || ps.nodeType != 1) {
46816             return false;
46817         }
46818         
46819         if (!ps || ps.tagName != 'BR') {
46820            
46821             return false;
46822         }
46823         
46824         
46825         
46826         if (!node.previousSibling) {
46827             return false;
46828         }
46829         var ps = node.previousSibling;
46830         
46831         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
46832             ps = ps.previousSibling;
46833         }
46834         if (!ps || ps.nodeType != 1) {
46835             return false;
46836         }
46837         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
46838         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
46839             return false;
46840         }
46841         
46842         node.parentNode.removeChild(node); // remove me...
46843         
46844         return false; // no need to do children
46845
46846     }
46847     
46848 }); 
46849
46850 /**
46851  * @class Roo.htmleditor.FilterBlock
46852  * removes id / data-block and contenteditable that are associated with blocks
46853  * usage should be done on a cloned copy of the dom
46854  * @constructor
46855 * Run a new Attribute Filter { node : xxxx }}
46856 * @param {Object} config Configuration options
46857  */
46858 Roo.htmleditor.FilterBlock = function(cfg)
46859 {
46860     Roo.apply(this, cfg);
46861     var qa = cfg.node.querySelectorAll;
46862     this.removeAttributes('data-block');
46863     this.removeAttributes('contenteditable');
46864     this.removeAttributes('id');
46865     
46866 }
46867
46868 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
46869 {
46870     node: true, // all tags
46871      
46872      
46873     removeAttributes : function(attr)
46874     {
46875         var ar = this.node.querySelectorAll('*[' + attr + ']');
46876         for (var i =0;i<ar.length;i++) {
46877             ar[i].removeAttribute(attr);
46878         }
46879     }
46880         
46881         
46882         
46883     
46884 });
46885 /***
46886  * This is based loosely on tinymce 
46887  * @class Roo.htmleditor.TidySerializer
46888  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
46889  * @constructor
46890  * @method Serializer
46891  * @param {Object} settings Name/value settings object.
46892  */
46893
46894
46895 Roo.htmleditor.TidySerializer = function(settings)
46896 {
46897     Roo.apply(this, settings);
46898     
46899     this.writer = new Roo.htmleditor.TidyWriter(settings);
46900     
46901     
46902
46903 };
46904 Roo.htmleditor.TidySerializer.prototype = {
46905     
46906     /**
46907      * @param {boolean} inner do the inner of the node.
46908      */
46909     inner : false,
46910     
46911     writer : false,
46912     
46913     /**
46914     * Serializes the specified node into a string.
46915     *
46916     * @example
46917     * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
46918     * @method serialize
46919     * @param {DomElement} node Node instance to serialize.
46920     * @return {String} String with HTML based on DOM tree.
46921     */
46922     serialize : function(node) {
46923         
46924         // = settings.validate;
46925         var writer = this.writer;
46926         var self  = this;
46927         this.handlers = {
46928             // #text
46929             3: function(node) {
46930                 
46931                 writer.text(node.nodeValue, node);
46932             },
46933             // #comment
46934             8: function(node) {
46935                 writer.comment(node.nodeValue);
46936             },
46937             // Processing instruction
46938             7: function(node) {
46939                 writer.pi(node.name, node.nodeValue);
46940             },
46941             // Doctype
46942             10: function(node) {
46943                 writer.doctype(node.nodeValue);
46944             },
46945             // CDATA
46946             4: function(node) {
46947                 writer.cdata(node.nodeValue);
46948             },
46949             // Document fragment
46950             11: function(node) {
46951                 node = node.firstChild;
46952                 if (!node) {
46953                     return;
46954                 }
46955                 while(node) {
46956                     self.walk(node);
46957                     node = node.nextSibling
46958                 }
46959             }
46960         };
46961         writer.reset();
46962         1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
46963         return writer.getContent();
46964     },
46965
46966     walk: function(node)
46967     {
46968         var attrName, attrValue, sortedAttrs, i, l, elementRule,
46969             handler = this.handlers[node.nodeType];
46970             
46971         if (handler) {
46972             handler(node);
46973             return;
46974         }
46975     
46976         var name = node.nodeName;
46977         var isEmpty = node.childNodes.length < 1;
46978       
46979         var writer = this.writer;
46980         var attrs = node.attributes;
46981         // Sort attributes
46982         
46983         writer.start(node.nodeName, attrs, isEmpty, node);
46984         if (isEmpty) {
46985             return;
46986         }
46987         node = node.firstChild;
46988         if (!node) {
46989             writer.end(name);
46990             return;
46991         }
46992         while (node) {
46993             this.walk(node);
46994             node = node.nextSibling;
46995         }
46996         writer.end(name);
46997         
46998     
46999     }
47000     // Serialize element and treat all non elements as fragments
47001    
47002 }; 
47003
47004 /***
47005  * This is based loosely on tinymce 
47006  * @class Roo.htmleditor.TidyWriter
47007  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
47008  *
47009  * Known issues?
47010  * - not tested much with 'PRE' formated elements.
47011  * 
47012  *
47013  *
47014  */
47015
47016 Roo.htmleditor.TidyWriter = function(settings)
47017 {
47018     
47019     // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
47020     Roo.apply(this, settings);
47021     this.html = [];
47022     this.state = [];
47023      
47024     this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
47025   
47026 }
47027 Roo.htmleditor.TidyWriter.prototype = {
47028
47029  
47030     state : false,
47031     
47032     indent :  '  ',
47033     
47034     // part of state...
47035     indentstr : '',
47036     in_pre: false,
47037     in_inline : false,
47038     last_inline : false,
47039     encode : false,
47040      
47041     
47042             /**
47043     * Writes the a start element such as <p id="a">.
47044     *
47045     * @method start
47046     * @param {String} name Name of the element.
47047     * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
47048     * @param {Boolean} empty Optional empty state if the tag should end like <br />.
47049     */
47050     start: function(name, attrs, empty, node)
47051     {
47052         var i, l, attr, value;
47053         
47054         // there are some situations where adding line break && indentation will not work. will not work.
47055         // <span / b / i ... formating?
47056         
47057         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
47058         var in_pre    = this.in_pre    || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
47059         
47060         var is_short   = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
47061         
47062         var add_lb = name == 'BR' ? false : in_inline;
47063         
47064         if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
47065             i_inline = false;
47066         }
47067
47068         var indentstr =  this.indentstr;
47069         
47070         // e_inline = elements that can be inline, but still allow \n before and after?
47071         // only 'BR' ??? any others?
47072         
47073         // ADD LINE BEFORE tage
47074         if (!this.in_pre) {
47075             if (in_inline) {
47076                 //code
47077                 if (name == 'BR') {
47078                     this.addLine();
47079                 } else if (this.lastElementEndsWS()) {
47080                     this.addLine();
47081                 } else{
47082                     // otherwise - no new line. (and dont indent.)
47083                     indentstr = '';
47084                 }
47085                 
47086             } else {
47087                 this.addLine();
47088             }
47089         } else {
47090             indentstr = '';
47091         }
47092         
47093         this.html.push(indentstr + '<', name.toLowerCase());
47094         
47095         if (attrs) {
47096             for (i = 0, l = attrs.length; i < l; i++) {
47097                 attr = attrs[i];
47098                 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
47099             }
47100         }
47101      
47102         if (empty) {
47103             if (is_short) {
47104                 this.html[this.html.length] = '/>';
47105             } else {
47106                 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
47107             }
47108             var e_inline = name == 'BR' ? false : this.in_inline;
47109             
47110             if (!e_inline && !this.in_pre) {
47111                 this.addLine();
47112             }
47113             return;
47114         
47115         }
47116         // not empty..
47117         this.html[this.html.length] = '>';
47118         
47119         // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
47120         /*
47121         if (!in_inline && !in_pre) {
47122             var cn = node.firstChild;
47123             while(cn) {
47124                 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
47125                     in_inline = true
47126                     break;
47127                 }
47128                 cn = cn.nextSibling;
47129             }
47130              
47131         }
47132         */
47133         
47134         
47135         this.pushState({
47136             indentstr : in_pre   ? '' : (this.indentstr + this.indent),
47137             in_pre : in_pre,
47138             in_inline :  in_inline
47139         });
47140         // add a line after if we are not in a
47141         
47142         if (!in_inline && !in_pre) {
47143             this.addLine();
47144         }
47145         
47146             
47147          
47148         
47149     },
47150     
47151     lastElementEndsWS : function()
47152     {
47153         var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
47154         if (value === false) {
47155             return true;
47156         }
47157         return value.match(/\s+$/);
47158         
47159     },
47160     
47161     /**
47162      * Writes the a end element such as </p>.
47163      *
47164      * @method end
47165      * @param {String} name Name of the element.
47166      */
47167     end: function(name) {
47168         var value;
47169         this.popState();
47170         var indentstr = '';
47171         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
47172         
47173         if (!this.in_pre && !in_inline) {
47174             this.addLine();
47175             indentstr  = this.indentstr;
47176         }
47177         this.html.push(indentstr + '</', name.toLowerCase(), '>');
47178         this.last_inline = in_inline;
47179         
47180         // pop the indent state..
47181     },
47182     /**
47183      * Writes a text node.
47184      *
47185      * In pre - we should not mess with the contents.
47186      * 
47187      *
47188      * @method text
47189      * @param {String} text String to write out.
47190      * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
47191      */
47192     text: function(in_text, node)
47193     {
47194         // if not in whitespace critical
47195         if (in_text.length < 1) {
47196             return;
47197         }
47198         var text = new XMLSerializer().serializeToString(document.createTextNode(in_text)); // escape it properly?
47199         
47200         if (this.in_pre) {
47201             this.html[this.html.length] =  text;
47202             return;   
47203         }
47204         
47205         if (this.in_inline) {
47206             text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
47207             if (text != ' ') {
47208                 text = text.replace(/\s+/,' ');  // all white space to single white space
47209                 
47210                     
47211                 // if next tag is '<BR>', then we can trim right..
47212                 if (node.nextSibling &&
47213                     node.nextSibling.nodeType == 1 &&
47214                     node.nextSibling.nodeName == 'BR' )
47215                 {
47216                     text = text.replace(/\s+$/g,'');
47217                 }
47218                 // if previous tag was a BR, we can also trim..
47219                 if (node.previousSibling &&
47220                     node.previousSibling.nodeType == 1 &&
47221                     node.previousSibling.nodeName == 'BR' )
47222                 {
47223                     text = this.indentstr +  text.replace(/^\s+/g,'');
47224                 }
47225                 if (text.match(/\n/)) {
47226                     text = text.replace(
47227                         /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
47228                     );
47229                     // remoeve the last whitespace / line break.
47230                     text = text.replace(/\n\s+$/,'');
47231                 }
47232                 // repace long lines
47233                 
47234             }
47235              
47236             this.html[this.html.length] =  text;
47237             return;   
47238         }
47239         // see if previous element was a inline element.
47240         var indentstr = this.indentstr;
47241    
47242         text = text.replace(/\s+/g," "); // all whitespace into single white space.
47243         
47244         // should trim left?
47245         if (node.previousSibling &&
47246             node.previousSibling.nodeType == 1 &&
47247             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
47248         {
47249             indentstr = '';
47250             
47251         } else {
47252             this.addLine();
47253             text = text.replace(/^\s+/,''); // trim left
47254           
47255         }
47256         // should trim right?
47257         if (node.nextSibling &&
47258             node.nextSibling.nodeType == 1 &&
47259             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
47260         {
47261           // noop
47262             
47263         }  else {
47264             text = text.replace(/\s+$/,''); // trim right
47265         }
47266          
47267               
47268         
47269         
47270         
47271         if (text.length < 1) {
47272             return;
47273         }
47274         if (!text.match(/\n/)) {
47275             this.html.push(indentstr + text);
47276             return;
47277         }
47278         
47279         text = this.indentstr + text.replace(
47280             /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
47281         );
47282         // remoeve the last whitespace / line break.
47283         text = text.replace(/\s+$/,''); 
47284         
47285         this.html.push(text);
47286         
47287         // split and indent..
47288         
47289         
47290     },
47291     /**
47292      * Writes a cdata node such as <![CDATA[data]]>.
47293      *
47294      * @method cdata
47295      * @param {String} text String to write out inside the cdata.
47296      */
47297     cdata: function(text) {
47298         this.html.push('<![CDATA[', text, ']]>');
47299     },
47300     /**
47301     * Writes a comment node such as <!-- Comment -->.
47302     *
47303     * @method cdata
47304     * @param {String} text String to write out inside the comment.
47305     */
47306    comment: function(text) {
47307        this.html.push('<!--', text, '-->');
47308    },
47309     /**
47310      * Writes a PI node such as <?xml attr="value" ?>.
47311      *
47312      * @method pi
47313      * @param {String} name Name of the pi.
47314      * @param {String} text String to write out inside the pi.
47315      */
47316     pi: function(name, text) {
47317         text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
47318         this.indent != '' && this.html.push('\n');
47319     },
47320     /**
47321      * Writes a doctype node such as <!DOCTYPE data>.
47322      *
47323      * @method doctype
47324      * @param {String} text String to write out inside the doctype.
47325      */
47326     doctype: function(text) {
47327         this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
47328     },
47329     /**
47330      * Resets the internal buffer if one wants to reuse the writer.
47331      *
47332      * @method reset
47333      */
47334     reset: function() {
47335         this.html.length = 0;
47336         this.state = [];
47337         this.pushState({
47338             indentstr : '',
47339             in_pre : false, 
47340             in_inline : false
47341         })
47342     },
47343     /**
47344      * Returns the contents that got serialized.
47345      *
47346      * @method getContent
47347      * @return {String} HTML contents that got written down.
47348      */
47349     getContent: function() {
47350         return this.html.join('').replace(/\n$/, '');
47351     },
47352     
47353     pushState : function(cfg)
47354     {
47355         this.state.push(cfg);
47356         Roo.apply(this, cfg);
47357     },
47358     
47359     popState : function()
47360     {
47361         if (this.state.length < 1) {
47362             return; // nothing to push
47363         }
47364         var cfg = {
47365             in_pre: false,
47366             indentstr : ''
47367         };
47368         this.state.pop();
47369         if (this.state.length > 0) {
47370             cfg = this.state[this.state.length-1]; 
47371         }
47372         Roo.apply(this, cfg);
47373     },
47374     
47375     addLine: function()
47376     {
47377         if (this.html.length < 1) {
47378             return;
47379         }
47380         
47381         
47382         var value = this.html[this.html.length - 1];
47383         if (value.length > 0 && '\n' !== value) {
47384             this.html.push('\n');
47385         }
47386     }
47387     
47388     
47389 //'pre script noscript style textarea video audio iframe object code'
47390 // shortended... 'area base basefont br col frame hr img input isindex link  meta param embed source wbr track');
47391 // inline 
47392 };
47393
47394 Roo.htmleditor.TidyWriter.inline_elements = [
47395         'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
47396         'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
47397 ];
47398 Roo.htmleditor.TidyWriter.shortend_elements = [
47399     'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
47400     'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
47401 ];
47402
47403 Roo.htmleditor.TidyWriter.whitespace_elements = [
47404     'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
47405 ];/***
47406  * This is based loosely on tinymce 
47407  * @class Roo.htmleditor.TidyEntities
47408  * @static
47409  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
47410  *
47411  * Not 100% sure this is actually used or needed.
47412  */
47413
47414 Roo.htmleditor.TidyEntities = {
47415     
47416     /**
47417      * initialize data..
47418      */
47419     init : function (){
47420      
47421         this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
47422        
47423     },
47424
47425
47426     buildEntitiesLookup: function(items, radix) {
47427         var i, chr, entity, lookup = {};
47428         if (!items) {
47429             return {};
47430         }
47431         items = typeof(items) == 'string' ? items.split(',') : items;
47432         radix = radix || 10;
47433         // Build entities lookup table
47434         for (i = 0; i < items.length; i += 2) {
47435             chr = String.fromCharCode(parseInt(items[i], radix));
47436             // Only add non base entities
47437             if (!this.baseEntities[chr]) {
47438                 entity = '&' + items[i + 1] + ';';
47439                 lookup[chr] = entity;
47440                 lookup[entity] = chr;
47441             }
47442         }
47443         return lookup;
47444         
47445     },
47446     
47447     asciiMap : {
47448             128: '€',
47449             130: '‚',
47450             131: 'ƒ',
47451             132: '„',
47452             133: '…',
47453             134: '†',
47454             135: '‡',
47455             136: 'ˆ',
47456             137: '‰',
47457             138: 'Š',
47458             139: '‹',
47459             140: 'Œ',
47460             142: 'Ž',
47461             145: '‘',
47462             146: '’',
47463             147: '“',
47464             148: '”',
47465             149: '•',
47466             150: '–',
47467             151: '—',
47468             152: '˜',
47469             153: '™',
47470             154: 'š',
47471             155: '›',
47472             156: 'œ',
47473             158: 'ž',
47474             159: 'Ÿ'
47475     },
47476     // Raw entities
47477     baseEntities : {
47478         '"': '&quot;',
47479         // Needs to be escaped since the YUI compressor would otherwise break the code
47480         '\'': '&#39;',
47481         '<': '&lt;',
47482         '>': '&gt;',
47483         '&': '&amp;',
47484         '`': '&#96;'
47485     },
47486     // Reverse lookup table for raw entities
47487     reverseEntities : {
47488         '&lt;': '<',
47489         '&gt;': '>',
47490         '&amp;': '&',
47491         '&quot;': '"',
47492         '&apos;': '\''
47493     },
47494     
47495     attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
47496     textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
47497     rawCharsRegExp : /[<>&\"\']/g,
47498     entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
47499     namedEntities  : false,
47500     namedEntitiesData : [ 
47501         '50',
47502         'nbsp',
47503         '51',
47504         'iexcl',
47505         '52',
47506         'cent',
47507         '53',
47508         'pound',
47509         '54',
47510         'curren',
47511         '55',
47512         'yen',
47513         '56',
47514         'brvbar',
47515         '57',
47516         'sect',
47517         '58',
47518         'uml',
47519         '59',
47520         'copy',
47521         '5a',
47522         'ordf',
47523         '5b',
47524         'laquo',
47525         '5c',
47526         'not',
47527         '5d',
47528         'shy',
47529         '5e',
47530         'reg',
47531         '5f',
47532         'macr',
47533         '5g',
47534         'deg',
47535         '5h',
47536         'plusmn',
47537         '5i',
47538         'sup2',
47539         '5j',
47540         'sup3',
47541         '5k',
47542         'acute',
47543         '5l',
47544         'micro',
47545         '5m',
47546         'para',
47547         '5n',
47548         'middot',
47549         '5o',
47550         'cedil',
47551         '5p',
47552         'sup1',
47553         '5q',
47554         'ordm',
47555         '5r',
47556         'raquo',
47557         '5s',
47558         'frac14',
47559         '5t',
47560         'frac12',
47561         '5u',
47562         'frac34',
47563         '5v',
47564         'iquest',
47565         '60',
47566         'Agrave',
47567         '61',
47568         'Aacute',
47569         '62',
47570         'Acirc',
47571         '63',
47572         'Atilde',
47573         '64',
47574         'Auml',
47575         '65',
47576         'Aring',
47577         '66',
47578         'AElig',
47579         '67',
47580         'Ccedil',
47581         '68',
47582         'Egrave',
47583         '69',
47584         'Eacute',
47585         '6a',
47586         'Ecirc',
47587         '6b',
47588         'Euml',
47589         '6c',
47590         'Igrave',
47591         '6d',
47592         'Iacute',
47593         '6e',
47594         'Icirc',
47595         '6f',
47596         'Iuml',
47597         '6g',
47598         'ETH',
47599         '6h',
47600         'Ntilde',
47601         '6i',
47602         'Ograve',
47603         '6j',
47604         'Oacute',
47605         '6k',
47606         'Ocirc',
47607         '6l',
47608         'Otilde',
47609         '6m',
47610         'Ouml',
47611         '6n',
47612         'times',
47613         '6o',
47614         'Oslash',
47615         '6p',
47616         'Ugrave',
47617         '6q',
47618         'Uacute',
47619         '6r',
47620         'Ucirc',
47621         '6s',
47622         'Uuml',
47623         '6t',
47624         'Yacute',
47625         '6u',
47626         'THORN',
47627         '6v',
47628         'szlig',
47629         '70',
47630         'agrave',
47631         '71',
47632         'aacute',
47633         '72',
47634         'acirc',
47635         '73',
47636         'atilde',
47637         '74',
47638         'auml',
47639         '75',
47640         'aring',
47641         '76',
47642         'aelig',
47643         '77',
47644         'ccedil',
47645         '78',
47646         'egrave',
47647         '79',
47648         'eacute',
47649         '7a',
47650         'ecirc',
47651         '7b',
47652         'euml',
47653         '7c',
47654         'igrave',
47655         '7d',
47656         'iacute',
47657         '7e',
47658         'icirc',
47659         '7f',
47660         'iuml',
47661         '7g',
47662         'eth',
47663         '7h',
47664         'ntilde',
47665         '7i',
47666         'ograve',
47667         '7j',
47668         'oacute',
47669         '7k',
47670         'ocirc',
47671         '7l',
47672         'otilde',
47673         '7m',
47674         'ouml',
47675         '7n',
47676         'divide',
47677         '7o',
47678         'oslash',
47679         '7p',
47680         'ugrave',
47681         '7q',
47682         'uacute',
47683         '7r',
47684         'ucirc',
47685         '7s',
47686         'uuml',
47687         '7t',
47688         'yacute',
47689         '7u',
47690         'thorn',
47691         '7v',
47692         'yuml',
47693         'ci',
47694         'fnof',
47695         'sh',
47696         'Alpha',
47697         'si',
47698         'Beta',
47699         'sj',
47700         'Gamma',
47701         'sk',
47702         'Delta',
47703         'sl',
47704         'Epsilon',
47705         'sm',
47706         'Zeta',
47707         'sn',
47708         'Eta',
47709         'so',
47710         'Theta',
47711         'sp',
47712         'Iota',
47713         'sq',
47714         'Kappa',
47715         'sr',
47716         'Lambda',
47717         'ss',
47718         'Mu',
47719         'st',
47720         'Nu',
47721         'su',
47722         'Xi',
47723         'sv',
47724         'Omicron',
47725         't0',
47726         'Pi',
47727         't1',
47728         'Rho',
47729         't3',
47730         'Sigma',
47731         't4',
47732         'Tau',
47733         't5',
47734         'Upsilon',
47735         't6',
47736         'Phi',
47737         't7',
47738         'Chi',
47739         't8',
47740         'Psi',
47741         't9',
47742         'Omega',
47743         'th',
47744         'alpha',
47745         'ti',
47746         'beta',
47747         'tj',
47748         'gamma',
47749         'tk',
47750         'delta',
47751         'tl',
47752         'epsilon',
47753         'tm',
47754         'zeta',
47755         'tn',
47756         'eta',
47757         'to',
47758         'theta',
47759         'tp',
47760         'iota',
47761         'tq',
47762         'kappa',
47763         'tr',
47764         'lambda',
47765         'ts',
47766         'mu',
47767         'tt',
47768         'nu',
47769         'tu',
47770         'xi',
47771         'tv',
47772         'omicron',
47773         'u0',
47774         'pi',
47775         'u1',
47776         'rho',
47777         'u2',
47778         'sigmaf',
47779         'u3',
47780         'sigma',
47781         'u4',
47782         'tau',
47783         'u5',
47784         'upsilon',
47785         'u6',
47786         'phi',
47787         'u7',
47788         'chi',
47789         'u8',
47790         'psi',
47791         'u9',
47792         'omega',
47793         'uh',
47794         'thetasym',
47795         'ui',
47796         'upsih',
47797         'um',
47798         'piv',
47799         '812',
47800         'bull',
47801         '816',
47802         'hellip',
47803         '81i',
47804         'prime',
47805         '81j',
47806         'Prime',
47807         '81u',
47808         'oline',
47809         '824',
47810         'frasl',
47811         '88o',
47812         'weierp',
47813         '88h',
47814         'image',
47815         '88s',
47816         'real',
47817         '892',
47818         'trade',
47819         '89l',
47820         'alefsym',
47821         '8cg',
47822         'larr',
47823         '8ch',
47824         'uarr',
47825         '8ci',
47826         'rarr',
47827         '8cj',
47828         'darr',
47829         '8ck',
47830         'harr',
47831         '8dl',
47832         'crarr',
47833         '8eg',
47834         'lArr',
47835         '8eh',
47836         'uArr',
47837         '8ei',
47838         'rArr',
47839         '8ej',
47840         'dArr',
47841         '8ek',
47842         'hArr',
47843         '8g0',
47844         'forall',
47845         '8g2',
47846         'part',
47847         '8g3',
47848         'exist',
47849         '8g5',
47850         'empty',
47851         '8g7',
47852         'nabla',
47853         '8g8',
47854         'isin',
47855         '8g9',
47856         'notin',
47857         '8gb',
47858         'ni',
47859         '8gf',
47860         'prod',
47861         '8gh',
47862         'sum',
47863         '8gi',
47864         'minus',
47865         '8gn',
47866         'lowast',
47867         '8gq',
47868         'radic',
47869         '8gt',
47870         'prop',
47871         '8gu',
47872         'infin',
47873         '8h0',
47874         'ang',
47875         '8h7',
47876         'and',
47877         '8h8',
47878         'or',
47879         '8h9',
47880         'cap',
47881         '8ha',
47882         'cup',
47883         '8hb',
47884         'int',
47885         '8hk',
47886         'there4',
47887         '8hs',
47888         'sim',
47889         '8i5',
47890         'cong',
47891         '8i8',
47892         'asymp',
47893         '8j0',
47894         'ne',
47895         '8j1',
47896         'equiv',
47897         '8j4',
47898         'le',
47899         '8j5',
47900         'ge',
47901         '8k2',
47902         'sub',
47903         '8k3',
47904         'sup',
47905         '8k4',
47906         'nsub',
47907         '8k6',
47908         'sube',
47909         '8k7',
47910         'supe',
47911         '8kl',
47912         'oplus',
47913         '8kn',
47914         'otimes',
47915         '8l5',
47916         'perp',
47917         '8m5',
47918         'sdot',
47919         '8o8',
47920         'lceil',
47921         '8o9',
47922         'rceil',
47923         '8oa',
47924         'lfloor',
47925         '8ob',
47926         'rfloor',
47927         '8p9',
47928         'lang',
47929         '8pa',
47930         'rang',
47931         '9ea',
47932         'loz',
47933         '9j0',
47934         'spades',
47935         '9j3',
47936         'clubs',
47937         '9j5',
47938         'hearts',
47939         '9j6',
47940         'diams',
47941         'ai',
47942         'OElig',
47943         'aj',
47944         'oelig',
47945         'b0',
47946         'Scaron',
47947         'b1',
47948         'scaron',
47949         'bo',
47950         'Yuml',
47951         'm6',
47952         'circ',
47953         'ms',
47954         'tilde',
47955         '802',
47956         'ensp',
47957         '803',
47958         'emsp',
47959         '809',
47960         'thinsp',
47961         '80c',
47962         'zwnj',
47963         '80d',
47964         'zwj',
47965         '80e',
47966         'lrm',
47967         '80f',
47968         'rlm',
47969         '80j',
47970         'ndash',
47971         '80k',
47972         'mdash',
47973         '80o',
47974         'lsquo',
47975         '80p',
47976         'rsquo',
47977         '80q',
47978         'sbquo',
47979         '80s',
47980         'ldquo',
47981         '80t',
47982         'rdquo',
47983         '80u',
47984         'bdquo',
47985         '810',
47986         'dagger',
47987         '811',
47988         'Dagger',
47989         '81g',
47990         'permil',
47991         '81p',
47992         'lsaquo',
47993         '81q',
47994         'rsaquo',
47995         '85c',
47996         'euro'
47997     ],
47998
47999          
48000     /**
48001      * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
48002      *
48003      * @method encodeRaw
48004      * @param {String} text Text to encode.
48005      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
48006      * @return {String} Entity encoded text.
48007      */
48008     encodeRaw: function(text, attr)
48009     {
48010         var t = this;
48011         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
48012             return t.baseEntities[chr] || chr;
48013         });
48014     },
48015     /**
48016      * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
48017      * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
48018      * and is exposed as the DOMUtils.encode function.
48019      *
48020      * @method encodeAllRaw
48021      * @param {String} text Text to encode.
48022      * @return {String} Entity encoded text.
48023      */
48024     encodeAllRaw: function(text) {
48025         var t = this;
48026         return ('' + text).replace(this.rawCharsRegExp, function(chr) {
48027             return t.baseEntities[chr] || chr;
48028         });
48029     },
48030     /**
48031      * Encodes the specified string using numeric entities. The core entities will be
48032      * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
48033      *
48034      * @method encodeNumeric
48035      * @param {String} text Text to encode.
48036      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
48037      * @return {String} Entity encoded text.
48038      */
48039     encodeNumeric: function(text, attr) {
48040         var t = this;
48041         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
48042             // Multi byte sequence convert it to a single entity
48043             if (chr.length > 1) {
48044                 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
48045             }
48046             return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
48047         });
48048     },
48049     /**
48050      * Encodes the specified string using named entities. The core entities will be encoded
48051      * as named ones but all non lower ascii characters will be encoded into named entities.
48052      *
48053      * @method encodeNamed
48054      * @param {String} text Text to encode.
48055      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
48056      * @param {Object} entities Optional parameter with entities to use.
48057      * @return {String} Entity encoded text.
48058      */
48059     encodeNamed: function(text, attr, entities) {
48060         var t = this;
48061         entities = entities || this.namedEntities;
48062         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
48063             return t.baseEntities[chr] || entities[chr] || chr;
48064         });
48065     },
48066     /**
48067      * Returns an encode function based on the name(s) and it's optional entities.
48068      *
48069      * @method getEncodeFunc
48070      * @param {String} name Comma separated list of encoders for example named,numeric.
48071      * @param {String} entities Optional parameter with entities to use instead of the built in set.
48072      * @return {function} Encode function to be used.
48073      */
48074     getEncodeFunc: function(name, entities) {
48075         entities = this.buildEntitiesLookup(entities) || this.namedEntities;
48076         var t = this;
48077         function encodeNamedAndNumeric(text, attr) {
48078             return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
48079                 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
48080             });
48081         }
48082
48083         function encodeCustomNamed(text, attr) {
48084             return t.encodeNamed(text, attr, entities);
48085         }
48086         // Replace + with , to be compatible with previous TinyMCE versions
48087         name = this.makeMap(name.replace(/\+/g, ','));
48088         // Named and numeric encoder
48089         if (name.named && name.numeric) {
48090             return this.encodeNamedAndNumeric;
48091         }
48092         // Named encoder
48093         if (name.named) {
48094             // Custom names
48095             if (entities) {
48096                 return encodeCustomNamed;
48097             }
48098             return this.encodeNamed;
48099         }
48100         // Numeric
48101         if (name.numeric) {
48102             return this.encodeNumeric;
48103         }
48104         // Raw encoder
48105         return this.encodeRaw;
48106     },
48107     /**
48108      * Decodes the specified string, this will replace entities with raw UTF characters.
48109      *
48110      * @method decode
48111      * @param {String} text Text to entity decode.
48112      * @return {String} Entity decoded string.
48113      */
48114     decode: function(text)
48115     {
48116         var  t = this;
48117         return text.replace(this.entityRegExp, function(all, numeric) {
48118             if (numeric) {
48119                 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
48120                 // Support upper UTF
48121                 if (numeric > 65535) {
48122                     numeric -= 65536;
48123                     return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
48124                 }
48125                 return t.asciiMap[numeric] || String.fromCharCode(numeric);
48126             }
48127             return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
48128         });
48129     },
48130     nativeDecode : function (text) {
48131         return text;
48132     },
48133     makeMap : function (items, delim, map) {
48134                 var i;
48135                 items = items || [];
48136                 delim = delim || ',';
48137                 if (typeof items == "string") {
48138                         items = items.split(delim);
48139                 }
48140                 map = map || {};
48141                 i = items.length;
48142                 while (i--) {
48143                         map[items[i]] = {};
48144                 }
48145                 return map;
48146         }
48147 };
48148     
48149     
48150     
48151 Roo.htmleditor.TidyEntities.init();
48152 /**
48153  * @class Roo.htmleditor.KeyEnter
48154  * Handle Enter press..
48155  * @cfg {Roo.HtmlEditorCore} core the editor.
48156  * @constructor
48157  * Create a new Filter.
48158  * @param {Object} config Configuration options
48159  */
48160
48161
48162
48163
48164
48165 Roo.htmleditor.KeyEnter = function(cfg) {
48166     Roo.apply(this, cfg);
48167     // this does not actually call walk as it's really just a abstract class
48168  
48169     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
48170 }
48171
48172 //Roo.htmleditor.KeyEnter.i = 0;
48173
48174
48175 Roo.htmleditor.KeyEnter.prototype = {
48176     
48177     core : false,
48178     
48179     keypress : function(e)
48180     {
48181         if (e.charCode != 13 && e.charCode != 10) {
48182             Roo.log([e.charCode,e]);
48183             return true;
48184         }
48185         e.preventDefault();
48186         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
48187         var doc = this.core.doc;
48188           //add a new line
48189        
48190     
48191         var sel = this.core.getSelection();
48192         var range = sel.getRangeAt(0);
48193         var n = range.commonAncestorContainer;
48194         var pc = range.closest([ 'ol', 'ul']);
48195         var pli = range.closest('li');
48196         if (!pc || e.ctrlKey) {
48197             // on it list, or ctrl pressed.
48198             if (!e.ctrlKey) {
48199                 sel.insertNode('br', 'after'); 
48200             } else {
48201                 // only do this if we have ctrl key..
48202                 var br = doc.createElement('br');
48203                 br.className = 'clear';
48204                 br.setAttribute('style', 'clear: both');
48205                 sel.insertNode(br, 'after'); 
48206             }
48207             
48208          
48209             this.core.undoManager.addEvent();
48210             this.core.fireEditorEvent(e);
48211             return false;
48212         }
48213         
48214         // deal with <li> insetion
48215         if (pli.innerText.trim() == '' &&
48216             pli.previousSibling &&
48217             pli.previousSibling.nodeName == 'LI' &&
48218             pli.previousSibling.innerText.trim() ==  '') {
48219             pli.parentNode.removeChild(pli.previousSibling);
48220             sel.cursorAfter(pc);
48221             this.core.undoManager.addEvent();
48222             this.core.fireEditorEvent(e);
48223             return false;
48224         }
48225     
48226         var li = doc.createElement('LI');
48227         li.innerHTML = '&nbsp;';
48228         if (!pli || !pli.firstSibling) {
48229             pc.appendChild(li);
48230         } else {
48231             pli.parentNode.insertBefore(li, pli.firstSibling);
48232         }
48233         sel.cursorText (li.firstChild);
48234       
48235         this.core.undoManager.addEvent();
48236         this.core.fireEditorEvent(e);
48237
48238         return false;
48239         
48240     
48241         
48242         
48243          
48244     }
48245 };
48246      
48247 /**
48248  * @class Roo.htmleditor.Block
48249  * Base class for html editor blocks - do not use it directly .. extend it..
48250  * @cfg {DomElement} node The node to apply stuff to.
48251  * @cfg {String} friendly_name the name that appears in the context bar about this block
48252  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
48253  
48254  * @constructor
48255  * Create a new Filter.
48256  * @param {Object} config Configuration options
48257  */
48258
48259 Roo.htmleditor.Block  = function(cfg)
48260 {
48261     // do nothing .. should not be called really.
48262 }
48263 /**
48264  * factory method to get the block from an element (using cache if necessary)
48265  * @static
48266  * @param {HtmlElement} the dom element
48267  */
48268 Roo.htmleditor.Block.factory = function(node)
48269 {
48270     var cc = Roo.htmleditor.Block.cache;
48271     var id = Roo.get(node).id;
48272     if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
48273         Roo.htmleditor.Block.cache[id].readElement(node);
48274         return Roo.htmleditor.Block.cache[id];
48275     }
48276     var db  = node.getAttribute('data-block');
48277     if (!db) {
48278         db = node.nodeName.toLowerCase().toUpperCaseFirst();
48279     }
48280     var cls = Roo.htmleditor['Block' + db];
48281     if (typeof(cls) == 'undefined') {
48282         //Roo.log(node.getAttribute('data-block'));
48283         Roo.log("OOps missing block : " + 'Block' + db);
48284         return false;
48285     }
48286     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
48287     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
48288 };
48289
48290 /**
48291  * initalize all Elements from content that are 'blockable'
48292  * @static
48293  * @param the body element
48294  */
48295 Roo.htmleditor.Block.initAll = function(body, type)
48296 {
48297     if (typeof(type) == 'undefined') {
48298         var ia = Roo.htmleditor.Block.initAll;
48299         ia(body,'table');
48300         ia(body,'td');
48301         ia(body,'figure');
48302         return;
48303     }
48304     Roo.each(Roo.get(body).query(type), function(e) {
48305         Roo.htmleditor.Block.factory(e);    
48306     },this);
48307 };
48308 // question goes here... do we need to clear out this cache sometimes?
48309 // or show we make it relivant to the htmleditor.
48310 Roo.htmleditor.Block.cache = {};
48311
48312 Roo.htmleditor.Block.prototype = {
48313     
48314     node : false,
48315     
48316      // used by context menu
48317     friendly_name : 'Based Block',
48318     
48319     // text for button to delete this element
48320     deleteTitle : false,
48321     
48322     context : false,
48323     /**
48324      * Update a node with values from this object
48325      * @param {DomElement} node
48326      */
48327     updateElement : function(node)
48328     {
48329         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
48330     },
48331      /**
48332      * convert to plain HTML for calling insertAtCursor..
48333      */
48334     toHTML : function()
48335     {
48336         return Roo.DomHelper.markup(this.toObject());
48337     },
48338     /**
48339      * used by readEleemnt to extract data from a node
48340      * may need improving as it's pretty basic
48341      
48342      * @param {DomElement} node
48343      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
48344      * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
48345      * @param {String} style the style property - eg. text-align
48346      */
48347     getVal : function(node, tag, attr, style)
48348     {
48349         var n = node;
48350         if (tag !== true && n.tagName != tag.toUpperCase()) {
48351             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
48352             // but kiss for now.
48353             n = node.getElementsByTagName(tag).item(0);
48354         }
48355         if (!n) {
48356             return '';
48357         }
48358         if (attr === false) {
48359             return n;
48360         }
48361         if (attr == 'html') {
48362             return n.innerHTML;
48363         }
48364         if (attr == 'style') {
48365             return n.style[style]; 
48366         }
48367         
48368         return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
48369             
48370     },
48371     /**
48372      * create a DomHelper friendly object - for use with 
48373      * Roo.DomHelper.markup / overwrite / etc..
48374      * (override this)
48375      */
48376     toObject : function()
48377     {
48378         return {};
48379     },
48380       /**
48381      * Read a node that has a 'data-block' property - and extract the values from it.
48382      * @param {DomElement} node - the node
48383      */
48384     readElement : function(node)
48385     {
48386         
48387     } 
48388     
48389     
48390 };
48391
48392  
48393
48394 /**
48395  * @class Roo.htmleditor.BlockFigure
48396  * Block that has an image and a figcaption
48397  * @cfg {String} image_src the url for the image
48398  * @cfg {String} align (left|right) alignment for the block default left
48399  * @cfg {String} caption the text to appear below  (and in the alt tag)
48400  * @cfg {String} caption_display (block|none) display or not the caption
48401  * @cfg {String|number} image_width the width of the image number or %?
48402  * @cfg {String|number} image_height the height of the image number or %?
48403  * 
48404  * @constructor
48405  * Create a new Filter.
48406  * @param {Object} config Configuration options
48407  */
48408
48409 Roo.htmleditor.BlockFigure = function(cfg)
48410 {
48411     if (cfg.node) {
48412         this.readElement(cfg.node);
48413         this.updateElement(cfg.node);
48414     }
48415     Roo.apply(this, cfg);
48416 }
48417 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
48418  
48419     
48420     // setable values.
48421     image_src: '',
48422     align: 'center',
48423     caption : '',
48424     caption_display : 'block',
48425     width : '100%',
48426     cls : '',
48427     href: '',
48428     video_url : '',
48429     
48430     // margin: '2%', not used
48431     
48432     text_align: 'left', //   (left|right) alignment for the text caption default left. - not used at present
48433
48434     
48435     // used by context menu
48436     friendly_name : 'Image with caption',
48437     deleteTitle : "Delete Image and Caption",
48438     
48439     contextMenu : function(toolbar)
48440     {
48441         
48442         var block = function() {
48443             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
48444         };
48445         
48446         
48447         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
48448         
48449         var syncValue = toolbar.editorcore.syncValue;
48450         
48451         var fields = {};
48452         
48453         return [
48454              {
48455                 xtype : 'TextItem',
48456                 text : "Source: ",
48457                 xns : rooui.Toolbar  //Boostrap?
48458             },
48459             {
48460                 xtype : 'Button',
48461                 text: 'Change Image URL',
48462                  
48463                 listeners : {
48464                     click: function (btn, state)
48465                     {
48466                         var b = block();
48467                         
48468                         Roo.MessageBox.show({
48469                             title : "Image Source URL",
48470                             msg : "Enter the url for the image",
48471                             buttons: Roo.MessageBox.OKCANCEL,
48472                             fn: function(btn, val){
48473                                 if (btn != 'ok') {
48474                                     return;
48475                                 }
48476                                 b.image_src = val;
48477                                 b.updateElement();
48478                                 syncValue();
48479                                 toolbar.editorcore.onEditorEvent();
48480                             },
48481                             minWidth:250,
48482                             prompt:true,
48483                             //multiline: multiline,
48484                             modal : true,
48485                             value : b.image_src
48486                         });
48487                     }
48488                 },
48489                 xns : rooui.Toolbar
48490             },
48491          
48492             {
48493                 xtype : 'Button',
48494                 text: 'Change Link URL',
48495                  
48496                 listeners : {
48497                     click: function (btn, state)
48498                     {
48499                         var b = block();
48500                         
48501                         Roo.MessageBox.show({
48502                             title : "Link URL",
48503                             msg : "Enter the url for the link - leave blank to have no link",
48504                             buttons: Roo.MessageBox.OKCANCEL,
48505                             fn: function(btn, val){
48506                                 if (btn != 'ok') {
48507                                     return;
48508                                 }
48509                                 b.href = val;
48510                                 b.updateElement();
48511                                 syncValue();
48512                                 toolbar.editorcore.onEditorEvent();
48513                             },
48514                             minWidth:250,
48515                             prompt:true,
48516                             //multiline: multiline,
48517                             modal : true,
48518                             value : b.href
48519                         });
48520                     }
48521                 },
48522                 xns : rooui.Toolbar
48523             },
48524             {
48525                 xtype : 'Button',
48526                 text: 'Show Video URL',
48527                  
48528                 listeners : {
48529                     click: function (btn, state)
48530                     {
48531                         Roo.MessageBox.alert("Video URL",
48532                             block().video_url == '' ? 'This image is not linked ot a video' :
48533                                 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
48534                     }
48535                 },
48536                 xns : rooui.Toolbar
48537             },
48538             
48539             
48540             {
48541                 xtype : 'TextItem',
48542                 text : "Width: ",
48543                 xns : rooui.Toolbar  //Boostrap?
48544             },
48545             {
48546                 xtype : 'ComboBox',
48547                 allowBlank : false,
48548                 displayField : 'val',
48549                 editable : true,
48550                 listWidth : 100,
48551                 triggerAction : 'all',
48552                 typeAhead : true,
48553                 valueField : 'val',
48554                 width : 70,
48555                 name : 'width',
48556                 listeners : {
48557                     select : function (combo, r, index)
48558                     {
48559                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48560                         var b = block();
48561                         b.width = r.get('val');
48562                         b.updateElement();
48563                         syncValue();
48564                         toolbar.editorcore.onEditorEvent();
48565                     }
48566                 },
48567                 xns : rooui.form,
48568                 store : {
48569                     xtype : 'SimpleStore',
48570                     data : [
48571                         ['100%'],
48572                         ['80%'],
48573                         ['50%'],
48574                         ['20%'],
48575                         ['10%']
48576                     ],
48577                     fields : [ 'val'],
48578                     xns : Roo.data
48579                 }
48580             },
48581             {
48582                 xtype : 'TextItem',
48583                 text : "Align: ",
48584                 xns : rooui.Toolbar  //Boostrap?
48585             },
48586             {
48587                 xtype : 'ComboBox',
48588                 allowBlank : false,
48589                 displayField : 'val',
48590                 editable : true,
48591                 listWidth : 100,
48592                 triggerAction : 'all',
48593                 typeAhead : true,
48594                 valueField : 'val',
48595                 width : 70,
48596                 name : 'align',
48597                 listeners : {
48598                     select : function (combo, r, index)
48599                     {
48600                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48601                         var b = block();
48602                         b.align = r.get('val');
48603                         b.updateElement();
48604                         syncValue();
48605                         toolbar.editorcore.onEditorEvent();
48606                     }
48607                 },
48608                 xns : rooui.form,
48609                 store : {
48610                     xtype : 'SimpleStore',
48611                     data : [
48612                         ['left'],
48613                         ['right'],
48614                         ['center']
48615                     ],
48616                     fields : [ 'val'],
48617                     xns : Roo.data
48618                 }
48619             },
48620             
48621               
48622             {
48623                 xtype : 'Button',
48624                 text: 'Hide Caption',
48625                 name : 'caption_display',
48626                 pressed : false,
48627                 enableToggle : true,
48628                 setValue : function(v) {
48629                     // this trigger toggle.
48630                      
48631                     this.setText(v ? "Hide Caption" : "Show Caption");
48632                     this.setPressed(v != 'block');
48633                 },
48634                 listeners : {
48635                     toggle: function (btn, state)
48636                     {
48637                         var b  = block();
48638                         b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
48639                         this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
48640                         b.updateElement();
48641                         syncValue();
48642                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48643                         toolbar.editorcore.onEditorEvent();
48644                     }
48645                 },
48646                 xns : rooui.Toolbar
48647             }
48648         ];
48649         
48650     },
48651     /**
48652      * create a DomHelper friendly object - for use with
48653      * Roo.DomHelper.markup / overwrite / etc..
48654      */
48655     toObject : function()
48656     {
48657         var d = document.createElement('div');
48658         d.innerHTML = this.caption;
48659         
48660         var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0; 
48661         
48662         var iw = this.align == 'center' ? this.width : '100%';
48663         var img =   {
48664             tag : 'img',
48665             contenteditable : 'false',
48666             src : this.image_src,
48667             alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
48668             style: {
48669                 width : iw,
48670                 maxWidth : iw + ' !important', // this is not getting rendered?
48671                 margin : m  
48672                 
48673             }
48674         };
48675         /*
48676         '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
48677                     '<a href="{2}">' + 
48678                         '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' + 
48679                     '</a>' + 
48680                 '</div>',
48681         */
48682                 
48683         if (this.href.length > 0) {
48684             img = {
48685                 tag : 'a',
48686                 href: this.href,
48687                 contenteditable : 'true',
48688                 cn : [
48689                     img
48690                 ]
48691             };
48692         }
48693         
48694         
48695         if (this.video_url.length > 0) {
48696             img = {
48697                 tag : 'div',
48698                 cls : this.cls,
48699                 frameborder : 0,
48700                 allowfullscreen : true,
48701                 width : 420,  // these are for video tricks - that we replace the outer
48702                 height : 315,
48703                 src : this.video_url,
48704                 cn : [
48705                     img
48706                 ]
48707             };
48708         }
48709
48710
48711   
48712         var ret =   {
48713             tag: 'figure',
48714             'data-block' : 'Figure',
48715             'data-width' : this.width,
48716             'data-caption' : this.caption, 
48717             'data-caption-display' : this.caption_display,
48718             contenteditable : 'false',
48719             
48720             style : {
48721                 display: 'block',
48722                 float :  this.align ,
48723                 maxWidth :  this.align == 'center' ? '100% !important' : (this.width + ' !important'),
48724                 width : this.align == 'center' ? '100%' : this.width,
48725                 margin:  '0px',
48726                 padding: this.align == 'center' ? '0' : '0 10px' ,
48727                 textAlign : this.align   // seems to work for email..
48728                 
48729             },
48730             
48731             align : this.align,
48732             cn : [
48733                 img
48734             ]
48735         };
48736
48737         // show figcaption only if caption_display is 'block'
48738         if(this.caption_display == 'block') {
48739             ret['cn'].push({
48740                 tag: 'figcaption',
48741                 style : {
48742                     textAlign : 'left',
48743                     fontSize : '16px',
48744                     lineHeight : '24px',
48745                     display : this.caption_display,
48746                     maxWidth : (this.align == 'center' ?  this.width : '100%' ) + ' !important',
48747                     margin: m,
48748                     width: this.align == 'center' ?  this.width : '100%' 
48749                 
48750                      
48751                 },
48752                 cls : this.cls.length > 0 ? (this.cls  + '-thumbnail' ) : '',
48753                 cn : [
48754                     {
48755                         tag: 'div',
48756                         style  : {
48757                             marginTop : '16px',
48758                             textAlign : 'start'
48759                         },
48760                         align: 'left',
48761                         cn : [
48762                             {
48763                                 // we can not rely on yahoo syndication to use CSS elements - so have to use  '<i>' to encase stuff.
48764                                 tag : 'i',
48765                                 contenteditable : Roo.htmleditor.BlockFigure.caption_edit,
48766                                 html : this.caption.length ? this.caption : "Caption" // fake caption
48767                             }
48768                             
48769                         ]
48770                     }
48771                     
48772                 ]
48773                 
48774             });
48775         }
48776         return ret;
48777          
48778     },
48779     
48780     readElement : function(node)
48781     {
48782         // this should not really come from the link...
48783         this.video_url = this.getVal(node, 'div', 'src');
48784         this.cls = this.getVal(node, 'div', 'class');
48785         this.href = this.getVal(node, 'a', 'href');
48786         
48787         
48788         this.image_src = this.getVal(node, 'img', 'src');
48789          
48790         this.align = this.getVal(node, 'figure', 'align');
48791
48792         // caption display is stored in figure
48793         this.caption_display = this.getVal(node, true, 'data-caption-display');
48794
48795         // backward compatible
48796         // it was stored in figcaption
48797         if(this.caption_display == '') {
48798             this.caption_display = this.getVal(node, 'figcaption', 'data-display');
48799         }
48800
48801         // read caption from figcaption
48802         var figcaption = this.getVal(node, 'figcaption', false);
48803
48804         if (figcaption !== '') {
48805             this.caption = this.getVal(figcaption, 'i', 'html');
48806         }
48807                 
48808
48809         // read caption from data-caption in figure if no caption from figcaption
48810         var dc = this.getVal(node, true, 'data-caption');
48811
48812         if(this.caption_display == 'none' && dc && dc.length){
48813             this.caption = dc;
48814         }
48815
48816         //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
48817         this.width = this.getVal(node, true, 'data-width');
48818         //this.margin = this.getVal(node, 'figure', 'style', 'margin');
48819         
48820     },
48821     removeNode : function()
48822     {
48823         return this.node;
48824     }
48825     
48826   
48827    
48828      
48829     
48830     
48831     
48832     
48833 });
48834
48835 Roo.apply(Roo.htmleditor.BlockFigure, {
48836     caption_edit : true
48837 });
48838
48839  
48840
48841 /**
48842  * @class Roo.htmleditor.BlockTable
48843  * Block that manages a table
48844  * 
48845  * @constructor
48846  * Create a new Filter.
48847  * @param {Object} config Configuration options
48848  */
48849
48850 Roo.htmleditor.BlockTable = function(cfg)
48851 {
48852     if (cfg.node) {
48853         this.readElement(cfg.node);
48854         this.updateElement(cfg.node);
48855     }
48856     Roo.apply(this, cfg);
48857     if (!cfg.node) {
48858         this.rows = [];
48859         for(var r = 0; r < this.no_row; r++) {
48860             this.rows[r] = [];
48861             for(var c = 0; c < this.no_col; c++) {
48862                 this.rows[r][c] = this.emptyCell();
48863             }
48864         }
48865     }
48866     
48867     
48868 }
48869 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
48870  
48871     rows : false,
48872     no_col : 1,
48873     no_row : 1,
48874     
48875     
48876     width: '100%',
48877     
48878     // used by context menu
48879     friendly_name : 'Table',
48880     deleteTitle : 'Delete Table',
48881     // context menu is drawn once..
48882     
48883     contextMenu : function(toolbar)
48884     {
48885         
48886         var block = function() {
48887             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
48888         };
48889         
48890         
48891         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
48892         
48893         var syncValue = toolbar.editorcore.syncValue;
48894         
48895         var fields = {};
48896         
48897         return [
48898             {
48899                 xtype : 'TextItem',
48900                 text : "Width: ",
48901                 xns : rooui.Toolbar  //Boostrap?
48902             },
48903             {
48904                 xtype : 'ComboBox',
48905                 allowBlank : false,
48906                 displayField : 'val',
48907                 editable : true,
48908                 listWidth : 100,
48909                 triggerAction : 'all',
48910                 typeAhead : true,
48911                 valueField : 'val',
48912                 width : 100,
48913                 name : 'width',
48914                 listeners : {
48915                     select : function (combo, r, index)
48916                     {
48917                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48918                         var b = block();
48919                         b.width = r.get('val');
48920                         b.updateElement();
48921                         syncValue();
48922                         toolbar.editorcore.onEditorEvent();
48923                     }
48924                 },
48925                 xns : rooui.form,
48926                 store : {
48927                     xtype : 'SimpleStore',
48928                     data : [
48929                         ['100%'],
48930                         ['auto']
48931                     ],
48932                     fields : [ 'val'],
48933                     xns : Roo.data
48934                 }
48935             },
48936             // -------- Cols
48937             
48938             {
48939                 xtype : 'TextItem',
48940                 text : "Columns: ",
48941                 xns : rooui.Toolbar  //Boostrap?
48942             },
48943          
48944             {
48945                 xtype : 'Button',
48946                 text: '-',
48947                 listeners : {
48948                     click : function (_self, e)
48949                     {
48950                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48951                         block().removeColumn();
48952                         syncValue();
48953                         toolbar.editorcore.onEditorEvent();
48954                     }
48955                 },
48956                 xns : rooui.Toolbar
48957             },
48958             {
48959                 xtype : 'Button',
48960                 text: '+',
48961                 listeners : {
48962                     click : function (_self, e)
48963                     {
48964                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48965                         block().addColumn();
48966                         syncValue();
48967                         toolbar.editorcore.onEditorEvent();
48968                     }
48969                 },
48970                 xns : rooui.Toolbar
48971             },
48972             // -------- ROWS
48973             {
48974                 xtype : 'TextItem',
48975                 text : "Rows: ",
48976                 xns : rooui.Toolbar  //Boostrap?
48977             },
48978          
48979             {
48980                 xtype : 'Button',
48981                 text: '-',
48982                 listeners : {
48983                     click : function (_self, e)
48984                     {
48985                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48986                         block().removeRow();
48987                         syncValue();
48988                         toolbar.editorcore.onEditorEvent();
48989                     }
48990                 },
48991                 xns : rooui.Toolbar
48992             },
48993             {
48994                 xtype : 'Button',
48995                 text: '+',
48996                 listeners : {
48997                     click : function (_self, e)
48998                     {
48999                         block().addRow();
49000                         syncValue();
49001                         toolbar.editorcore.onEditorEvent();
49002                     }
49003                 },
49004                 xns : rooui.Toolbar
49005             },
49006             // -------- ROWS
49007             {
49008                 xtype : 'Button',
49009                 text: 'Reset Column Widths',
49010                 listeners : {
49011                     
49012                     click : function (_self, e)
49013                     {
49014                         block().resetWidths();
49015                         syncValue();
49016                         toolbar.editorcore.onEditorEvent();
49017                     }
49018                 },
49019                 xns : rooui.Toolbar
49020             } 
49021             
49022             
49023             
49024         ];
49025         
49026     },
49027     
49028     
49029   /**
49030      * create a DomHelper friendly object - for use with
49031      * Roo.DomHelper.markup / overwrite / etc..
49032      * ?? should it be called with option to hide all editing features?
49033      */
49034     toObject : function()
49035     {
49036         
49037         var ret = {
49038             tag : 'table',
49039             contenteditable : 'false', // this stops cell selection from picking the table.
49040             'data-block' : 'Table',
49041             style : {
49042                 width:  this.width,
49043                 border : 'solid 1px #000', // ??? hard coded?
49044                 'border-collapse' : 'collapse' 
49045             },
49046             cn : [
49047                 { tag : 'tbody' , cn : [] }
49048             ]
49049         };
49050         
49051         // do we have a head = not really 
49052         var ncols = 0;
49053         Roo.each(this.rows, function( row ) {
49054             var tr = {
49055                 tag: 'tr',
49056                 style : {
49057                     margin: '6px',
49058                     border : 'solid 1px #000',
49059                     textAlign : 'left' 
49060                 },
49061                 cn : [ ]
49062             };
49063             
49064             ret.cn[0].cn.push(tr);
49065             // does the row have any properties? ?? height?
49066             var nc = 0;
49067             Roo.each(row, function( cell ) {
49068                 
49069                 var td = {
49070                     tag : 'td',
49071                     contenteditable :  'true',
49072                     'data-block' : 'Td',
49073                     html : cell.html,
49074                     style : cell.style
49075                 };
49076                 if (cell.colspan > 1) {
49077                     td.colspan = cell.colspan ;
49078                     nc += cell.colspan;
49079                 } else {
49080                     nc++;
49081                 }
49082                 if (cell.rowspan > 1) {
49083                     td.rowspan = cell.rowspan ;
49084                 }
49085                 
49086                 
49087                 // widths ?
49088                 tr.cn.push(td);
49089                     
49090                 
49091             }, this);
49092             ncols = Math.max(nc, ncols);
49093             
49094             
49095         }, this);
49096         // add the header row..
49097         
49098         ncols++;
49099          
49100         
49101         return ret;
49102          
49103     },
49104     
49105     readElement : function(node)
49106     {
49107         node  = node ? node : this.node ;
49108         this.width = this.getVal(node, true, 'style', 'width') || '100%';
49109         
49110         this.rows = [];
49111         this.no_row = 0;
49112         var trs = Array.from(node.rows);
49113         trs.forEach(function(tr) {
49114             var row =  [];
49115             this.rows.push(row);
49116             
49117             this.no_row++;
49118             var no_column = 0;
49119             Array.from(tr.cells).forEach(function(td) {
49120                 
49121                 var add = {
49122                     colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
49123                     rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
49124                     style : td.hasAttribute('style') ? td.getAttribute('style') : '',
49125                     html : td.innerHTML
49126                 };
49127                 no_column += add.colspan;
49128                      
49129                 
49130                 row.push(add);
49131                 
49132                 
49133             },this);
49134             this.no_col = Math.max(this.no_col, no_column);
49135             
49136             
49137         },this);
49138         
49139         
49140     },
49141     normalizeRows: function()
49142     {
49143         var ret= [];
49144         var rid = -1;
49145         this.rows.forEach(function(row) {
49146             rid++;
49147             ret[rid] = [];
49148             row = this.normalizeRow(row);
49149             var cid = 0;
49150             row.forEach(function(c) {
49151                 while (typeof(ret[rid][cid]) != 'undefined') {
49152                     cid++;
49153                 }
49154                 if (typeof(ret[rid]) == 'undefined') {
49155                     ret[rid] = [];
49156                 }
49157                 ret[rid][cid] = c;
49158                 c.row = rid;
49159                 c.col = cid;
49160                 if (c.rowspan < 2) {
49161                     return;
49162                 }
49163                 
49164                 for(var i = 1 ;i < c.rowspan; i++) {
49165                     if (typeof(ret[rid+i]) == 'undefined') {
49166                         ret[rid+i] = [];
49167                     }
49168                     ret[rid+i][cid] = c;
49169                 }
49170             });
49171         }, this);
49172         return ret;
49173     
49174     },
49175     
49176     normalizeRow: function(row)
49177     {
49178         var ret= [];
49179         row.forEach(function(c) {
49180             if (c.colspan < 2) {
49181                 ret.push(c);
49182                 return;
49183             }
49184             for(var i =0 ;i < c.colspan; i++) {
49185                 ret.push(c);
49186             }
49187         });
49188         return ret;
49189     
49190     },
49191     
49192     deleteColumn : function(sel)
49193     {
49194         if (!sel || sel.type != 'col') {
49195             return;
49196         }
49197         if (this.no_col < 2) {
49198             return;
49199         }
49200         
49201         this.rows.forEach(function(row) {
49202             var cols = this.normalizeRow(row);
49203             var col = cols[sel.col];
49204             if (col.colspan > 1) {
49205                 col.colspan --;
49206             } else {
49207                 row.remove(col);
49208             }
49209             
49210         }, this);
49211         this.no_col--;
49212         
49213     },
49214     removeColumn : function()
49215     {
49216         this.deleteColumn({
49217             type: 'col',
49218             col : this.no_col-1
49219         });
49220         this.updateElement();
49221     },
49222     
49223      
49224     addColumn : function()
49225     {
49226         
49227         this.rows.forEach(function(row) {
49228             row.push(this.emptyCell());
49229            
49230         }, this);
49231         this.updateElement();
49232     },
49233     
49234     deleteRow : function(sel)
49235     {
49236         if (!sel || sel.type != 'row') {
49237             return;
49238         }
49239         
49240         if (this.no_row < 2) {
49241             return;
49242         }
49243         
49244         var rows = this.normalizeRows();
49245         
49246         
49247         rows[sel.row].forEach(function(col) {
49248             if (col.rowspan > 1) {
49249                 col.rowspan--;
49250             } else {
49251                 col.remove = 1; // flage it as removed.
49252             }
49253             
49254         }, this);
49255         var newrows = [];
49256         this.rows.forEach(function(row) {
49257             newrow = [];
49258             row.forEach(function(c) {
49259                 if (typeof(c.remove) == 'undefined') {
49260                     newrow.push(c);
49261                 }
49262                 
49263             });
49264             if (newrow.length > 0) {
49265                 newrows.push(row);
49266             }
49267         });
49268         this.rows =  newrows;
49269         
49270         
49271         
49272         this.no_row--;
49273         this.updateElement();
49274         
49275     },
49276     removeRow : function()
49277     {
49278         this.deleteRow({
49279             type: 'row',
49280             row : this.no_row-1
49281         });
49282         
49283     },
49284     
49285      
49286     addRow : function()
49287     {
49288         
49289         var row = [];
49290         for (var i = 0; i < this.no_col; i++ ) {
49291             
49292             row.push(this.emptyCell());
49293            
49294         }
49295         this.rows.push(row);
49296         this.updateElement();
49297         
49298     },
49299      
49300     // the default cell object... at present...
49301     emptyCell : function() {
49302         return (new Roo.htmleditor.BlockTd({})).toObject();
49303         
49304      
49305     },
49306     
49307     removeNode : function()
49308     {
49309         return this.node;
49310     },
49311     
49312     
49313     
49314     resetWidths : function()
49315     {
49316         Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
49317             var nn = Roo.htmleditor.Block.factory(n);
49318             nn.width = '';
49319             nn.updateElement(n);
49320         });
49321     }
49322     
49323     
49324     
49325     
49326 })
49327
49328 /**
49329  *
49330  * editing a TD?
49331  *
49332  * since selections really work on the table cell, then editing really should work from there
49333  *
49334  * The original plan was to support merging etc... - but that may not be needed yet..
49335  *
49336  * So this simple version will support:
49337  *   add/remove cols
49338  *   adjust the width +/-
49339  *   reset the width...
49340  *   
49341  *
49342  */
49343
49344
49345  
49346
49347 /**
49348  * @class Roo.htmleditor.BlockTable
49349  * Block that manages a table
49350  * 
49351  * @constructor
49352  * Create a new Filter.
49353  * @param {Object} config Configuration options
49354  */
49355
49356 Roo.htmleditor.BlockTd = function(cfg)
49357 {
49358     if (cfg.node) {
49359         this.readElement(cfg.node);
49360         this.updateElement(cfg.node);
49361     }
49362     Roo.apply(this, cfg);
49363      
49364     
49365     
49366 }
49367 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
49368  
49369     node : false,
49370     
49371     width: '',
49372     textAlign : 'left',
49373     valign : 'top',
49374     
49375     colspan : 1,
49376     rowspan : 1,
49377     
49378     
49379     // used by context menu
49380     friendly_name : 'Table Cell',
49381     deleteTitle : false, // use our customer delete
49382     
49383     // context menu is drawn once..
49384     
49385     contextMenu : function(toolbar)
49386     {
49387         
49388         var cell = function() {
49389             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
49390         };
49391         
49392         var table = function() {
49393             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
49394         };
49395         
49396         var lr = false;
49397         var saveSel = function()
49398         {
49399             lr = toolbar.editorcore.getSelection().getRangeAt(0);
49400         }
49401         var restoreSel = function()
49402         {
49403             if (lr) {
49404                 (function() {
49405                     toolbar.editorcore.focus();
49406                     var cr = toolbar.editorcore.getSelection();
49407                     cr.removeAllRanges();
49408                     cr.addRange(lr);
49409                     toolbar.editorcore.onEditorEvent();
49410                 }).defer(10, this);
49411                 
49412                 
49413             }
49414         }
49415         
49416         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
49417         
49418         var syncValue = toolbar.editorcore.syncValue;
49419         
49420         var fields = {};
49421         
49422         return [
49423             {
49424                 xtype : 'Button',
49425                 text : 'Edit Table',
49426                 listeners : {
49427                     click : function() {
49428                         var t = toolbar.tb.selectedNode.closest('table');
49429                         toolbar.editorcore.selectNode(t);
49430                         toolbar.editorcore.onEditorEvent();                        
49431                     }
49432                 }
49433                 
49434             },
49435               
49436            
49437              
49438             {
49439                 xtype : 'TextItem',
49440                 text : "Column Width: ",
49441                  xns : rooui.Toolbar 
49442                
49443             },
49444             {
49445                 xtype : 'Button',
49446                 text: '-',
49447                 listeners : {
49448                     click : function (_self, e)
49449                     {
49450                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
49451                         cell().shrinkColumn();
49452                         syncValue();
49453                          toolbar.editorcore.onEditorEvent();
49454                     }
49455                 },
49456                 xns : rooui.Toolbar
49457             },
49458             {
49459                 xtype : 'Button',
49460                 text: '+',
49461                 listeners : {
49462                     click : function (_self, e)
49463                     {
49464                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
49465                         cell().growColumn();
49466                         syncValue();
49467                         toolbar.editorcore.onEditorEvent();
49468                     }
49469                 },
49470                 xns : rooui.Toolbar
49471             },
49472             
49473             {
49474                 xtype : 'TextItem',
49475                 text : "Vertical Align: ",
49476                 xns : rooui.Toolbar  //Boostrap?
49477             },
49478             {
49479                 xtype : 'ComboBox',
49480                 allowBlank : false,
49481                 displayField : 'val',
49482                 editable : true,
49483                 listWidth : 100,
49484                 triggerAction : 'all',
49485                 typeAhead : true,
49486                 valueField : 'val',
49487                 width : 100,
49488                 name : 'valign',
49489                 listeners : {
49490                     select : function (combo, r, index)
49491                     {
49492                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
49493                         var b = cell();
49494                         b.valign = r.get('val');
49495                         b.updateElement();
49496                         syncValue();
49497                         toolbar.editorcore.onEditorEvent();
49498                     }
49499                 },
49500                 xns : rooui.form,
49501                 store : {
49502                     xtype : 'SimpleStore',
49503                     data : [
49504                         ['top'],
49505                         ['middle'],
49506                         ['bottom'] // there are afew more... 
49507                     ],
49508                     fields : [ 'val'],
49509                     xns : Roo.data
49510                 }
49511             },
49512             
49513             {
49514                 xtype : 'TextItem',
49515                 text : "Merge Cells: ",
49516                  xns : rooui.Toolbar 
49517                
49518             },
49519             
49520             
49521             {
49522                 xtype : 'Button',
49523                 text: 'Right',
49524                 listeners : {
49525                     click : function (_self, e)
49526                     {
49527                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
49528                         cell().mergeRight();
49529                         //block().growColumn();
49530                         syncValue();
49531                         toolbar.editorcore.onEditorEvent();
49532                     }
49533                 },
49534                 xns : rooui.Toolbar
49535             },
49536              
49537             {
49538                 xtype : 'Button',
49539                 text: 'Below',
49540                 listeners : {
49541                     click : function (_self, e)
49542                     {
49543                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
49544                         cell().mergeBelow();
49545                         //block().growColumn();
49546                         syncValue();
49547                         toolbar.editorcore.onEditorEvent();
49548                     }
49549                 },
49550                 xns : rooui.Toolbar
49551             },
49552             {
49553                 xtype : 'TextItem',
49554                 text : "| ",
49555                  xns : rooui.Toolbar 
49556                
49557             },
49558             
49559             {
49560                 xtype : 'Button',
49561                 text: 'Split',
49562                 listeners : {
49563                     click : function (_self, e)
49564                     {
49565                         //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
49566                         cell().split();
49567                         syncValue();
49568                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
49569                         toolbar.editorcore.onEditorEvent();
49570                                              
49571                     }
49572                 },
49573                 xns : rooui.Toolbar
49574             },
49575             {
49576                 xtype : 'Fill',
49577                 xns : rooui.Toolbar 
49578                
49579             },
49580         
49581           
49582             {
49583                 xtype : 'Button',
49584                 text: 'Delete',
49585                  
49586                 xns : rooui.Toolbar,
49587                 menu : {
49588                     xtype : 'Menu',
49589                     xns : rooui.menu,
49590                     items : [
49591                         {
49592                             xtype : 'Item',
49593                             html: 'Column',
49594                             listeners : {
49595                                 click : function (_self, e)
49596                                 {
49597                                     var t = table();
49598                                     
49599                                     cell().deleteColumn();
49600                                     syncValue();
49601                                     toolbar.editorcore.selectNode(t.node);
49602                                     toolbar.editorcore.onEditorEvent();   
49603                                 }
49604                             },
49605                             xns : rooui.menu
49606                         },
49607                         {
49608                             xtype : 'Item',
49609                             html: 'Row',
49610                             listeners : {
49611                                 click : function (_self, e)
49612                                 {
49613                                     var t = table();
49614                                     cell().deleteRow();
49615                                     syncValue();
49616                                     
49617                                     toolbar.editorcore.selectNode(t.node);
49618                                     toolbar.editorcore.onEditorEvent();   
49619                                                          
49620                                 }
49621                             },
49622                             xns : rooui.menu
49623                         },
49624                        {
49625                             xtype : 'Separator',
49626                             xns : rooui.menu
49627                         },
49628                         {
49629                             xtype : 'Item',
49630                             html: 'Table',
49631                             listeners : {
49632                                 click : function (_self, e)
49633                                 {
49634                                     var t = table();
49635                                     var nn = t.node.nextSibling || t.node.previousSibling;
49636                                     t.node.parentNode.removeChild(t.node);
49637                                     if (nn) { 
49638                                         toolbar.editorcore.selectNode(nn, true);
49639                                     }
49640                                     toolbar.editorcore.onEditorEvent();   
49641                                                          
49642                                 }
49643                             },
49644                             xns : rooui.menu
49645                         }
49646                     ]
49647                 }
49648             }
49649             
49650             // align... << fixme
49651             
49652         ];
49653         
49654     },
49655     
49656     
49657   /**
49658      * create a DomHelper friendly object - for use with
49659      * Roo.DomHelper.markup / overwrite / etc..
49660      * ?? should it be called with option to hide all editing features?
49661      */
49662  /**
49663      * create a DomHelper friendly object - for use with
49664      * Roo.DomHelper.markup / overwrite / etc..
49665      * ?? should it be called with option to hide all editing features?
49666      */
49667     toObject : function()
49668     {
49669         var ret = {
49670             tag : 'td',
49671             contenteditable : 'true', // this stops cell selection from picking the table.
49672             'data-block' : 'Td',
49673             valign : this.valign,
49674             style : {  
49675                 'text-align' :  this.textAlign,
49676                 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
49677                 'border-collapse' : 'collapse',
49678                 padding : '6px', // 8 for desktop / 4 for mobile
49679                 'vertical-align': this.valign
49680             },
49681             html : this.html
49682         };
49683         if (this.width != '') {
49684             ret.width = this.width;
49685             ret.style.width = this.width;
49686         }
49687         
49688         
49689         if (this.colspan > 1) {
49690             ret.colspan = this.colspan ;
49691         } 
49692         if (this.rowspan > 1) {
49693             ret.rowspan = this.rowspan ;
49694         }
49695         
49696            
49697         
49698         return ret;
49699          
49700     },
49701     
49702     readElement : function(node)
49703     {
49704         node  = node ? node : this.node ;
49705         this.width = node.style.width;
49706         this.colspan = Math.max(1,1*node.getAttribute('colspan'));
49707         this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
49708         this.html = node.innerHTML;
49709         if (node.style.textAlign != '') {
49710             this.textAlign = node.style.textAlign;
49711         }
49712         
49713         
49714     },
49715      
49716     // the default cell object... at present...
49717     emptyCell : function() {
49718         return {
49719             colspan :  1,
49720             rowspan :  1,
49721             textAlign : 'left',
49722             html : "&nbsp;" // is this going to be editable now?
49723         };
49724      
49725     },
49726     
49727     removeNode : function()
49728     {
49729         return this.node.closest('table');
49730          
49731     },
49732     
49733     cellData : false,
49734     
49735     colWidths : false,
49736     
49737     toTableArray  : function()
49738     {
49739         var ret = [];
49740         var tab = this.node.closest('tr').closest('table');
49741         Array.from(tab.rows).forEach(function(r, ri){
49742             ret[ri] = [];
49743         });
49744         var rn = 0;
49745         this.colWidths = [];
49746         var all_auto = true;
49747         Array.from(tab.rows).forEach(function(r, ri){
49748             
49749             var cn = 0;
49750             Array.from(r.cells).forEach(function(ce, ci){
49751                 var c =  {
49752                     cell : ce,
49753                     row : rn,
49754                     col: cn,
49755                     colspan : ce.colSpan,
49756                     rowspan : ce.rowSpan
49757                 };
49758                 if (ce.isEqualNode(this.node)) {
49759                     this.cellData = c;
49760                 }
49761                 // if we have been filled up by a row?
49762                 if (typeof(ret[rn][cn]) != 'undefined') {
49763                     while(typeof(ret[rn][cn]) != 'undefined') {
49764                         cn++;
49765                     }
49766                     c.col = cn;
49767                 }
49768                 
49769                 if (typeof(this.colWidths[cn]) == 'undefined' && c.colspan < 2) {
49770                     this.colWidths[cn] =   ce.style.width;
49771                     if (this.colWidths[cn] != '') {
49772                         all_auto = false;
49773                     }
49774                 }
49775                 
49776                 
49777                 if (c.colspan < 2 && c.rowspan < 2 ) {
49778                     ret[rn][cn] = c;
49779                     cn++;
49780                     return;
49781                 }
49782                 for(var j = 0; j < c.rowspan; j++) {
49783                     if (typeof(ret[rn+j]) == 'undefined') {
49784                         continue; // we have a problem..
49785                     }
49786                     ret[rn+j][cn] = c;
49787                     for(var i = 0; i < c.colspan; i++) {
49788                         ret[rn+j][cn+i] = c;
49789                     }
49790                 }
49791                 
49792                 cn += c.colspan;
49793             }, this);
49794             rn++;
49795         }, this);
49796         
49797         // initalize widths.?
49798         // either all widths or no widths..
49799         if (all_auto) {
49800             this.colWidths[0] = false; // no widths flag.
49801         }
49802         
49803         
49804         return ret;
49805         
49806     },
49807     
49808     
49809     
49810     
49811     mergeRight: function()
49812     {
49813          
49814         // get the contents of the next cell along..
49815         var tr = this.node.closest('tr');
49816         var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
49817         if (i >= tr.childNodes.length - 1) {
49818             return; // no cells on right to merge with.
49819         }
49820         var table = this.toTableArray();
49821         
49822         if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
49823             return; // nothing right?
49824         }
49825         var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
49826         // right cell - must be same rowspan and on the same row.
49827         if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
49828             return; // right hand side is not same rowspan.
49829         }
49830         
49831         
49832         
49833         this.node.innerHTML += ' ' + rc.cell.innerHTML;
49834         tr.removeChild(rc.cell);
49835         this.colspan += rc.colspan;
49836         this.node.setAttribute('colspan', this.colspan);
49837
49838         var table = this.toTableArray();
49839         this.normalizeWidths(table);
49840         this.updateWidths(table);
49841     },
49842     
49843     
49844     mergeBelow : function()
49845     {
49846         var table = this.toTableArray();
49847         if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
49848             return; // no row below
49849         }
49850         if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
49851             return; // nothing right?
49852         }
49853         var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
49854         
49855         if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
49856             return; // right hand side is not same rowspan.
49857         }
49858         this.node.innerHTML =  this.node.innerHTML + rc.cell.innerHTML ;
49859         rc.cell.parentNode.removeChild(rc.cell);
49860         this.rowspan += rc.rowspan;
49861         this.node.setAttribute('rowspan', this.rowspan);
49862     },
49863     
49864     split: function()
49865     {
49866         if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
49867             return;
49868         }
49869         var table = this.toTableArray();
49870         var cd = this.cellData;
49871         this.rowspan = 1;
49872         this.colspan = 1;
49873         
49874         for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
49875              
49876             
49877             for(var c = cd.col; c < cd.col + cd.colspan; c++) {
49878                 if (r == cd.row && c == cd.col) {
49879                     this.node.removeAttribute('rowspan');
49880                     this.node.removeAttribute('colspan');
49881                 }
49882                  
49883                 var ntd = this.node.cloneNode(); // which col/row should be 0..
49884                 ntd.removeAttribute('id'); 
49885                 ntd.style.width  = this.colWidths[c];
49886                 ntd.innerHTML = '';
49887                 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
49888             }
49889             
49890         }
49891         this.redrawAllCells(table);
49892         
49893     },
49894     
49895     
49896     
49897     redrawAllCells: function(table)
49898     {
49899         
49900          
49901         var tab = this.node.closest('tr').closest('table');
49902         var ctr = tab.rows[0].parentNode;
49903         Array.from(tab.rows).forEach(function(r, ri){
49904             
49905             Array.from(r.cells).forEach(function(ce, ci){
49906                 ce.parentNode.removeChild(ce);
49907             });
49908             r.parentNode.removeChild(r);
49909         });
49910         for(var r = 0 ; r < table.length; r++) {
49911             var re = tab.rows[r];
49912             
49913             var re = tab.ownerDocument.createElement('tr');
49914             ctr.appendChild(re);
49915             for(var c = 0 ; c < table[r].length; c++) {
49916                 if (table[r][c].cell === false) {
49917                     continue;
49918                 }
49919                 
49920                 re.appendChild(table[r][c].cell);
49921                  
49922                 table[r][c].cell = false;
49923             }
49924         }
49925         
49926     },
49927     updateWidths : function(table)
49928     {
49929         for(var r = 0 ; r < table.length; r++) {
49930            
49931             for(var c = 0 ; c < table[r].length; c++) {
49932                 if (table[r][c].cell === false) {
49933                     continue;
49934                 }
49935                 
49936                 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
49937                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
49938                     el.width = Math.floor(this.colWidths[c])  +'%';
49939                     el.updateElement(el.node);
49940                 }
49941                 if (this.colWidths[0] != false && table[r][c].colspan > 1) {
49942                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
49943                     var width = 0;
49944                     for(var i = 0; i < table[r][c].colspan; i ++) {
49945                         width += Math.floor(this.colWidths[c + i]);
49946                     }
49947                     el.width = width  +'%';
49948                     el.updateElement(el.node);
49949                 }
49950                 table[r][c].cell = false; // done
49951             }
49952         }
49953     },
49954     normalizeWidths : function(table)
49955     {
49956         if (this.colWidths[0] === false) {
49957             var nw = 100.0 / this.colWidths.length;
49958             this.colWidths.forEach(function(w,i) {
49959                 this.colWidths[i] = nw;
49960             },this);
49961             return;
49962         }
49963     
49964         var t = 0, missing = [];
49965         
49966         this.colWidths.forEach(function(w,i) {
49967             //if you mix % and
49968             this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
49969             var add =  this.colWidths[i];
49970             if (add > 0) {
49971                 t+=add;
49972                 return;
49973             }
49974             missing.push(i);
49975             
49976             
49977         },this);
49978         var nc = this.colWidths.length;
49979         if (missing.length) {
49980             var mult = (nc - missing.length) / (1.0 * nc);
49981             var t = mult * t;
49982             var ew = (100 -t) / (1.0 * missing.length);
49983             this.colWidths.forEach(function(w,i) {
49984                 if (w > 0) {
49985                     this.colWidths[i] = w * mult;
49986                     return;
49987                 }
49988                 
49989                 this.colWidths[i] = ew;
49990             }, this);
49991             // have to make up numbers..
49992              
49993         }
49994         // now we should have all the widths..
49995         
49996     
49997     },
49998     
49999     shrinkColumn : function()
50000     {
50001         var table = this.toTableArray();
50002         this.normalizeWidths(table);
50003         var col = this.cellData.col;
50004         var nw = this.colWidths[col] * 0.8;
50005         if (nw < 5) {
50006             return;
50007         }
50008         var otherAdd = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
50009         this.colWidths.forEach(function(w,i) {
50010             if (i == col) {
50011                  this.colWidths[i] = nw;
50012                 return;
50013             }
50014             this.colWidths[i] += otherAdd
50015         }, this);
50016         this.updateWidths(table);
50017          
50018     },
50019     growColumn : function()
50020     {
50021         var table = this.toTableArray();
50022         this.normalizeWidths(table);
50023         var col = this.cellData.col;
50024         var nw = this.colWidths[col] * 1.2;
50025         if (nw > 90) {
50026             return;
50027         }
50028         var otherSub = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
50029         this.colWidths.forEach(function(w,i) {
50030             if (i == col) {
50031                 this.colWidths[i] = nw;
50032                 return;
50033             }
50034             this.colWidths[i] -= otherSub
50035         }, this);
50036         this.updateWidths(table);
50037          
50038     },
50039     deleteRow : function()
50040     {
50041         // delete this rows 'tr'
50042         // if any of the cells in this row have a rowspan > 1 && row!= this row..
50043         // then reduce the rowspan.
50044         var table = this.toTableArray();
50045         // this.cellData.row;
50046         for (var i =0;i< table[this.cellData.row].length ; i++) {
50047             var c = table[this.cellData.row][i];
50048             if (c.row != this.cellData.row) {
50049                 
50050                 c.rowspan--;
50051                 c.cell.setAttribute('rowspan', c.rowspan);
50052                 continue;
50053             }
50054             if (c.rowspan > 1) {
50055                 c.rowspan--;
50056                 c.cell.setAttribute('rowspan', c.rowspan);
50057             }
50058         }
50059         table.splice(this.cellData.row,1);
50060         this.redrawAllCells(table);
50061         
50062     },
50063     deleteColumn : function()
50064     {
50065         var table = this.toTableArray();
50066         
50067         for (var i =0;i< table.length ; i++) {
50068             var c = table[i][this.cellData.col];
50069             if (c.col != this.cellData.col) {
50070                 table[i][this.cellData.col].colspan--;
50071             } else if (c.colspan > 1) {
50072                 c.colspan--;
50073                 c.cell.setAttribute('colspan', c.colspan);
50074             }
50075             table[i].splice(this.cellData.col,1);
50076         }
50077         
50078         this.redrawAllCells(table);
50079     }
50080     
50081     
50082     
50083     
50084 })
50085
50086 //<script type="text/javascript">
50087
50088 /*
50089  * Based  Ext JS Library 1.1.1
50090  * Copyright(c) 2006-2007, Ext JS, LLC.
50091  * LGPL
50092  *
50093  */
50094  
50095 /**
50096  * @class Roo.HtmlEditorCore
50097  * @extends Roo.Component
50098  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
50099  *
50100  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
50101  */
50102
50103 Roo.HtmlEditorCore = function(config){
50104     
50105     
50106     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
50107     
50108     
50109     this.addEvents({
50110         /**
50111          * @event initialize
50112          * Fires when the editor is fully initialized (including the iframe)
50113          * @param {Roo.HtmlEditorCore} this
50114          */
50115         initialize: true,
50116         /**
50117          * @event activate
50118          * Fires when the editor is first receives the focus. Any insertion must wait
50119          * until after this event.
50120          * @param {Roo.HtmlEditorCore} this
50121          */
50122         activate: true,
50123          /**
50124          * @event beforesync
50125          * Fires before the textarea is updated with content from the editor iframe. Return false
50126          * to cancel the sync.
50127          * @param {Roo.HtmlEditorCore} this
50128          * @param {String} html
50129          */
50130         beforesync: true,
50131          /**
50132          * @event beforepush
50133          * Fires before the iframe editor is updated with content from the textarea. Return false
50134          * to cancel the push.
50135          * @param {Roo.HtmlEditorCore} this
50136          * @param {String} html
50137          */
50138         beforepush: true,
50139          /**
50140          * @event sync
50141          * Fires when the textarea is updated with content from the editor iframe.
50142          * @param {Roo.HtmlEditorCore} this
50143          * @param {String} html
50144          */
50145         sync: true,
50146          /**
50147          * @event push
50148          * Fires when the iframe editor is updated with content from the textarea.
50149          * @param {Roo.HtmlEditorCore} this
50150          * @param {String} html
50151          */
50152         push: true,
50153         
50154         /**
50155          * @event editorevent
50156          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
50157          * @param {Roo.HtmlEditorCore} this
50158          */
50159         editorevent: true 
50160         
50161         
50162     });
50163     
50164     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
50165     
50166     // defaults : white / black...
50167     this.applyBlacklists();
50168     
50169     
50170     
50171 };
50172
50173
50174 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
50175
50176
50177      /**
50178      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
50179      */
50180     
50181     owner : false,
50182     
50183      /**
50184      * @cfg {String} css styling for resizing. (used on bootstrap only)
50185      */
50186     resize : false,
50187      /**
50188      * @cfg {Number} height (in pixels)
50189      */   
50190     height: 300,
50191    /**
50192      * @cfg {Number} width (in pixels)
50193      */   
50194     width: 500,
50195      /**
50196      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
50197      *         if you are doing an email editor, this probably needs disabling, it's designed
50198      */
50199     autoClean: true,
50200     
50201     /**
50202      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
50203      */
50204     enableBlocks : true,
50205     /**
50206      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
50207      * 
50208      */
50209     stylesheets: false,
50210      /**
50211      * @cfg {String} language default en - language of text (usefull for rtl languages)
50212      * 
50213      */
50214     language: 'en',
50215     
50216     /**
50217      * @cfg {boolean} allowComments - default false - allow comments in HTML source
50218      *          - by default they are stripped - if you are editing email you may need this.
50219      */
50220     allowComments: false,
50221     // id of frame..
50222     frameId: false,
50223     
50224     // private properties
50225     validationEvent : false,
50226     deferHeight: true,
50227     initialized : false,
50228     activated : false,
50229     sourceEditMode : false,
50230     onFocus : Roo.emptyFn,
50231     iframePad:3,
50232     hideMode:'offsets',
50233     
50234     clearUp: true,
50235     
50236     // blacklist + whitelisted elements..
50237     black: false,
50238     white: false,
50239      
50240     bodyCls : '',
50241
50242     
50243     undoManager : false,
50244     /**
50245      * Protected method that will not generally be called directly. It
50246      * is called when the editor initializes the iframe with HTML contents. Override this method if you
50247      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
50248      */
50249     getDocMarkup : function(){
50250         // body styles..
50251         var st = '';
50252         
50253         // inherit styels from page...?? 
50254         if (this.stylesheets === false) {
50255             
50256             Roo.get(document.head).select('style').each(function(node) {
50257                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
50258             });
50259             
50260             Roo.get(document.head).select('link').each(function(node) { 
50261                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
50262             });
50263             
50264         } else if (!this.stylesheets.length) {
50265                 // simple..
50266                 st = '<style type="text/css">' +
50267                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
50268                    '</style>';
50269         } else {
50270             for (var i in this.stylesheets) {
50271                 if (typeof(this.stylesheets[i]) != 'string') {
50272                     continue;
50273                 }
50274                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
50275             }
50276             
50277         }
50278         
50279         st +=  '<style type="text/css">' +
50280             'IMG { cursor: pointer } ' +
50281         '</style>';
50282         
50283         st += '<meta name="google" content="notranslate">';
50284         
50285         var cls = 'notranslate roo-htmleditor-body';
50286         
50287         if(this.bodyCls.length){
50288             cls += ' ' + this.bodyCls;
50289         }
50290         
50291         return '<html  class="notranslate" translate="no"><head>' + st  +
50292             //<style type="text/css">' +
50293             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
50294             //'</style>' +
50295             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
50296     },
50297
50298     // private
50299     onRender : function(ct, position)
50300     {
50301         var _t = this;
50302         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
50303         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
50304         
50305         
50306         this.el.dom.style.border = '0 none';
50307         this.el.dom.setAttribute('tabIndex', -1);
50308         this.el.addClass('x-hidden hide');
50309         
50310         
50311         
50312         if(Roo.isIE){ // fix IE 1px bogus margin
50313             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
50314         }
50315        
50316         
50317         this.frameId = Roo.id();
50318         
50319         var ifcfg = {
50320             tag: 'iframe',
50321             cls: 'form-control', // bootstrap..
50322             id: this.frameId,
50323             name: this.frameId,
50324             frameBorder : 'no',
50325             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
50326         };
50327         if (this.resize) {
50328             ifcfg.style = { resize : this.resize };
50329         }
50330         
50331         var iframe = this.owner.wrap.createChild(ifcfg, this.el); 
50332         
50333         
50334         this.iframe = iframe.dom;
50335
50336         this.assignDocWin();
50337         
50338         this.doc.designMode = 'on';
50339        
50340         this.doc.open();
50341         this.doc.write(this.getDocMarkup());
50342         this.doc.close();
50343
50344         
50345         var task = { // must defer to wait for browser to be ready
50346             run : function(){
50347                 //console.log("run task?" + this.doc.readyState);
50348                 this.assignDocWin();
50349                 if(this.doc.body || this.doc.readyState == 'complete'){
50350                     try {
50351                         this.doc.designMode="on";
50352                         
50353                     } catch (e) {
50354                         return;
50355                     }
50356                     Roo.TaskMgr.stop(task);
50357                     this.initEditor.defer(10, this);
50358                 }
50359             },
50360             interval : 10,
50361             duration: 10000,
50362             scope: this
50363         };
50364         Roo.TaskMgr.start(task);
50365
50366     },
50367
50368     // private
50369     onResize : function(w, h)
50370     {
50371          Roo.log('resize: ' +w + ',' + h );
50372         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
50373         if(!this.iframe){
50374             return;
50375         }
50376         if(typeof w == 'number'){
50377             
50378             this.iframe.style.width = w + 'px';
50379         }
50380         if(typeof h == 'number'){
50381             
50382             this.iframe.style.height = h + 'px';
50383             if(this.doc){
50384                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
50385             }
50386         }
50387         
50388     },
50389
50390     /**
50391      * Toggles the editor between standard and source edit mode.
50392      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
50393      */
50394     toggleSourceEdit : function(sourceEditMode){
50395         
50396         this.sourceEditMode = sourceEditMode === true;
50397         
50398         if(this.sourceEditMode){
50399  
50400             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
50401             
50402         }else{
50403             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
50404             //this.iframe.className = '';
50405             this.deferFocus();
50406         }
50407         //this.setSize(this.owner.wrap.getSize());
50408         //this.fireEvent('editmodechange', this, this.sourceEditMode);
50409     },
50410
50411     
50412   
50413
50414     /**
50415      * Protected method that will not generally be called directly. If you need/want
50416      * custom HTML cleanup, this is the method you should override.
50417      * @param {String} html The HTML to be cleaned
50418      * return {String} The cleaned HTML
50419      */
50420     cleanHtml : function(html)
50421     {
50422         html = String(html);
50423         if(html.length > 5){
50424             if(Roo.isSafari){ // strip safari nonsense
50425                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
50426             }
50427         }
50428         if(html == '&nbsp;'){
50429             html = '';
50430         }
50431         return html;
50432     },
50433
50434     /**
50435      * HTML Editor -> Textarea
50436      * Protected method that will not generally be called directly. Syncs the contents
50437      * of the editor iframe with the textarea.
50438      */
50439     syncValue : function()
50440     {
50441         //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
50442         if(this.initialized){
50443             
50444             if (this.undoManager) {
50445                 this.undoManager.addEvent();
50446             }
50447
50448             
50449             var bd = (this.doc.body || this.doc.documentElement);
50450            
50451             
50452             var sel = this.win.getSelection();
50453             
50454             var div = document.createElement('div');
50455             div.innerHTML = bd.innerHTML;
50456             var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
50457             if (gtx.length > 0) {
50458                 var rm = gtx.item(0).parentNode;
50459                 rm.parentNode.removeChild(rm);
50460             }
50461             
50462            
50463             if (this.enableBlocks) {
50464                 new Roo.htmleditor.FilterBlock({ node : div });
50465             }
50466             
50467             var html = div.innerHTML;
50468             
50469             //?? tidy?
50470             if (this.autoClean) {
50471                 
50472                 new Roo.htmleditor.FilterAttributes({
50473                     node : div,
50474                     attrib_white : [
50475                             'href',
50476                             'src',
50477                             'name',
50478                             'align',
50479                             'colspan',
50480                             'rowspan',
50481                             'data-display',
50482                             'data-caption-display',
50483                             'data-width',
50484                             'data-caption',
50485                             'start' ,
50486                             'style',
50487                             // youtube embed.
50488                             'class',
50489                             'allowfullscreen',
50490                             'frameborder',
50491                             'width',
50492                             'height',
50493                             'alt'
50494                             ],
50495                     attrib_clean : ['href', 'src' ] 
50496                 });
50497                 
50498                 var tidy = new Roo.htmleditor.TidySerializer({
50499                     inner:  true
50500                 });
50501                 html  = tidy.serialize(div);
50502                 
50503             }
50504             
50505             
50506             if(Roo.isSafari){
50507                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
50508                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
50509                 if(m && m[1]){
50510                     html = '<div style="'+m[0]+'">' + html + '</div>';
50511                 }
50512             }
50513             html = this.cleanHtml(html);
50514             // fix up the special chars.. normaly like back quotes in word...
50515             // however we do not want to do this with chinese..
50516             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
50517                 
50518                 var cc = match.charCodeAt();
50519
50520                 // Get the character value, handling surrogate pairs
50521                 if (match.length == 2) {
50522                     // It's a surrogate pair, calculate the Unicode code point
50523                     var high = match.charCodeAt(0) - 0xD800;
50524                     var low  = match.charCodeAt(1) - 0xDC00;
50525                     cc = (high * 0x400) + low + 0x10000;
50526                 }  else if (
50527                     (cc >= 0x4E00 && cc < 0xA000 ) ||
50528                     (cc >= 0x3400 && cc < 0x4E00 ) ||
50529                     (cc >= 0xf900 && cc < 0xfb00 )
50530                 ) {
50531                         return match;
50532                 }  
50533          
50534                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
50535                 return "&#" + cc + ";";
50536                 
50537                 
50538             });
50539             
50540             
50541              
50542             if(this.owner.fireEvent('beforesync', this, html) !== false){
50543                 this.el.dom.value = html;
50544                 this.owner.fireEvent('sync', this, html);
50545             }
50546         }
50547     },
50548
50549     /**
50550      * TEXTAREA -> EDITABLE
50551      * Protected method that will not generally be called directly. Pushes the value of the textarea
50552      * into the iframe editor.
50553      */
50554     pushValue : function()
50555     {
50556         //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
50557         if(this.initialized){
50558             var v = this.el.dom.value.trim();
50559             
50560             
50561             if(this.owner.fireEvent('beforepush', this, v) !== false){
50562                 var d = (this.doc.body || this.doc.documentElement);
50563                 d.innerHTML = v;
50564                  
50565                 this.el.dom.value = d.innerHTML;
50566                 this.owner.fireEvent('push', this, v);
50567             }
50568             if (this.autoClean) {
50569                 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
50570                 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
50571             }
50572             if (this.enableBlocks) {
50573                 Roo.htmleditor.Block.initAll(this.doc.body);
50574             }
50575             
50576             this.updateLanguage();
50577             
50578             var lc = this.doc.body.lastChild;
50579             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
50580                 // add an extra line at the end.
50581                 this.doc.body.appendChild(this.doc.createElement('br'));
50582             }
50583             
50584             
50585         }
50586     },
50587
50588     // private
50589     deferFocus : function(){
50590         this.focus.defer(10, this);
50591     },
50592
50593     // doc'ed in Field
50594     focus : function(){
50595         if(this.win && !this.sourceEditMode){
50596             this.win.focus();
50597         }else{
50598             this.el.focus();
50599         }
50600     },
50601     
50602     assignDocWin: function()
50603     {
50604         var iframe = this.iframe;
50605         
50606          if(Roo.isIE){
50607             this.doc = iframe.contentWindow.document;
50608             this.win = iframe.contentWindow;
50609         } else {
50610 //            if (!Roo.get(this.frameId)) {
50611 //                return;
50612 //            }
50613 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
50614 //            this.win = Roo.get(this.frameId).dom.contentWindow;
50615             
50616             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
50617                 return;
50618             }
50619             
50620             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
50621             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
50622         }
50623     },
50624     
50625     // private
50626     initEditor : function(){
50627         //console.log("INIT EDITOR");
50628         this.assignDocWin();
50629         
50630         
50631         
50632         this.doc.designMode="on";
50633         this.doc.open();
50634         this.doc.write(this.getDocMarkup());
50635         this.doc.close();
50636         
50637         var dbody = (this.doc.body || this.doc.documentElement);
50638         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
50639         // this copies styles from the containing element into thsi one..
50640         // not sure why we need all of this..
50641         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
50642         
50643         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
50644         //ss['background-attachment'] = 'fixed'; // w3c
50645         dbody.bgProperties = 'fixed'; // ie
50646         dbody.setAttribute("translate", "no");
50647         
50648         //Roo.DomHelper.applyStyles(dbody, ss);
50649         Roo.EventManager.on(this.doc, {
50650              
50651             'mouseup': this.onEditorEvent,
50652             'dblclick': this.onEditorEvent,
50653             'click': this.onEditorEvent,
50654             'keyup': this.onEditorEvent,
50655             
50656             buffer:100,
50657             scope: this
50658         });
50659         Roo.EventManager.on(this.doc, {
50660             'paste': this.onPasteEvent,
50661             scope : this
50662         });
50663         if(Roo.isGecko){
50664             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
50665         }
50666         //??? needed???
50667         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
50668             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
50669         }
50670         this.initialized = true;
50671
50672         
50673         // initialize special key events - enter
50674         new Roo.htmleditor.KeyEnter({core : this});
50675         
50676          
50677         
50678         this.owner.fireEvent('initialize', this);
50679         this.pushValue();
50680     },
50681     // this is to prevent a href clicks resulting in a redirect?
50682    
50683     onPasteEvent : function(e,v)
50684     {
50685         // I think we better assume paste is going to be a dirty load of rubish from word..
50686         
50687         // even pasting into a 'email version' of this widget will have to clean up that mess.
50688         var cd = (e.browserEvent.clipboardData || window.clipboardData);
50689         
50690         // check what type of paste - if it's an image, then handle it differently.
50691         if (cd.files && cd.files.length > 0 && cd.types.indexOf('text/html') < 0) {
50692             // pasting images? 
50693             var urlAPI = (window.createObjectURL && window) || 
50694                 (window.URL && URL.revokeObjectURL && URL) || 
50695                 (window.webkitURL && webkitURL);
50696             
50697             var r = new FileReader();
50698             var t = this;
50699             r.addEventListener('load',function()
50700             {
50701                 
50702                 var d = (new DOMParser().parseFromString('<img src="' + r.result+ '">', 'text/html')).body;
50703                 // is insert asycn?
50704                 if (t.enableBlocks) {
50705                     
50706                     Array.from(d.getElementsByTagName('img')).forEach(function(img) {
50707                         if (img.closest('figure')) { // assume!! that it's aready
50708                             return;
50709                         }
50710                         var fig  = new Roo.htmleditor.BlockFigure({
50711                             image_src  : img.src
50712                         });
50713                         fig.updateElement(img); // replace it..
50714                         
50715                     });
50716                 }
50717                 t.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
50718                 t.owner.fireEvent('paste', this);
50719             });
50720             r.readAsDataURL(cd.files[0]);
50721             
50722             e.preventDefault();
50723             
50724             return false;
50725         }
50726         if (cd.types.indexOf('text/html') < 0 ) {
50727             return false;
50728         }
50729         var images = [];
50730         var html = cd.getData('text/html'); // clipboard event
50731         if (cd.types.indexOf('text/rtf') > -1) {
50732             var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
50733             images = parser.doc ? parser.doc.getElementsByType('pict') : [];
50734         }
50735         // Roo.log(images);
50736         // Roo.log(imgs);
50737         // fixme..
50738         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
50739                        .map(function(g) { return g.toDataURL(); })
50740                        .filter(function(g) { return g != 'about:blank'; });
50741         
50742         //Roo.log(html);
50743         html = this.cleanWordChars(html);
50744         
50745         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
50746         
50747         
50748         var sn = this.getParentElement();
50749         // check if d contains a table, and prevent nesting??
50750         //Roo.log(d.getElementsByTagName('table'));
50751         //Roo.log(sn);
50752         //Roo.log(sn.closest('table'));
50753         if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
50754             e.preventDefault();
50755             this.insertAtCursor("You can not nest tables");
50756             //Roo.log("prevent?"); // fixme - 
50757             return false;
50758         }
50759         
50760         
50761         
50762         if (images.length > 0) {
50763             // replace all v:imagedata - with img.
50764             var ar = Array.from(d.getElementsByTagName('v:imagedata'));
50765             Roo.each(ar, function(node) {
50766                 node.parentNode.insertBefore(d.ownerDocument.createElement('img'), node );
50767                 node.parentNode.removeChild(node);
50768             });
50769             
50770             
50771             Roo.each(d.getElementsByTagName('img'), function(img, i) {
50772                 img.setAttribute('src', images[i]);
50773             });
50774         }
50775         if (this.autoClean) {
50776             new Roo.htmleditor.FilterWord({ node : d });
50777             
50778             new Roo.htmleditor.FilterStyleToTag({ node : d });
50779             new Roo.htmleditor.FilterAttributes({
50780                 node : d,
50781                 attrib_white : [
50782                     'href',
50783                     'src',
50784                     'name',
50785                     'align',
50786                     'colspan',
50787                     'rowspan' 
50788                 /*  THESE ARE NOT ALLWOED FOR PASTE
50789                  *    'data-display',
50790                     'data-caption-display',
50791                     'data-width',
50792                     'data-caption',
50793                     'start' ,
50794                     'style',
50795                     // youtube embed.
50796                     'class',
50797                     'allowfullscreen',
50798                     'frameborder',
50799                     'width',
50800                     'height',
50801                     'alt'
50802                     */
50803                     ],
50804                 attrib_clean : ['href', 'src' ] 
50805             });
50806             new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
50807             // should be fonts..
50808             new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', ':' ]} );
50809             new Roo.htmleditor.FilterParagraph({ node : d });
50810             new Roo.htmleditor.FilterHashLink({node : d});
50811             new Roo.htmleditor.FilterSpan({ node : d });
50812             new Roo.htmleditor.FilterLongBr({ node : d });
50813             new Roo.htmleditor.FilterComment({ node : d });
50814             
50815             
50816         }
50817         if (this.enableBlocks) {
50818                 
50819             Array.from(d.getElementsByTagName('img')).forEach(function(img) {
50820                 if (img.closest('figure')) { // assume!! that it's aready
50821                     return;
50822                 }
50823                 var fig  = new Roo.htmleditor.BlockFigure({
50824                     image_src  : img.src
50825                 });
50826                 fig.updateElement(img); // replace it..
50827                 
50828             });
50829         }
50830         
50831         
50832         this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
50833         if (this.enableBlocks) {
50834             Roo.htmleditor.Block.initAll(this.doc.body);
50835         }
50836          
50837         
50838         e.preventDefault();
50839         this.owner.fireEvent('paste', this);
50840         return false;
50841         // default behaveiour should be our local cleanup paste? (optional?)
50842         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
50843         //this.owner.fireEvent('paste', e, v);
50844     },
50845     // private
50846     onDestroy : function(){
50847         
50848         
50849         
50850         if(this.rendered){
50851             
50852             //for (var i =0; i < this.toolbars.length;i++) {
50853             //    // fixme - ask toolbars for heights?
50854             //    this.toolbars[i].onDestroy();
50855            // }
50856             
50857             //this.wrap.dom.innerHTML = '';
50858             //this.wrap.remove();
50859         }
50860     },
50861
50862     // private
50863     onFirstFocus : function(){
50864         
50865         this.assignDocWin();
50866         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
50867         
50868         this.activated = true;
50869          
50870     
50871         if(Roo.isGecko){ // prevent silly gecko errors
50872             this.win.focus();
50873             var s = this.win.getSelection();
50874             if(!s.focusNode || s.focusNode.nodeType != 3){
50875                 var r = s.getRangeAt(0);
50876                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
50877                 r.collapse(true);
50878                 this.deferFocus();
50879             }
50880             try{
50881                 this.execCmd('useCSS', true);
50882                 this.execCmd('styleWithCSS', false);
50883             }catch(e){}
50884         }
50885         this.owner.fireEvent('activate', this);
50886     },
50887
50888     // private
50889     adjustFont: function(btn){
50890         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
50891         //if(Roo.isSafari){ // safari
50892         //    adjust *= 2;
50893        // }
50894         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
50895         if(Roo.isSafari){ // safari
50896             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
50897             v =  (v < 10) ? 10 : v;
50898             v =  (v > 48) ? 48 : v;
50899             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
50900             
50901         }
50902         
50903         
50904         v = Math.max(1, v+adjust);
50905         
50906         this.execCmd('FontSize', v  );
50907     },
50908
50909     onEditorEvent : function(e)
50910     {
50911          
50912         
50913         if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
50914             return; // we do not handle this.. (undo manager does..)
50915         }
50916         // clicking a 'block'?
50917         
50918         // in theory this detects if the last element is not a br, then we try and do that.
50919         // its so clicking in space at bottom triggers adding a br and moving the cursor.
50920         if (e &&
50921             e.target.nodeName == 'BODY' &&
50922             e.type == "mouseup" &&
50923             this.doc.body.lastChild
50924            ) {
50925             var lc = this.doc.body.lastChild;
50926             // gtx-trans is google translate plugin adding crap.
50927             while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
50928                 lc = lc.previousSibling;
50929             }
50930             if (lc.nodeType == 1 && lc.nodeName != 'BR') {
50931             // if last element is <BR> - then dont do anything.
50932             
50933                 var ns = this.doc.createElement('br');
50934                 this.doc.body.appendChild(ns);
50935                 range = this.doc.createRange();
50936                 range.setStartAfter(ns);
50937                 range.collapse(true);
50938                 var sel = this.win.getSelection();
50939                 sel.removeAllRanges();
50940                 sel.addRange(range);
50941             }
50942         }
50943         
50944         
50945         
50946         this.fireEditorEvent(e);
50947       //  this.updateToolbar();
50948         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
50949     },
50950     
50951     fireEditorEvent: function(e)
50952     {
50953         this.owner.fireEvent('editorevent', this, e);
50954     },
50955
50956     insertTag : function(tg)
50957     {
50958         // could be a bit smarter... -> wrap the current selected tRoo..
50959         if (tg.toLowerCase() == 'span' ||
50960             tg.toLowerCase() == 'code' ||
50961             tg.toLowerCase() == 'sup' ||
50962             tg.toLowerCase() == 'sub' 
50963             ) {
50964             
50965             range = this.createRange(this.getSelection());
50966             var wrappingNode = this.doc.createElement(tg.toLowerCase());
50967             wrappingNode.appendChild(range.extractContents());
50968             range.insertNode(wrappingNode);
50969
50970             return;
50971             
50972             
50973             
50974         }
50975         this.execCmd("formatblock",   tg);
50976         this.undoManager.addEvent(); 
50977     },
50978     
50979     insertText : function(txt)
50980     {
50981         
50982         
50983         var range = this.createRange();
50984         range.deleteContents();
50985                //alert(Sender.getAttribute('label'));
50986                
50987         range.insertNode(this.doc.createTextNode(txt));
50988         this.undoManager.addEvent();
50989     } ,
50990     
50991      
50992
50993     /**
50994      * Executes a Midas editor command on the editor document and performs necessary focus and
50995      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
50996      * @param {String} cmd The Midas command
50997      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
50998      */
50999     relayCmd : function(cmd, value)
51000     {
51001         
51002         switch (cmd) {
51003             case 'justifyleft':
51004             case 'justifyright':
51005             case 'justifycenter':
51006                 // if we are in a cell, then we will adjust the
51007                 var n = this.getParentElement();
51008                 var td = n.closest('td');
51009                 if (td) {
51010                     var bl = Roo.htmleditor.Block.factory(td);
51011                     bl.textAlign = cmd.replace('justify','');
51012                     bl.updateElement();
51013                     this.owner.fireEvent('editorevent', this);
51014                     return;
51015                 }
51016                 this.execCmd('styleWithCSS', true); // 
51017                 break;
51018             case 'bold':
51019             case 'italic':
51020             case 'underline':                
51021                 // if there is no selection, then we insert, and set the curson inside it..
51022                 this.execCmd('styleWithCSS', false); 
51023                 break;
51024                 
51025         
51026             default:
51027                 break;
51028         }
51029         
51030         
51031         this.win.focus();
51032         this.execCmd(cmd, value);
51033         this.owner.fireEvent('editorevent', this);
51034         //this.updateToolbar();
51035         this.owner.deferFocus();
51036     },
51037
51038     /**
51039      * Executes a Midas editor command directly on the editor document.
51040      * For visual commands, you should use {@link #relayCmd} instead.
51041      * <b>This should only be called after the editor is initialized.</b>
51042      * @param {String} cmd The Midas command
51043      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
51044      */
51045     execCmd : function(cmd, value){
51046         this.doc.execCommand(cmd, false, value === undefined ? null : value);
51047         this.syncValue();
51048     },
51049  
51050  
51051    
51052     /**
51053      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
51054      * to insert tRoo.
51055      * @param {String} text | dom node.. 
51056      */
51057     insertAtCursor : function(text)
51058     {
51059         
51060         if(!this.activated){
51061             return;
51062         }
51063          
51064         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
51065             this.win.focus();
51066             
51067             
51068             // from jquery ui (MIT licenced)
51069             var range, node;
51070             var win = this.win;
51071             
51072             if (win.getSelection && win.getSelection().getRangeAt) {
51073                 
51074                 // delete the existing?
51075                 
51076                 this.createRange(this.getSelection()).deleteContents();
51077                 range = win.getSelection().getRangeAt(0);
51078                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
51079                 range.insertNode(node);
51080                 range = range.cloneRange();
51081                 range.collapse(false);
51082                  
51083                 win.getSelection().removeAllRanges();
51084                 win.getSelection().addRange(range);
51085                 
51086                 
51087                 
51088             } else if (win.document.selection && win.document.selection.createRange) {
51089                 // no firefox support
51090                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
51091                 win.document.selection.createRange().pasteHTML(txt);
51092             
51093             } else {
51094                 // no firefox support
51095                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
51096                 this.execCmd('InsertHTML', txt);
51097             } 
51098             this.syncValue();
51099             
51100             this.deferFocus();
51101         }
51102     },
51103  // private
51104     mozKeyPress : function(e){
51105         if(e.ctrlKey){
51106             var c = e.getCharCode(), cmd;
51107           
51108             if(c > 0){
51109                 c = String.fromCharCode(c).toLowerCase();
51110                 switch(c){
51111                     case 'b':
51112                         cmd = 'bold';
51113                         break;
51114                     case 'i':
51115                         cmd = 'italic';
51116                         break;
51117                     
51118                     case 'u':
51119                         cmd = 'underline';
51120                         break;
51121                     
51122                     //case 'v':
51123                       //  this.cleanUpPaste.defer(100, this);
51124                       //  return;
51125                         
51126                 }
51127                 if(cmd){
51128                     
51129                     this.relayCmd(cmd);
51130                     //this.win.focus();
51131                     //this.execCmd(cmd);
51132                     //this.deferFocus();
51133                     e.preventDefault();
51134                 }
51135                 
51136             }
51137         }
51138     },
51139
51140     // private
51141     fixKeys : function(){ // load time branching for fastest keydown performance
51142         
51143         
51144         if(Roo.isIE){
51145             return function(e){
51146                 var k = e.getKey(), r;
51147                 if(k == e.TAB){
51148                     e.stopEvent();
51149                     r = this.doc.selection.createRange();
51150                     if(r){
51151                         r.collapse(true);
51152                         r.pasteHTML('&#160;&#160;&#160;&#160;');
51153                         this.deferFocus();
51154                     }
51155                     return;
51156                 }
51157                 /// this is handled by Roo.htmleditor.KeyEnter
51158                  /*
51159                 if(k == e.ENTER){
51160                     r = this.doc.selection.createRange();
51161                     if(r){
51162                         var target = r.parentElement();
51163                         if(!target || target.tagName.toLowerCase() != 'li'){
51164                             e.stopEvent();
51165                             r.pasteHTML('<br/>');
51166                             r.collapse(false);
51167                             r.select();
51168                         }
51169                     }
51170                 }
51171                 */
51172                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
51173                 //    this.cleanUpPaste.defer(100, this);
51174                 //    return;
51175                 //}
51176                 
51177                 
51178             };
51179         }else if(Roo.isOpera){
51180             return function(e){
51181                 var k = e.getKey();
51182                 if(k == e.TAB){
51183                     e.stopEvent();
51184                     this.win.focus();
51185                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
51186                     this.deferFocus();
51187                 }
51188                
51189                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
51190                 //    this.cleanUpPaste.defer(100, this);
51191                  //   return;
51192                 //}
51193                 
51194             };
51195         }else if(Roo.isSafari){
51196             return function(e){
51197                 var k = e.getKey();
51198                 
51199                 if(k == e.TAB){
51200                     e.stopEvent();
51201                     this.execCmd('InsertText','\t');
51202                     this.deferFocus();
51203                     return;
51204                 }
51205                  this.mozKeyPress(e);
51206                 
51207                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
51208                  //   this.cleanUpPaste.defer(100, this);
51209                  //   return;
51210                // }
51211                 
51212              };
51213         }
51214     }(),
51215     
51216     getAllAncestors: function()
51217     {
51218         var p = this.getSelectedNode();
51219         var a = [];
51220         if (!p) {
51221             a.push(p); // push blank onto stack..
51222             p = this.getParentElement();
51223         }
51224         
51225         
51226         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
51227             a.push(p);
51228             p = p.parentNode;
51229         }
51230         a.push(this.doc.body);
51231         return a;
51232     },
51233     lastSel : false,
51234     lastSelNode : false,
51235     
51236     
51237     getSelection : function() 
51238     {
51239         this.assignDocWin();
51240         return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
51241     },
51242     /**
51243      * Select a dom node
51244      * @param {DomElement} node the node to select
51245      */
51246     selectNode : function(node, collapse)
51247     {
51248         var nodeRange = node.ownerDocument.createRange();
51249         try {
51250             nodeRange.selectNode(node);
51251         } catch (e) {
51252             nodeRange.selectNodeContents(node);
51253         }
51254         if (collapse === true) {
51255             nodeRange.collapse(true);
51256         }
51257         //
51258         var s = this.win.getSelection();
51259         s.removeAllRanges();
51260         s.addRange(nodeRange);
51261     },
51262     
51263     getSelectedNode: function() 
51264     {
51265         // this may only work on Gecko!!!
51266         
51267         // should we cache this!!!!
51268         
51269          
51270          
51271         var range = this.createRange(this.getSelection()).cloneRange();
51272         
51273         if (Roo.isIE) {
51274             var parent = range.parentElement();
51275             while (true) {
51276                 var testRange = range.duplicate();
51277                 testRange.moveToElementText(parent);
51278                 if (testRange.inRange(range)) {
51279                     break;
51280                 }
51281                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
51282                     break;
51283                 }
51284                 parent = parent.parentElement;
51285             }
51286             return parent;
51287         }
51288         
51289         // is ancestor a text element.
51290         var ac =  range.commonAncestorContainer;
51291         if (ac.nodeType == 3) {
51292             ac = ac.parentNode;
51293         }
51294         
51295         var ar = ac.childNodes;
51296          
51297         var nodes = [];
51298         var other_nodes = [];
51299         var has_other_nodes = false;
51300         for (var i=0;i<ar.length;i++) {
51301             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
51302                 continue;
51303             }
51304             // fullly contained node.
51305             
51306             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
51307                 nodes.push(ar[i]);
51308                 continue;
51309             }
51310             
51311             // probably selected..
51312             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
51313                 other_nodes.push(ar[i]);
51314                 continue;
51315             }
51316             // outer..
51317             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
51318                 continue;
51319             }
51320             
51321             
51322             has_other_nodes = true;
51323         }
51324         if (!nodes.length && other_nodes.length) {
51325             nodes= other_nodes;
51326         }
51327         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
51328             return false;
51329         }
51330         
51331         return nodes[0];
51332     },
51333     
51334     
51335     createRange: function(sel)
51336     {
51337         // this has strange effects when using with 
51338         // top toolbar - not sure if it's a great idea.
51339         //this.editor.contentWindow.focus();
51340         if (typeof sel != "undefined") {
51341             try {
51342                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
51343             } catch(e) {
51344                 return this.doc.createRange();
51345             }
51346         } else {
51347             return this.doc.createRange();
51348         }
51349     },
51350     getParentElement: function()
51351     {
51352         
51353         this.assignDocWin();
51354         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
51355         
51356         var range = this.createRange(sel);
51357          
51358         try {
51359             var p = range.commonAncestorContainer;
51360             while (p.nodeType == 3) { // text node
51361                 p = p.parentNode;
51362             }
51363             return p;
51364         } catch (e) {
51365             return null;
51366         }
51367     
51368     },
51369     /***
51370      *
51371      * Range intersection.. the hard stuff...
51372      *  '-1' = before
51373      *  '0' = hits..
51374      *  '1' = after.
51375      *         [ -- selected range --- ]
51376      *   [fail]                        [fail]
51377      *
51378      *    basically..
51379      *      if end is before start or  hits it. fail.
51380      *      if start is after end or hits it fail.
51381      *
51382      *   if either hits (but other is outside. - then it's not 
51383      *   
51384      *    
51385      **/
51386     
51387     
51388     // @see http://www.thismuchiknow.co.uk/?p=64.
51389     rangeIntersectsNode : function(range, node)
51390     {
51391         var nodeRange = node.ownerDocument.createRange();
51392         try {
51393             nodeRange.selectNode(node);
51394         } catch (e) {
51395             nodeRange.selectNodeContents(node);
51396         }
51397     
51398         var rangeStartRange = range.cloneRange();
51399         rangeStartRange.collapse(true);
51400     
51401         var rangeEndRange = range.cloneRange();
51402         rangeEndRange.collapse(false);
51403     
51404         var nodeStartRange = nodeRange.cloneRange();
51405         nodeStartRange.collapse(true);
51406     
51407         var nodeEndRange = nodeRange.cloneRange();
51408         nodeEndRange.collapse(false);
51409     
51410         return rangeStartRange.compareBoundaryPoints(
51411                  Range.START_TO_START, nodeEndRange) == -1 &&
51412                rangeEndRange.compareBoundaryPoints(
51413                  Range.START_TO_START, nodeStartRange) == 1;
51414         
51415          
51416     },
51417     rangeCompareNode : function(range, node)
51418     {
51419         var nodeRange = node.ownerDocument.createRange();
51420         try {
51421             nodeRange.selectNode(node);
51422         } catch (e) {
51423             nodeRange.selectNodeContents(node);
51424         }
51425         
51426         
51427         range.collapse(true);
51428     
51429         nodeRange.collapse(true);
51430      
51431         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
51432         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
51433          
51434         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
51435         
51436         var nodeIsBefore   =  ss == 1;
51437         var nodeIsAfter    = ee == -1;
51438         
51439         if (nodeIsBefore && nodeIsAfter) {
51440             return 0; // outer
51441         }
51442         if (!nodeIsBefore && nodeIsAfter) {
51443             return 1; //right trailed.
51444         }
51445         
51446         if (nodeIsBefore && !nodeIsAfter) {
51447             return 2;  // left trailed.
51448         }
51449         // fully contined.
51450         return 3;
51451     },
51452  
51453     cleanWordChars : function(input) {// change the chars to hex code
51454         
51455        var swapCodes  = [ 
51456             [    8211, "&#8211;" ], 
51457             [    8212, "&#8212;" ], 
51458             [    8216,  "'" ],  
51459             [    8217, "'" ],  
51460             [    8220, '"' ],  
51461             [    8221, '"' ],  
51462             [    8226, "*" ],  
51463             [    8230, "..." ]
51464         ]; 
51465         var output = input;
51466         Roo.each(swapCodes, function(sw) { 
51467             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
51468             
51469             output = output.replace(swapper, sw[1]);
51470         });
51471         
51472         return output;
51473     },
51474     
51475      
51476     
51477         
51478     
51479     cleanUpChild : function (node)
51480     {
51481         
51482         new Roo.htmleditor.FilterComment({node : node});
51483         new Roo.htmleditor.FilterAttributes({
51484                 node : node,
51485                 attrib_black : this.ablack,
51486                 attrib_clean : this.aclean,
51487                 style_white : this.cwhite,
51488                 style_black : this.cblack
51489         });
51490         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
51491         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
51492          
51493         
51494     },
51495     
51496     /**
51497      * Clean up MS wordisms...
51498      * @deprecated - use filter directly
51499      */
51500     cleanWord : function(node)
51501     {
51502         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
51503         new Roo.htmleditor.FilterKeepChildren({node : node ? node : this.doc.body, tag : [ 'FONT', ':' ]} );
51504         
51505     },
51506    
51507     
51508     /**
51509
51510      * @deprecated - use filters
51511      */
51512     cleanTableWidths : function(node)
51513     {
51514         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
51515         
51516  
51517     },
51518     
51519      
51520         
51521     applyBlacklists : function()
51522     {
51523         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
51524         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
51525         
51526         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
51527         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
51528         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
51529         
51530         this.white = [];
51531         this.black = [];
51532         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
51533             if (b.indexOf(tag) > -1) {
51534                 return;
51535             }
51536             this.white.push(tag);
51537             
51538         }, this);
51539         
51540         Roo.each(w, function(tag) {
51541             if (b.indexOf(tag) > -1) {
51542                 return;
51543             }
51544             if (this.white.indexOf(tag) > -1) {
51545                 return;
51546             }
51547             this.white.push(tag);
51548             
51549         }, this);
51550         
51551         
51552         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
51553             if (w.indexOf(tag) > -1) {
51554                 return;
51555             }
51556             this.black.push(tag);
51557             
51558         }, this);
51559         
51560         Roo.each(b, function(tag) {
51561             if (w.indexOf(tag) > -1) {
51562                 return;
51563             }
51564             if (this.black.indexOf(tag) > -1) {
51565                 return;
51566             }
51567             this.black.push(tag);
51568             
51569         }, this);
51570         
51571         
51572         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
51573         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
51574         
51575         this.cwhite = [];
51576         this.cblack = [];
51577         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
51578             if (b.indexOf(tag) > -1) {
51579                 return;
51580             }
51581             this.cwhite.push(tag);
51582             
51583         }, this);
51584         
51585         Roo.each(w, function(tag) {
51586             if (b.indexOf(tag) > -1) {
51587                 return;
51588             }
51589             if (this.cwhite.indexOf(tag) > -1) {
51590                 return;
51591             }
51592             this.cwhite.push(tag);
51593             
51594         }, this);
51595         
51596         
51597         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
51598             if (w.indexOf(tag) > -1) {
51599                 return;
51600             }
51601             this.cblack.push(tag);
51602             
51603         }, this);
51604         
51605         Roo.each(b, function(tag) {
51606             if (w.indexOf(tag) > -1) {
51607                 return;
51608             }
51609             if (this.cblack.indexOf(tag) > -1) {
51610                 return;
51611             }
51612             this.cblack.push(tag);
51613             
51614         }, this);
51615     },
51616     
51617     setStylesheets : function(stylesheets)
51618     {
51619         if(typeof(stylesheets) == 'string'){
51620             Roo.get(this.iframe.contentDocument.head).createChild({
51621                 tag : 'link',
51622                 rel : 'stylesheet',
51623                 type : 'text/css',
51624                 href : stylesheets
51625             });
51626             
51627             return;
51628         }
51629         var _this = this;
51630      
51631         Roo.each(stylesheets, function(s) {
51632             if(!s.length){
51633                 return;
51634             }
51635             
51636             Roo.get(_this.iframe.contentDocument.head).createChild({
51637                 tag : 'link',
51638                 rel : 'stylesheet',
51639                 type : 'text/css',
51640                 href : s
51641             });
51642         });
51643
51644         
51645     },
51646     
51647     
51648     updateLanguage : function()
51649     {
51650         if (!this.iframe || !this.iframe.contentDocument) {
51651             return;
51652         }
51653         Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
51654     },
51655     
51656     
51657     removeStylesheets : function()
51658     {
51659         var _this = this;
51660         
51661         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
51662             s.remove();
51663         });
51664     },
51665     
51666     setStyle : function(style)
51667     {
51668         Roo.get(this.iframe.contentDocument.head).createChild({
51669             tag : 'style',
51670             type : 'text/css',
51671             html : style
51672         });
51673
51674         return;
51675     }
51676     
51677     // hide stuff that is not compatible
51678     /**
51679      * @event blur
51680      * @hide
51681      */
51682     /**
51683      * @event change
51684      * @hide
51685      */
51686     /**
51687      * @event focus
51688      * @hide
51689      */
51690     /**
51691      * @event specialkey
51692      * @hide
51693      */
51694     /**
51695      * @cfg {String} fieldClass @hide
51696      */
51697     /**
51698      * @cfg {String} focusClass @hide
51699      */
51700     /**
51701      * @cfg {String} autoCreate @hide
51702      */
51703     /**
51704      * @cfg {String} inputType @hide
51705      */
51706     /**
51707      * @cfg {String} invalidClass @hide
51708      */
51709     /**
51710      * @cfg {String} invalidText @hide
51711      */
51712     /**
51713      * @cfg {String} msgFx @hide
51714      */
51715     /**
51716      * @cfg {String} validateOnBlur @hide
51717      */
51718 });
51719
51720 Roo.HtmlEditorCore.white = [
51721         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
51722         
51723        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
51724        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
51725        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
51726        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
51727        'TABLE',   'UL',         'XMP', 
51728        
51729        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
51730       'THEAD',   'TR', 
51731      
51732       'DIR', 'MENU', 'OL', 'UL', 'DL',
51733        
51734       'EMBED',  'OBJECT'
51735 ];
51736
51737
51738 Roo.HtmlEditorCore.black = [
51739     //    'embed',  'object', // enable - backend responsiblity to clean thiese
51740         'APPLET', // 
51741         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
51742         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
51743         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
51744         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
51745         //'FONT' // CLEAN LATER..
51746         'COLGROUP', 'COL'   // messy tables.
51747         
51748         
51749 ];
51750 Roo.HtmlEditorCore.clean = [ // ?? needed???
51751      'SCRIPT', 'STYLE', 'TITLE', 'XML'
51752 ];
51753 Roo.HtmlEditorCore.tag_remove = [
51754     'FONT', 'TBODY'  
51755 ];
51756 // attributes..
51757
51758 Roo.HtmlEditorCore.ablack = [
51759     'on'
51760 ];
51761     
51762 Roo.HtmlEditorCore.aclean = [ 
51763     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
51764 ];
51765
51766 // protocols..
51767 Roo.HtmlEditorCore.pwhite= [
51768         'http',  'https',  'mailto'
51769 ];
51770
51771 // white listed style attributes.
51772 Roo.HtmlEditorCore.cwhite= [
51773       //  'text-align', /// default is to allow most things..
51774       
51775          
51776 //        'font-size'//??
51777 ];
51778
51779 // black listed style attributes.
51780 Roo.HtmlEditorCore.cblack= [
51781       //  'font-size' -- this can be set by the project 
51782 ];
51783
51784
51785
51786
51787     //<script type="text/javascript">
51788
51789 /*
51790  * Ext JS Library 1.1.1
51791  * Copyright(c) 2006-2007, Ext JS, LLC.
51792  * Licence LGPL
51793  * 
51794  */
51795  
51796  
51797 Roo.form.HtmlEditor = function(config){
51798     
51799     
51800     
51801     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
51802     
51803     if (!this.toolbars) {
51804         this.toolbars = [];
51805     }
51806     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
51807     
51808     
51809 };
51810
51811 /**
51812  * @class Roo.form.HtmlEditor
51813  * @extends Roo.form.Field
51814  * Provides a lightweight HTML Editor component.
51815  *
51816  * This has been tested on Fireforx / Chrome.. IE may not be so great..
51817  * 
51818  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
51819  * supported by this editor.</b><br/><br/>
51820  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
51821  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
51822  */
51823 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
51824     /**
51825      * @cfg {Boolean} clearUp
51826      */
51827     clearUp : true,
51828       /**
51829      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
51830      */
51831     toolbars : false,
51832    
51833      /**
51834      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
51835      *                        Roo.resizable.
51836      */
51837     resizable : false,
51838      /**
51839      * @cfg {Number} height (in pixels)
51840      */   
51841     height: 300,
51842    /**
51843      * @cfg {Number} width (in pixels)
51844      */   
51845     width: 500,
51846     
51847     /**
51848      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea  rootURL + '/roojs1/css/undoreset.css',   .
51849      * 
51850      */
51851     stylesheets: false,
51852     
51853     
51854      /**
51855      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
51856      * 
51857      */
51858     cblack: false,
51859     /**
51860      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
51861      * 
51862      */
51863     cwhite: false,
51864     
51865      /**
51866      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
51867      * 
51868      */
51869     black: false,
51870     /**
51871      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
51872      * 
51873      */
51874     white: false,
51875     /**
51876      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
51877      */
51878     allowComments: false,
51879     /**
51880      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
51881      */
51882     enableBlocks : true,
51883     
51884     /**
51885      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
51886      *         if you are doing an email editor, this probably needs disabling, it's designed
51887      */
51888     autoClean: true,
51889     /**
51890      * @cfg {string} bodyCls default '' default classes to add to body of editable area - usually undoreset is a good start..
51891      */
51892     bodyCls : '',
51893     /**
51894      * @cfg {String} language default en - language of text (usefull for rtl languages)
51895      * 
51896      */
51897     language: 'en',
51898     
51899      
51900     // id of frame..
51901     frameId: false,
51902     
51903     // private properties
51904     validationEvent : false,
51905     deferHeight: true,
51906     initialized : false,
51907     activated : false,
51908     
51909     onFocus : Roo.emptyFn,
51910     iframePad:3,
51911     hideMode:'offsets',
51912     
51913     actionMode : 'container', // defaults to hiding it...
51914     
51915     defaultAutoCreate : { // modified by initCompnoent..
51916         tag: "textarea",
51917         style:"width:500px;height:300px;",
51918         autocomplete: "new-password"
51919     },
51920
51921     // private
51922     initComponent : function(){
51923         this.addEvents({
51924             /**
51925              * @event initialize
51926              * Fires when the editor is fully initialized (including the iframe)
51927              * @param {HtmlEditor} this
51928              */
51929             initialize: true,
51930             /**
51931              * @event activate
51932              * Fires when the editor is first receives the focus. Any insertion must wait
51933              * until after this event.
51934              * @param {HtmlEditor} this
51935              */
51936             activate: true,
51937              /**
51938              * @event beforesync
51939              * Fires before the textarea is updated with content from the editor iframe. Return false
51940              * to cancel the sync.
51941              * @param {HtmlEditor} this
51942              * @param {String} html
51943              */
51944             beforesync: true,
51945              /**
51946              * @event beforepush
51947              * Fires before the iframe editor is updated with content from the textarea. Return false
51948              * to cancel the push.
51949              * @param {HtmlEditor} this
51950              * @param {String} html
51951              */
51952             beforepush: true,
51953              /**
51954              * @event sync
51955              * Fires when the textarea is updated with content from the editor iframe.
51956              * @param {HtmlEditor} this
51957              * @param {String} html
51958              */
51959             sync: true,
51960              /**
51961              * @event push
51962              * Fires when the iframe editor is updated with content from the textarea.
51963              * @param {HtmlEditor} this
51964              * @param {String} html
51965              */
51966             push: true,
51967              /**
51968              * @event editmodechange
51969              * Fires when the editor switches edit modes
51970              * @param {HtmlEditor} this
51971              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
51972              */
51973             editmodechange: true,
51974             /**
51975              * @event editorevent
51976              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
51977              * @param {HtmlEditor} this
51978              */
51979             editorevent: true,
51980             /**
51981              * @event firstfocus
51982              * Fires when on first focus - needed by toolbars..
51983              * @param {HtmlEditor} this
51984              */
51985             firstfocus: true,
51986             /**
51987              * @event autosave
51988              * Auto save the htmlEditor value as a file into Events
51989              * @param {HtmlEditor} this
51990              */
51991             autosave: true,
51992             /**
51993              * @event savedpreview
51994              * preview the saved version of htmlEditor
51995              * @param {HtmlEditor} this
51996              */
51997             savedpreview: true,
51998             
51999             /**
52000             * @event stylesheetsclick
52001             * Fires when press the Sytlesheets button
52002             * @param {Roo.HtmlEditorCore} this
52003             */
52004             stylesheetsclick: true,
52005             /**
52006             * @event paste
52007             * Fires when press user pastes into the editor
52008             * @param {Roo.HtmlEditorCore} this
52009             */
52010             paste: true 
52011             
52012         });
52013         this.defaultAutoCreate =  {
52014             tag: "textarea",
52015             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
52016             autocomplete: "new-password"
52017         };
52018     },
52019
52020     /**
52021      * Protected method that will not generally be called directly. It
52022      * is called when the editor creates its toolbar. Override this method if you need to
52023      * add custom toolbar buttons.
52024      * @param {HtmlEditor} editor
52025      */
52026     createToolbar : function(editor){
52027         Roo.log("create toolbars");
52028         if (!editor.toolbars || !editor.toolbars.length) {
52029             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
52030         }
52031         
52032         for (var i =0 ; i < editor.toolbars.length;i++) {
52033             editor.toolbars[i] = Roo.factory(
52034                     typeof(editor.toolbars[i]) == 'string' ?
52035                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
52036                 Roo.form.HtmlEditor);
52037             editor.toolbars[i].init(editor);
52038         }
52039          
52040         
52041     },
52042     /**
52043      * get the Context selected node
52044      * @returns {DomElement|boolean} selected node if active or false if none
52045      * 
52046      */
52047     getSelectedNode : function()
52048     {
52049         if (this.toolbars.length < 2 || !this.toolbars[1].tb) {
52050             return false;
52051         }
52052         return this.toolbars[1].tb.selectedNode;
52053     
52054     },
52055     // private
52056     onRender : function(ct, position)
52057     {
52058         var _t = this;
52059         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
52060         
52061         this.wrap = this.el.wrap({
52062             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
52063         });
52064         
52065         this.editorcore.onRender(ct, position);
52066          
52067         if (this.resizable) {
52068             this.resizeEl = new Roo.Resizable(this.wrap, {
52069                 pinned : true,
52070                 wrap: true,
52071                 dynamic : true,
52072                 minHeight : this.height,
52073                 height: this.height,
52074                 handles : this.resizable,
52075                 width: this.width,
52076                 listeners : {
52077                     resize : function(r, w, h) {
52078                         _t.onResize(w,h); // -something
52079                     }
52080                 }
52081             });
52082             
52083         }
52084         this.createToolbar(this);
52085        
52086         
52087         if(!this.width){
52088             this.setSize(this.wrap.getSize());
52089         }
52090         if (this.resizeEl) {
52091             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
52092             // should trigger onReize..
52093         }
52094         
52095         this.keyNav = new Roo.KeyNav(this.el, {
52096             
52097             "tab" : function(e){
52098                 e.preventDefault();
52099                 
52100                 var value = this.getValue();
52101                 
52102                 var start = this.el.dom.selectionStart;
52103                 var end = this.el.dom.selectionEnd;
52104                 
52105                 if(!e.shiftKey){
52106                     
52107                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
52108                     this.el.dom.setSelectionRange(end + 1, end + 1);
52109                     return;
52110                 }
52111                 
52112                 var f = value.substring(0, start).split("\t");
52113                 
52114                 if(f.pop().length != 0){
52115                     return;
52116                 }
52117                 
52118                 this.setValue(f.join("\t") + value.substring(end));
52119                 this.el.dom.setSelectionRange(start - 1, start - 1);
52120                 
52121             },
52122             
52123             "home" : function(e){
52124                 e.preventDefault();
52125                 
52126                 var curr = this.el.dom.selectionStart;
52127                 var lines = this.getValue().split("\n");
52128                 
52129                 if(!lines.length){
52130                     return;
52131                 }
52132                 
52133                 if(e.ctrlKey){
52134                     this.el.dom.setSelectionRange(0, 0);
52135                     return;
52136                 }
52137                 
52138                 var pos = 0;
52139                 
52140                 for (var i = 0; i < lines.length;i++) {
52141                     pos += lines[i].length;
52142                     
52143                     if(i != 0){
52144                         pos += 1;
52145                     }
52146                     
52147                     if(pos < curr){
52148                         continue;
52149                     }
52150                     
52151                     pos -= lines[i].length;
52152                     
52153                     break;
52154                 }
52155                 
52156                 if(!e.shiftKey){
52157                     this.el.dom.setSelectionRange(pos, pos);
52158                     return;
52159                 }
52160                 
52161                 this.el.dom.selectionStart = pos;
52162                 this.el.dom.selectionEnd = curr;
52163             },
52164             
52165             "end" : function(e){
52166                 e.preventDefault();
52167                 
52168                 var curr = this.el.dom.selectionStart;
52169                 var lines = this.getValue().split("\n");
52170                 
52171                 if(!lines.length){
52172                     return;
52173                 }
52174                 
52175                 if(e.ctrlKey){
52176                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
52177                     return;
52178                 }
52179                 
52180                 var pos = 0;
52181                 
52182                 for (var i = 0; i < lines.length;i++) {
52183                     
52184                     pos += lines[i].length;
52185                     
52186                     if(i != 0){
52187                         pos += 1;
52188                     }
52189                     
52190                     if(pos < curr){
52191                         continue;
52192                     }
52193                     
52194                     break;
52195                 }
52196                 
52197                 if(!e.shiftKey){
52198                     this.el.dom.setSelectionRange(pos, pos);
52199                     return;
52200                 }
52201                 
52202                 this.el.dom.selectionStart = curr;
52203                 this.el.dom.selectionEnd = pos;
52204             },
52205
52206             scope : this,
52207
52208             doRelay : function(foo, bar, hname){
52209                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
52210             },
52211
52212             forceKeyDown: true
52213         });
52214         
52215 //        if(this.autosave && this.w){
52216 //            this.autoSaveFn = setInterval(this.autosave, 1000);
52217 //        }
52218     },
52219
52220     // private
52221     onResize : function(w, h)
52222     {
52223         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
52224         var ew = false;
52225         var eh = false;
52226         
52227         if(this.el ){
52228             if(typeof w == 'number'){
52229                 var aw = w - this.wrap.getFrameWidth('lr');
52230                 this.el.setWidth(this.adjustWidth('textarea', aw));
52231                 ew = aw;
52232             }
52233             if(typeof h == 'number'){
52234                 var tbh = 0;
52235                 for (var i =0; i < this.toolbars.length;i++) {
52236                     // fixme - ask toolbars for heights?
52237                     tbh += this.toolbars[i].tb.el.getHeight();
52238                     if (this.toolbars[i].footer) {
52239                         tbh += this.toolbars[i].footer.el.getHeight();
52240                     }
52241                 }
52242                 
52243                 
52244                 
52245                 
52246                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
52247                 ah -= 5; // knock a few pixes off for look..
52248 //                Roo.log(ah);
52249                 this.el.setHeight(this.adjustWidth('textarea', ah));
52250                 var eh = ah;
52251             }
52252         }
52253         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
52254         this.editorcore.onResize(ew,eh);
52255         
52256     },
52257
52258     /**
52259      * Toggles the editor between standard and source edit mode.
52260      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
52261      */
52262     toggleSourceEdit : function(sourceEditMode)
52263     {
52264         this.editorcore.toggleSourceEdit(sourceEditMode);
52265         
52266         if(this.editorcore.sourceEditMode){
52267             Roo.log('editor - showing textarea');
52268             
52269 //            Roo.log('in');
52270 //            Roo.log(this.syncValue());
52271             this.editorcore.syncValue();
52272             this.el.removeClass('x-hidden');
52273             this.el.dom.removeAttribute('tabIndex');
52274             this.el.focus();
52275             this.el.dom.scrollTop = 0;
52276             
52277             
52278             for (var i = 0; i < this.toolbars.length; i++) {
52279                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
52280                     this.toolbars[i].tb.hide();
52281                     this.toolbars[i].footer.hide();
52282                 }
52283             }
52284             
52285         }else{
52286             Roo.log('editor - hiding textarea');
52287 //            Roo.log('out')
52288 //            Roo.log(this.pushValue()); 
52289             this.editorcore.pushValue();
52290             
52291             this.el.addClass('x-hidden');
52292             this.el.dom.setAttribute('tabIndex', -1);
52293             
52294             for (var i = 0; i < this.toolbars.length; i++) {
52295                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
52296                     this.toolbars[i].tb.show();
52297                     this.toolbars[i].footer.show();
52298                 }
52299             }
52300             
52301             //this.deferFocus();
52302         }
52303         
52304         this.setSize(this.wrap.getSize());
52305         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
52306         
52307         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
52308     },
52309  
52310     // private (for BoxComponent)
52311     adjustSize : Roo.BoxComponent.prototype.adjustSize,
52312
52313     // private (for BoxComponent)
52314     getResizeEl : function(){
52315         return this.wrap;
52316     },
52317
52318     // private (for BoxComponent)
52319     getPositionEl : function(){
52320         return this.wrap;
52321     },
52322
52323     // private
52324     initEvents : function(){
52325         this.originalValue = this.getValue();
52326     },
52327
52328     /**
52329      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
52330      * @method
52331      */
52332     markInvalid : Roo.emptyFn,
52333     /**
52334      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
52335      * @method
52336      */
52337     clearInvalid : Roo.emptyFn,
52338
52339     setValue : function(v){
52340         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
52341         this.editorcore.pushValue();
52342     },
52343
52344     /**
52345      * update the language in the body - really done by core
52346      * @param {String} language - eg. en / ar / zh-CN etc..
52347      */
52348     updateLanguage : function(lang)
52349     {
52350         this.language = lang;
52351         this.editorcore.language = lang;
52352         this.editorcore.updateLanguage();
52353      
52354     },
52355     // private
52356     deferFocus : function(){
52357         this.focus.defer(10, this);
52358     },
52359
52360     // doc'ed in Field
52361     focus : function(){
52362         this.editorcore.focus();
52363         
52364     },
52365       
52366
52367     // private
52368     onDestroy : function(){
52369         
52370         
52371         
52372         if(this.rendered){
52373             
52374             for (var i =0; i < this.toolbars.length;i++) {
52375                 // fixme - ask toolbars for heights?
52376                 this.toolbars[i].onDestroy();
52377             }
52378             
52379             this.wrap.dom.innerHTML = '';
52380             this.wrap.remove();
52381         }
52382     },
52383
52384     // private
52385     onFirstFocus : function(){
52386         //Roo.log("onFirstFocus");
52387         this.editorcore.onFirstFocus();
52388          for (var i =0; i < this.toolbars.length;i++) {
52389             this.toolbars[i].onFirstFocus();
52390         }
52391         
52392     },
52393     
52394     // private
52395     syncValue : function()
52396     {
52397         this.editorcore.syncValue();
52398     },
52399     
52400     pushValue : function()
52401     {
52402         this.editorcore.pushValue();
52403     },
52404     
52405     setStylesheets : function(stylesheets)
52406     {
52407         this.editorcore.setStylesheets(stylesheets);
52408     },
52409     
52410     removeStylesheets : function()
52411     {
52412         this.editorcore.removeStylesheets();
52413     }
52414      
52415     
52416     // hide stuff that is not compatible
52417     /**
52418      * @event blur
52419      * @hide
52420      */
52421     /**
52422      * @event change
52423      * @hide
52424      */
52425     /**
52426      * @event focus
52427      * @hide
52428      */
52429     /**
52430      * @event specialkey
52431      * @hide
52432      */
52433     /**
52434      * @cfg {String} fieldClass @hide
52435      */
52436     /**
52437      * @cfg {String} focusClass @hide
52438      */
52439     /**
52440      * @cfg {String} autoCreate @hide
52441      */
52442     /**
52443      * @cfg {String} inputType @hide
52444      */
52445     /**
52446      * @cfg {String} invalidClass @hide
52447      */
52448     /**
52449      * @cfg {String} invalidText @hide
52450      */
52451     /**
52452      * @cfg {String} msgFx @hide
52453      */
52454     /**
52455      * @cfg {String} validateOnBlur @hide
52456      */
52457 });
52458  
52459     /*
52460  * Based on
52461  * Ext JS Library 1.1.1
52462  * Copyright(c) 2006-2007, Ext JS, LLC.
52463  *  
52464  
52465  */
52466
52467 /**
52468  * @class Roo.form.HtmlEditor.ToolbarStandard
52469  * Basic Toolbar
52470
52471  * Usage:
52472  *
52473  new Roo.form.HtmlEditor({
52474     ....
52475     toolbars : [
52476         new Roo.form.HtmlEditorToolbar1({
52477             disable : { fonts: 1 , format: 1, ..., ... , ...],
52478             btns : [ .... ]
52479         })
52480     }
52481      
52482  * 
52483  * @cfg {Object} disable List of elements to disable..
52484  * @cfg {Roo.Toolbar.Item|Roo.Toolbar.Button|Roo.Toolbar.SplitButton|Roo.form.Field} btns[] List of additional buttons.
52485  * 
52486  * 
52487  * NEEDS Extra CSS? 
52488  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
52489  */
52490  
52491 Roo.form.HtmlEditor.ToolbarStandard = function(config)
52492 {
52493     
52494     Roo.apply(this, config);
52495     
52496     // default disabled, based on 'good practice'..
52497     this.disable = this.disable || {};
52498     Roo.applyIf(this.disable, {
52499         fontSize : true,
52500         colors : true,
52501         specialElements : true
52502     });
52503     
52504     
52505     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
52506     // dont call parent... till later.
52507 }
52508
52509 Roo.form.HtmlEditor.ToolbarStandard.prototype = {
52510     
52511     tb: false,
52512     
52513     rendered: false,
52514     
52515     editor : false,
52516     editorcore : false,
52517     /**
52518      * @cfg {Object} disable  List of toolbar elements to disable
52519          
52520      */
52521     disable : false,
52522     
52523     
52524      /**
52525      * @cfg {String} createLinkText The default text for the create link prompt
52526      */
52527     createLinkText : 'Please enter the URL for the link:',
52528     /**
52529      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
52530      */
52531     defaultLinkValue : 'http:/'+'/',
52532    
52533     
52534       /**
52535      * @cfg {Array} fontFamilies An array of available font families
52536      */
52537     fontFamilies : [
52538         'Arial',
52539         'Courier New',
52540         'Tahoma',
52541         'Times New Roman',
52542         'Verdana'
52543     ],
52544     
52545     specialChars : [
52546            "&#169;",
52547           "&#174;",     
52548           "&#8482;",    
52549           "&#163;" ,    
52550          // "&#8212;",    
52551           "&#8230;",    
52552           "&#247;" ,    
52553         //  "&#225;" ,     ?? a acute?
52554            "&#8364;"    , //Euro
52555        //   "&#8220;"    ,
52556         //  "&#8221;"    ,
52557         //  "&#8226;"    ,
52558           "&#176;"  //   , // degrees
52559
52560          // "&#233;"     , // e ecute
52561          // "&#250;"     , // u ecute?
52562     ],
52563     
52564     specialElements : [
52565         {
52566             text: "Insert Table",
52567             xtype: 'MenuItem',
52568             xns : Roo.Menu,
52569             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
52570                 
52571         },
52572         {    
52573             text: "Insert Image",
52574             xtype: 'MenuItem',
52575             xns : Roo.Menu,
52576             ihtml : '<img src="about:blank"/>'
52577             
52578         }
52579         
52580          
52581     ],
52582     
52583     
52584     inputElements : [ 
52585             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
52586             "input:submit", "input:button", "select", "textarea", "label" ],
52587     formats : [
52588         ["p"] ,  
52589         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
52590         ["pre"],[ "code"], 
52591         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
52592         ['div'],['span'],
52593         ['sup'],['sub']
52594     ],
52595     
52596     cleanStyles : [
52597         "font-size"
52598     ],
52599      /**
52600      * @cfg {String} defaultFont default font to use.
52601      */
52602     defaultFont: 'tahoma',
52603    
52604     fontSelect : false,
52605     
52606     
52607     formatCombo : false,
52608     
52609     init : function(editor)
52610     {
52611         this.editor = editor;
52612         this.editorcore = editor.editorcore ? editor.editorcore : editor;
52613         var editorcore = this.editorcore;
52614         
52615         var _t = this;
52616         
52617         var fid = editorcore.frameId;
52618         var etb = this;
52619         function btn(id, toggle, handler){
52620             var xid = fid + '-'+ id ;
52621             return {
52622                 id : xid,
52623                 cmd : id,
52624                 cls : 'x-btn-icon x-edit-'+id,
52625                 enableToggle:toggle !== false,
52626                 scope: _t, // was editor...
52627                 handler:handler||_t.relayBtnCmd,
52628                 clickEvent:'mousedown',
52629                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
52630                 tabIndex:-1
52631             };
52632         }
52633         
52634         
52635         
52636         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
52637         this.tb = tb;
52638          // stop form submits
52639         tb.el.on('click', function(e){
52640             e.preventDefault(); // what does this do?
52641         });
52642
52643         if(!this.disable.font) { // && !Roo.isSafari){
52644             /* why no safari for fonts 
52645             editor.fontSelect = tb.el.createChild({
52646                 tag:'select',
52647                 tabIndex: -1,
52648                 cls:'x-font-select',
52649                 html: this.createFontOptions()
52650             });
52651             
52652             editor.fontSelect.on('change', function(){
52653                 var font = editor.fontSelect.dom.value;
52654                 editor.relayCmd('fontname', font);
52655                 editor.deferFocus();
52656             }, editor);
52657             
52658             tb.add(
52659                 editor.fontSelect.dom,
52660                 '-'
52661             );
52662             */
52663             
52664         };
52665         if(!this.disable.formats){
52666             this.formatCombo = new Roo.form.ComboBox({
52667                 store: new Roo.data.SimpleStore({
52668                     id : 'tag',
52669                     fields: ['tag'],
52670                     data : this.formats // from states.js
52671                 }),
52672                 blockFocus : true,
52673                 name : '',
52674                 //autoCreate : {tag: "div",  size: "20"},
52675                 displayField:'tag',
52676                 typeAhead: false,
52677                 mode: 'local',
52678                 editable : false,
52679                 triggerAction: 'all',
52680                 emptyText:'Add tag',
52681                 selectOnFocus:true,
52682                 width:135,
52683                 listeners : {
52684                     'select': function(c, r, i) {
52685                         editorcore.insertTag(r.get('tag'));
52686                         editor.focus();
52687                     }
52688                 }
52689
52690             });
52691             tb.addField(this.formatCombo);
52692             
52693         }
52694         
52695         if(!this.disable.format){
52696             tb.add(
52697                 btn('bold'),
52698                 btn('italic'),
52699                 btn('underline'),
52700                 btn('strikethrough')
52701             );
52702         };
52703         if(!this.disable.fontSize){
52704             tb.add(
52705                 '-',
52706                 
52707                 
52708                 btn('increasefontsize', false, editorcore.adjustFont),
52709                 btn('decreasefontsize', false, editorcore.adjustFont)
52710             );
52711         };
52712         
52713         
52714         if(!this.disable.colors){
52715             tb.add(
52716                 '-', {
52717                     id:editorcore.frameId +'-forecolor',
52718                     cls:'x-btn-icon x-edit-forecolor',
52719                     clickEvent:'mousedown',
52720                     tooltip: this.buttonTips['forecolor'] || undefined,
52721                     tabIndex:-1,
52722                     menu : new Roo.menu.ColorMenu({
52723                         allowReselect: true,
52724                         focus: Roo.emptyFn,
52725                         value:'000000',
52726                         plain:true,
52727                         selectHandler: function(cp, color){
52728                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
52729                             editor.deferFocus();
52730                         },
52731                         scope: editorcore,
52732                         clickEvent:'mousedown'
52733                     })
52734                 }, {
52735                     id:editorcore.frameId +'backcolor',
52736                     cls:'x-btn-icon x-edit-backcolor',
52737                     clickEvent:'mousedown',
52738                     tooltip: this.buttonTips['backcolor'] || undefined,
52739                     tabIndex:-1,
52740                     menu : new Roo.menu.ColorMenu({
52741                         focus: Roo.emptyFn,
52742                         value:'FFFFFF',
52743                         plain:true,
52744                         allowReselect: true,
52745                         selectHandler: function(cp, color){
52746                             if(Roo.isGecko){
52747                                 editorcore.execCmd('useCSS', false);
52748                                 editorcore.execCmd('hilitecolor', color);
52749                                 editorcore.execCmd('useCSS', true);
52750                                 editor.deferFocus();
52751                             }else{
52752                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
52753                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
52754                                 editor.deferFocus();
52755                             }
52756                         },
52757                         scope:editorcore,
52758                         clickEvent:'mousedown'
52759                     })
52760                 }
52761             );
52762         };
52763         // now add all the items...
52764         
52765
52766         if(!this.disable.alignments){
52767             tb.add(
52768                 '-',
52769                 btn('justifyleft'),
52770                 btn('justifycenter'),
52771                 btn('justifyright')
52772             );
52773         };
52774
52775         //if(!Roo.isSafari){
52776             if(!this.disable.links){
52777                 tb.add(
52778                     '-',
52779                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
52780                 );
52781             };
52782
52783             if(!this.disable.lists){
52784                 tb.add(
52785                     '-',
52786                     btn('insertorderedlist'),
52787                     btn('insertunorderedlist')
52788                 );
52789             }
52790             if(!this.disable.sourceEdit){
52791                 tb.add(
52792                     '-',
52793                     btn('sourceedit', true, function(btn){
52794                         this.toggleSourceEdit(btn.pressed);
52795                     })
52796                 );
52797             }
52798         //}
52799         
52800         var smenu = { };
52801         // special menu.. - needs to be tidied up..
52802         if (!this.disable.special) {
52803             smenu = {
52804                 text: "&#169;",
52805                 cls: 'x-edit-none',
52806                 
52807                 menu : {
52808                     items : []
52809                 }
52810             };
52811             for (var i =0; i < this.specialChars.length; i++) {
52812                 smenu.menu.items.push({
52813                     
52814                     html: this.specialChars[i],
52815                     handler: function(a,b) {
52816                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
52817                         //editor.insertAtCursor(a.html);
52818                         
52819                     },
52820                     tabIndex:-1
52821                 });
52822             }
52823             
52824             
52825             tb.add(smenu);
52826             
52827             
52828         }
52829         
52830         var cmenu = { };
52831         if (!this.disable.cleanStyles) {
52832             cmenu = {
52833                 cls: 'x-btn-icon x-btn-clear',
52834                 
52835                 menu : {
52836                     items : []
52837                 }
52838             };
52839             for (var i =0; i < this.cleanStyles.length; i++) {
52840                 cmenu.menu.items.push({
52841                     actiontype : this.cleanStyles[i],
52842                     html: 'Remove ' + this.cleanStyles[i],
52843                     handler: function(a,b) {
52844 //                        Roo.log(a);
52845 //                        Roo.log(b);
52846                         var c = Roo.get(editorcore.doc.body);
52847                         c.select('[style]').each(function(s) {
52848                             s.dom.style.removeProperty(a.actiontype);
52849                         });
52850                         editorcore.syncValue();
52851                     },
52852                     tabIndex:-1
52853                 });
52854             }
52855             cmenu.menu.items.push({
52856                 actiontype : 'tablewidths',
52857                 html: 'Remove Table Widths',
52858                 handler: function(a,b) {
52859                     editorcore.cleanTableWidths();
52860                     editorcore.syncValue();
52861                 },
52862                 tabIndex:-1
52863             });
52864             cmenu.menu.items.push({
52865                 actiontype : 'word',
52866                 html: 'Remove MS Word Formating',
52867                 handler: function(a,b) {
52868                     editorcore.cleanWord();
52869                     editorcore.syncValue();
52870                 },
52871                 tabIndex:-1
52872             });
52873             
52874             cmenu.menu.items.push({
52875                 actiontype : 'all',
52876                 html: 'Remove All Styles',
52877                 handler: function(a,b) {
52878                     
52879                     var c = Roo.get(editorcore.doc.body);
52880                     c.select('[style]').each(function(s) {
52881                         s.dom.removeAttribute('style');
52882                     });
52883                     editorcore.syncValue();
52884                 },
52885                 tabIndex:-1
52886             });
52887             
52888             cmenu.menu.items.push({
52889                 actiontype : 'all',
52890                 html: 'Remove All CSS Classes',
52891                 handler: function(a,b) {
52892                     
52893                     var c = Roo.get(editorcore.doc.body);
52894                     c.select('[class]').each(function(s) {
52895                         s.dom.removeAttribute('class');
52896                     });
52897                     editorcore.cleanWord();
52898                     editorcore.syncValue();
52899                 },
52900                 tabIndex:-1
52901             });
52902             
52903              cmenu.menu.items.push({
52904                 actiontype : 'tidy',
52905                 html: 'Tidy HTML Source',
52906                 handler: function(a,b) {
52907                     new Roo.htmleditor.Tidy(editorcore.doc.body);
52908                     editorcore.syncValue();
52909                 },
52910                 tabIndex:-1
52911             });
52912             
52913             
52914             tb.add(cmenu);
52915         }
52916          
52917         if (!this.disable.specialElements) {
52918             var semenu = {
52919                 text: "Other;",
52920                 cls: 'x-edit-none',
52921                 menu : {
52922                     items : []
52923                 }
52924             };
52925             for (var i =0; i < this.specialElements.length; i++) {
52926                 semenu.menu.items.push(
52927                     Roo.apply({ 
52928                         handler: function(a,b) {
52929                             editor.insertAtCursor(this.ihtml);
52930                         }
52931                     }, this.specialElements[i])
52932                 );
52933                     
52934             }
52935             
52936             tb.add(semenu);
52937             
52938             
52939         }
52940          
52941         
52942         if (this.btns) {
52943             for(var i =0; i< this.btns.length;i++) {
52944                 var b = Roo.factory(this.btns[i],this.btns[i].xns || Roo.form);
52945                 b.cls =  'x-edit-none';
52946                 
52947                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
52948                     b.cls += ' x-init-enable';
52949                 }
52950                 
52951                 b.scope = editorcore;
52952                 tb.add(b);
52953             }
52954         
52955         }
52956         
52957         
52958         
52959         // disable everything...
52960         
52961         this.tb.items.each(function(item){
52962             
52963            if(
52964                 item.id != editorcore.frameId+ '-sourceedit' && 
52965                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
52966             ){
52967                 
52968                 item.disable();
52969             }
52970         });
52971         this.rendered = true;
52972         
52973         // the all the btns;
52974         editor.on('editorevent', this.updateToolbar, this);
52975         // other toolbars need to implement this..
52976         //editor.on('editmodechange', this.updateToolbar, this);
52977     },
52978     
52979     
52980     relayBtnCmd : function(btn) {
52981         this.editorcore.relayCmd(btn.cmd);
52982     },
52983     // private used internally
52984     createLink : function(){
52985         //Roo.log("create link?");
52986         var ec = this.editorcore;
52987         var ar = ec.getAllAncestors();
52988         var n = false;
52989         for(var i = 0;i< ar.length;i++) {
52990             if (ar[i] && ar[i].nodeName == 'A') {
52991                 n = ar[i];
52992                 break;
52993             }
52994         }
52995         
52996         (function() {
52997             
52998             Roo.MessageBox.show({
52999                 title : "Add / Edit Link URL",
53000                 msg : "Enter the url for the link",
53001                 buttons: Roo.MessageBox.OKCANCEL,
53002                 fn: function(btn, url){
53003                     if (btn != 'ok') {
53004                         return;
53005                     }
53006                     if(url && url != 'http:/'+'/'){
53007                         if (n) {
53008                             n.setAttribute('href', url);
53009                         } else {
53010                             ec.relayCmd('createlink', url);
53011                         }
53012                     }
53013                 },
53014                 minWidth:250,
53015                 prompt:true,
53016                 //multiline: multiline,
53017                 modal : true,
53018                 value :  n  ? n.getAttribute('href') : '' 
53019             });
53020             
53021              
53022         }).defer(100, this); // we have to defer this , otherwise the mouse click gives focus to the main window.
53023         
53024     },
53025
53026     
53027     /**
53028      * Protected method that will not generally be called directly. It triggers
53029      * a toolbar update by reading the markup state of the current selection in the editor.
53030      */
53031     updateToolbar: function(){
53032
53033         if(!this.editorcore.activated){
53034             this.editor.onFirstFocus();
53035             return;
53036         }
53037
53038         var btns = this.tb.items.map, 
53039             doc = this.editorcore.doc,
53040             frameId = this.editorcore.frameId;
53041
53042         if(!this.disable.font && !Roo.isSafari){
53043             /*
53044             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
53045             if(name != this.fontSelect.dom.value){
53046                 this.fontSelect.dom.value = name;
53047             }
53048             */
53049         }
53050         if(!this.disable.format){
53051             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
53052             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
53053             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
53054             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
53055         }
53056         if(!this.disable.alignments){
53057             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
53058             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
53059             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
53060         }
53061         if(!Roo.isSafari && !this.disable.lists){
53062             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
53063             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
53064         }
53065         
53066         var ans = this.editorcore.getAllAncestors();
53067         if (this.formatCombo) {
53068             
53069             
53070             var store = this.formatCombo.store;
53071             this.formatCombo.setValue("");
53072             for (var i =0; i < ans.length;i++) {
53073                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
53074                     // select it..
53075                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
53076                     break;
53077                 }
53078             }
53079         }
53080         
53081         
53082         
53083         // hides menus... - so this cant be on a menu...
53084         Roo.menu.MenuMgr.hideAll();
53085
53086         //this.editorsyncValue();
53087     },
53088    
53089     
53090     createFontOptions : function(){
53091         var buf = [], fs = this.fontFamilies, ff, lc;
53092         
53093         
53094         
53095         for(var i = 0, len = fs.length; i< len; i++){
53096             ff = fs[i];
53097             lc = ff.toLowerCase();
53098             buf.push(
53099                 '<option value="',lc,'" style="font-family:',ff,';"',
53100                     (this.defaultFont == lc ? ' selected="true">' : '>'),
53101                     ff,
53102                 '</option>'
53103             );
53104         }
53105         return buf.join('');
53106     },
53107     
53108     toggleSourceEdit : function(sourceEditMode){
53109         
53110         Roo.log("toolbar toogle");
53111         if(sourceEditMode === undefined){
53112             sourceEditMode = !this.sourceEditMode;
53113         }
53114         this.sourceEditMode = sourceEditMode === true;
53115         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
53116         // just toggle the button?
53117         if(btn.pressed !== this.sourceEditMode){
53118             btn.toggle(this.sourceEditMode);
53119             return;
53120         }
53121         
53122         if(sourceEditMode){
53123             Roo.log("disabling buttons");
53124             this.tb.items.each(function(item){
53125                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
53126                     item.disable();
53127                 }
53128             });
53129           
53130         }else{
53131             Roo.log("enabling buttons");
53132             if(this.editorcore.initialized){
53133                 this.tb.items.each(function(item){
53134                     item.enable();
53135                 });
53136                 // initialize 'blocks'
53137                 Roo.each(Roo.get(this.editorcore.doc.body).query('*[data-block]'), function(e) {
53138                     Roo.htmleditor.Block.factory(e).updateElement(e);
53139                 },this);
53140             
53141             }
53142             
53143         }
53144         Roo.log("calling toggole on editor");
53145         // tell the editor that it's been pressed..
53146         this.editor.toggleSourceEdit(sourceEditMode);
53147        
53148     },
53149      /**
53150      * Object collection of toolbar tooltips for the buttons in the editor. The key
53151      * is the command id associated with that button and the value is a valid QuickTips object.
53152      * For example:
53153 <pre><code>
53154 {
53155     bold : {
53156         title: 'Bold (Ctrl+B)',
53157         text: 'Make the selected text bold.',
53158         cls: 'x-html-editor-tip'
53159     },
53160     italic : {
53161         title: 'Italic (Ctrl+I)',
53162         text: 'Make the selected text italic.',
53163         cls: 'x-html-editor-tip'
53164     },
53165     ...
53166 </code></pre>
53167     * @type Object
53168      */
53169     buttonTips : {
53170         bold : {
53171             title: 'Bold (Ctrl+B)',
53172             text: 'Make the selected text bold.',
53173             cls: 'x-html-editor-tip'
53174         },
53175         italic : {
53176             title: 'Italic (Ctrl+I)',
53177             text: 'Make the selected text italic.',
53178             cls: 'x-html-editor-tip'
53179         },
53180         underline : {
53181             title: 'Underline (Ctrl+U)',
53182             text: 'Underline the selected text.',
53183             cls: 'x-html-editor-tip'
53184         },
53185         strikethrough : {
53186             title: 'Strikethrough',
53187             text: 'Strikethrough the selected text.',
53188             cls: 'x-html-editor-tip'
53189         },
53190         increasefontsize : {
53191             title: 'Grow Text',
53192             text: 'Increase the font size.',
53193             cls: 'x-html-editor-tip'
53194         },
53195         decreasefontsize : {
53196             title: 'Shrink Text',
53197             text: 'Decrease the font size.',
53198             cls: 'x-html-editor-tip'
53199         },
53200         backcolor : {
53201             title: 'Text Highlight Color',
53202             text: 'Change the background color of the selected text.',
53203             cls: 'x-html-editor-tip'
53204         },
53205         forecolor : {
53206             title: 'Font Color',
53207             text: 'Change the color of the selected text.',
53208             cls: 'x-html-editor-tip'
53209         },
53210         justifyleft : {
53211             title: 'Align Text Left',
53212             text: 'Align text to the left.',
53213             cls: 'x-html-editor-tip'
53214         },
53215         justifycenter : {
53216             title: 'Center Text',
53217             text: 'Center text in the editor.',
53218             cls: 'x-html-editor-tip'
53219         },
53220         justifyright : {
53221             title: 'Align Text Right',
53222             text: 'Align text to the right.',
53223             cls: 'x-html-editor-tip'
53224         },
53225         insertunorderedlist : {
53226             title: 'Bullet List',
53227             text: 'Start a bulleted list.',
53228             cls: 'x-html-editor-tip'
53229         },
53230         insertorderedlist : {
53231             title: 'Numbered List',
53232             text: 'Start a numbered list.',
53233             cls: 'x-html-editor-tip'
53234         },
53235         createlink : {
53236             title: 'Hyperlink',
53237             text: 'Make the selected text a hyperlink.',
53238             cls: 'x-html-editor-tip'
53239         },
53240         sourceedit : {
53241             title: 'Source Edit',
53242             text: 'Switch to source editing mode.',
53243             cls: 'x-html-editor-tip'
53244         }
53245     },
53246     // private
53247     onDestroy : function(){
53248         if(this.rendered){
53249             
53250             this.tb.items.each(function(item){
53251                 if(item.menu){
53252                     item.menu.removeAll();
53253                     if(item.menu.el){
53254                         item.menu.el.destroy();
53255                     }
53256                 }
53257                 item.destroy();
53258             });
53259              
53260         }
53261     },
53262     onFirstFocus: function() {
53263         this.tb.items.each(function(item){
53264            item.enable();
53265         });
53266     }
53267 };
53268
53269
53270
53271
53272 // <script type="text/javascript">
53273 /*
53274  * Based on
53275  * Ext JS Library 1.1.1
53276  * Copyright(c) 2006-2007, Ext JS, LLC.
53277  *  
53278  
53279  */
53280
53281  
53282 /**
53283  * @class Roo.form.HtmlEditor.ToolbarContext
53284  * Context Toolbar
53285  * 
53286  * Usage:
53287  *
53288  new Roo.form.HtmlEditor({
53289     ....
53290     toolbars : [
53291         { xtype: 'ToolbarStandard', styles : {} }
53292         { xtype: 'ToolbarContext', disable : {} }
53293     ]
53294 })
53295
53296      
53297  * 
53298  * @config : {Object} disable List of elements to disable.. (not done yet.)
53299  * @config : {Object} styles  Map of styles available.
53300  * 
53301  */
53302
53303 Roo.form.HtmlEditor.ToolbarContext = function(config)
53304 {
53305     
53306     Roo.apply(this, config);
53307     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
53308     // dont call parent... till later.
53309     this.styles = this.styles || {};
53310 }
53311
53312  
53313
53314 Roo.form.HtmlEditor.ToolbarContext.types = {
53315     'IMG' : [
53316         {
53317             name : 'width',
53318             title: "Width",
53319             width: 40
53320         },
53321         {
53322             name : 'height',
53323             title: "Height",
53324             width: 40
53325         },
53326         {
53327             name : 'align',
53328             title: "Align",
53329             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
53330             width : 80
53331             
53332         },
53333         {
53334             name : 'border',
53335             title: "Border",
53336             width: 40
53337         },
53338         {
53339             name : 'alt',
53340             title: "Alt",
53341             width: 120
53342         },
53343         {
53344             name : 'src',
53345             title: "Src",
53346             width: 220
53347         }
53348         
53349     ],
53350     
53351     'FIGURE' : [
53352         {
53353             name : 'align',
53354             title: "Align",
53355             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
53356             width : 80  
53357         }
53358     ],
53359     'A' : [
53360         {
53361             name : 'name',
53362             title: "Name",
53363             width: 50
53364         },
53365         {
53366             name : 'target',
53367             title: "Target",
53368             width: 120
53369         },
53370         {
53371             name : 'href',
53372             title: "Href",
53373             width: 220
53374         } // border?
53375         
53376     ],
53377     
53378     'INPUT' : [
53379         {
53380             name : 'name',
53381             title: "name",
53382             width: 120
53383         },
53384         {
53385             name : 'value',
53386             title: "Value",
53387             width: 120
53388         },
53389         {
53390             name : 'width',
53391             title: "Width",
53392             width: 40
53393         }
53394     ],
53395     'LABEL' : [
53396          {
53397             name : 'for',
53398             title: "For",
53399             width: 120
53400         }
53401     ],
53402     'TEXTAREA' : [
53403         {
53404             name : 'name',
53405             title: "name",
53406             width: 120
53407         },
53408         {
53409             name : 'rows',
53410             title: "Rows",
53411             width: 20
53412         },
53413         {
53414             name : 'cols',
53415             title: "Cols",
53416             width: 20
53417         }
53418     ],
53419     'SELECT' : [
53420         {
53421             name : 'name',
53422             title: "name",
53423             width: 120
53424         },
53425         {
53426             name : 'selectoptions',
53427             title: "Options",
53428             width: 200
53429         }
53430     ],
53431     
53432     // should we really allow this??
53433     // should this just be 
53434     'BODY' : [
53435         
53436         {
53437             name : 'title',
53438             title: "Title",
53439             width: 200,
53440             disabled : true
53441         }
53442     ],
53443  
53444     '*' : [
53445         // empty.
53446     ]
53447
53448 };
53449
53450 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
53451 Roo.form.HtmlEditor.ToolbarContext.stores = false;
53452
53453 Roo.form.HtmlEditor.ToolbarContext.options = {
53454         'font-family'  : [ 
53455                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
53456                 [ 'Courier New', 'Courier New'],
53457                 [ 'Tahoma', 'Tahoma'],
53458                 [ 'Times New Roman,serif', 'Times'],
53459                 [ 'Verdana','Verdana' ]
53460         ]
53461 };
53462
53463 // fixme - these need to be configurable..
53464  
53465
53466 //Roo.form.HtmlEditor.ToolbarContext.types
53467
53468
53469 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
53470     
53471     tb: false,
53472     
53473     rendered: false,
53474     
53475     editor : false,
53476     editorcore : false,
53477     /**
53478      * @cfg {Object} disable  List of toolbar elements to disable
53479          
53480      */
53481     disable : false,
53482     /**
53483      * @cfg {Object} styles List of styles 
53484      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
53485      *
53486      * These must be defined in the page, so they get rendered correctly..
53487      * .headline { }
53488      * TD.underline { }
53489      * 
53490      */
53491     styles : false,
53492     
53493     options: false,
53494     
53495     toolbars : false,
53496     
53497     init : function(editor)
53498     {
53499         this.editor = editor;
53500         this.editorcore = editor.editorcore ? editor.editorcore : editor;
53501         var editorcore = this.editorcore;
53502         
53503         var fid = editorcore.frameId;
53504         var etb = this;
53505         function btn(id, toggle, handler){
53506             var xid = fid + '-'+ id ;
53507             return {
53508                 id : xid,
53509                 cmd : id,
53510                 cls : 'x-btn-icon x-edit-'+id,
53511                 enableToggle:toggle !== false,
53512                 scope: editorcore, // was editor...
53513                 handler:handler||editorcore.relayBtnCmd,
53514                 clickEvent:'mousedown',
53515                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
53516                 tabIndex:-1
53517             };
53518         }
53519         // create a new element.
53520         var wdiv = editor.wrap.createChild({
53521                 tag: 'div'
53522             }, editor.wrap.dom.firstChild.nextSibling, true);
53523         
53524         // can we do this more than once??
53525         
53526          // stop form submits
53527       
53528  
53529         // disable everything...
53530         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
53531         this.toolbars = {};
53532         // block toolbars are built in updateToolbar when needed.
53533         for (var i in  ty) {
53534             
53535             this.toolbars[i] = this.buildToolbar(ty[i],i);
53536         }
53537         this.tb = this.toolbars.BODY;
53538         this.tb.el.show();
53539         this.buildFooter();
53540         this.footer.show();
53541         editor.on('hide', function( ) { this.footer.hide() }, this);
53542         editor.on('show', function( ) { this.footer.show() }, this);
53543         
53544          
53545         this.rendered = true;
53546         
53547         // the all the btns;
53548         editor.on('editorevent', this.updateToolbar, this);
53549         // other toolbars need to implement this..
53550         //editor.on('editmodechange', this.updateToolbar, this);
53551     },
53552     
53553     
53554     
53555     /**
53556      * Protected method that will not generally be called directly. It triggers
53557      * a toolbar update by reading the markup state of the current selection in the editor.
53558      *
53559      * Note you can force an update by calling on('editorevent', scope, false)
53560      */
53561     updateToolbar: function(editor ,ev, sel)
53562     {
53563         
53564         if (ev) {
53565             ev.stopEvent(); // se if we can stop this looping with mutiple events.
53566         }
53567         
53568         //Roo.log(ev);
53569         // capture mouse up - this is handy for selecting images..
53570         // perhaps should go somewhere else...
53571         if(!this.editorcore.activated){
53572              this.editor.onFirstFocus();
53573             return;
53574         }
53575         //Roo.log(ev ? ev.target : 'NOTARGET');
53576         
53577         
53578         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
53579         // selectNode - might want to handle IE?
53580         
53581         
53582         
53583         if (ev &&
53584             (ev.type == 'mouseup' || ev.type == 'click' ) &&
53585             ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
53586             // they have click on an image...
53587             // let's see if we can change the selection...
53588             sel = ev.target;
53589             
53590             // this triggers looping?
53591             //this.editorcore.selectNode(sel);
53592              
53593         }
53594         
53595         // this forces an id..
53596         Array.from(this.editorcore.doc.body.querySelectorAll('.roo-ed-selection')).forEach(function(e) {
53597              e.classList.remove('roo-ed-selection');
53598         });
53599         //Roo.select('.roo-ed-selection', false, this.editorcore.doc).removeClass('roo-ed-selection');
53600         //Roo.get(node).addClass('roo-ed-selection');
53601       
53602         //var updateFooter = sel ? false : true; 
53603         
53604         
53605         var ans = this.editorcore.getAllAncestors();
53606         
53607         // pick
53608         var ty = Roo.form.HtmlEditor.ToolbarContext.types;
53609         
53610         if (!sel) { 
53611             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
53612             sel = sel ? sel : this.editorcore.doc.body;
53613             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
53614             
53615         }
53616         
53617         var tn = sel.tagName.toUpperCase();
53618         var lastSel = this.tb.selectedNode;
53619         this.tb.selectedNode = sel;
53620         var left_label = tn;
53621         
53622         // ok see if we are editing a block?
53623         
53624         var db = false;
53625         // you are not actually selecting the block.
53626         if (sel && sel.hasAttribute('data-block')) {
53627             db = sel;
53628         } else if (sel && sel.closest('[data-block]')) {
53629             
53630             db = sel.closest('[data-block]');
53631             //var cepar = sel.closest('[contenteditable=true]');
53632             //if (db && cepar && cepar.tagName != 'BODY') {
53633             //   db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
53634             //}   
53635         }
53636         
53637         
53638         var block = false;
53639         //if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
53640         if (db && this.editorcore.enableBlocks) {
53641             block = Roo.htmleditor.Block.factory(db);
53642             
53643             
53644             if (block) {
53645                  db.className = (
53646                         db.classList.length > 0  ? db.className + ' ' : ''
53647                     )  + 'roo-ed-selection';
53648                  
53649                  // since we removed it earlier... its not there..
53650                 tn = 'BLOCK.' + db.getAttribute('data-block');
53651                 
53652                 //this.editorcore.selectNode(db);
53653                 if (typeof(this.toolbars[tn]) == 'undefined') {
53654                    this.toolbars[tn] = this.buildToolbar( false  ,tn ,block.friendly_name, block);
53655                 }
53656                 this.toolbars[tn].selectedNode = db;
53657                 left_label = block.friendly_name;
53658                 ans = this.editorcore.getAllAncestors();
53659             }
53660             
53661                 
53662             
53663         }
53664         
53665         
53666         if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
53667             return; // no change?
53668         }
53669         
53670         
53671           
53672         this.tb.el.hide();
53673         ///console.log("show: " + tn);
53674         this.tb =  typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
53675         
53676         this.tb.el.show();
53677         // update name
53678         this.tb.items.first().el.innerHTML = left_label + ':&nbsp;';
53679         
53680         
53681         // update attributes
53682         if (block && this.tb.fields) {
53683              
53684             this.tb.fields.each(function(e) {
53685                 e.setValue(block[e.name]);
53686             });
53687             
53688             
53689         } else  if (this.tb.fields && this.tb.selectedNode) {
53690             this.tb.fields.each( function(e) {
53691                 if (e.stylename) {
53692                     e.setValue(this.tb.selectedNode.style[e.stylename]);
53693                     return;
53694                 } 
53695                 e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
53696             }, this);
53697             this.updateToolbarStyles(this.tb.selectedNode);  
53698         }
53699         
53700         
53701        
53702         Roo.menu.MenuMgr.hideAll();
53703
53704         
53705         
53706     
53707         // update the footer
53708         //
53709         this.updateFooter(ans);
53710              
53711     },
53712     
53713     updateToolbarStyles : function(sel)
53714     {
53715         var hasStyles = false;
53716         for(var i in this.styles) {
53717             hasStyles = true;
53718             break;
53719         }
53720         
53721         // update styles
53722         if (hasStyles && this.tb.hasStyles) { 
53723             var st = this.tb.fields.item(0);
53724             
53725             st.store.removeAll();
53726             var cn = sel.className.split(/\s+/);
53727             
53728             var avs = [];
53729             if (this.styles['*']) {
53730                 
53731                 Roo.each(this.styles['*'], function(v) {
53732                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
53733                 });
53734             }
53735             if (this.styles[tn]) { 
53736                 Roo.each(this.styles[tn], function(v) {
53737                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
53738                 });
53739             }
53740             
53741             st.store.loadData(avs);
53742             st.collapse();
53743             st.setValue(cn);
53744         }
53745     },
53746     
53747      
53748     updateFooter : function(ans)
53749     {
53750         var html = '';
53751         if (ans === false) {
53752             this.footDisp.dom.innerHTML = '';
53753             return;
53754         }
53755         
53756         this.footerEls = ans.reverse();
53757         Roo.each(this.footerEls, function(a,i) {
53758             if (!a) { return; }
53759             html += html.length ? ' &gt; '  :  '';
53760             
53761             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
53762             
53763         });
53764        
53765         // 
53766         var sz = this.footDisp.up('td').getSize();
53767         this.footDisp.dom.style.width = (sz.width -10) + 'px';
53768         this.footDisp.dom.style.marginLeft = '5px';
53769         
53770         this.footDisp.dom.style.overflow = 'hidden';
53771         
53772         this.footDisp.dom.innerHTML = html;
53773             
53774         
53775     },
53776    
53777        
53778     // private
53779     onDestroy : function(){
53780         if(this.rendered){
53781             
53782             this.tb.items.each(function(item){
53783                 if(item.menu){
53784                     item.menu.removeAll();
53785                     if(item.menu.el){
53786                         item.menu.el.destroy();
53787                     }
53788                 }
53789                 item.destroy();
53790             });
53791              
53792         }
53793     },
53794     onFirstFocus: function() {
53795         // need to do this for all the toolbars..
53796         this.tb.items.each(function(item){
53797            item.enable();
53798         });
53799     },
53800     buildToolbar: function(tlist, nm, friendly_name, block)
53801     {
53802         var editor = this.editor;
53803         var editorcore = this.editorcore;
53804          // create a new element.
53805         var wdiv = editor.wrap.createChild({
53806                 tag: 'div'
53807             }, editor.wrap.dom.firstChild.nextSibling, true);
53808         
53809        
53810         var tb = new Roo.Toolbar(wdiv);
53811         ///this.tb = tb; // << this sets the active toolbar..
53812         if (tlist === false && block) {
53813             tlist = block.contextMenu(this);
53814         }
53815         
53816         tb.hasStyles = false;
53817         tb.name = nm;
53818         
53819         tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ":&nbsp;");
53820         
53821         var styles = Array.from(this.styles);
53822         
53823         
53824         // styles...
53825         if (styles && styles.length) {
53826             tb.hasStyles = true;
53827             // this needs a multi-select checkbox...
53828             tb.addField( new Roo.form.ComboBox({
53829                 store: new Roo.data.SimpleStore({
53830                     id : 'val',
53831                     fields: ['val', 'selected'],
53832                     data : [] 
53833                 }),
53834                 name : '-roo-edit-className',
53835                 attrname : 'className',
53836                 displayField: 'val',
53837                 typeAhead: false,
53838                 mode: 'local',
53839                 editable : false,
53840                 triggerAction: 'all',
53841                 emptyText:'Select Style',
53842                 selectOnFocus:true,
53843                 width: 130,
53844                 listeners : {
53845                     'select': function(c, r, i) {
53846                         // initial support only for on class per el..
53847                         tb.selectedNode.className =  r ? r.get('val') : '';
53848                         editorcore.syncValue();
53849                     }
53850                 }
53851     
53852             }));
53853         }
53854         
53855         var tbc = Roo.form.HtmlEditor.ToolbarContext;
53856         
53857         
53858         for (var i = 0; i < tlist.length; i++) {
53859             
53860             // newer versions will use xtype cfg to create menus.
53861             if (typeof(tlist[i].xtype) != 'undefined') {
53862                 
53863                 tb[typeof(tlist[i].name)== 'undefined' ? 'add' : 'addField'](Roo.factory(tlist[i]));
53864                 
53865                 
53866                 continue;
53867             }
53868             
53869             var item = tlist[i];
53870             tb.add(item.title + ":&nbsp;");
53871             
53872             
53873             //optname == used so you can configure the options available..
53874             var opts = item.opts ? item.opts : false;
53875             if (item.optname) { // use the b
53876                 opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
53877            
53878             }
53879             
53880             if (opts) {
53881                 // opts == pulldown..
53882                 tb.addField( new Roo.form.ComboBox({
53883                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
53884                         id : 'val',
53885                         fields: ['val', 'display'],
53886                         data : opts  
53887                     }),
53888                     name : '-roo-edit-' + tlist[i].name,
53889                     
53890                     attrname : tlist[i].name,
53891                     stylename : item.style ? item.style : false,
53892                     
53893                     displayField: item.displayField ? item.displayField : 'val',
53894                     valueField :  'val',
53895                     typeAhead: false,
53896                     mode: typeof(tbc.stores[tlist[i].name]) != 'undefined'  ? 'remote' : 'local',
53897                     editable : false,
53898                     triggerAction: 'all',
53899                     emptyText:'Select',
53900                     selectOnFocus:true,
53901                     width: item.width ? item.width  : 130,
53902                     listeners : {
53903                         'select': function(c, r, i) {
53904                              
53905                             
53906                             if (c.stylename) {
53907                                 tb.selectedNode.style[c.stylename] =  r.get('val');
53908                                 editorcore.syncValue();
53909                                 return;
53910                             }
53911                             if (r === false) {
53912                                 tb.selectedNode.removeAttribute(c.attrname);
53913                                 editorcore.syncValue();
53914                                 return;
53915                             }
53916                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
53917                             editorcore.syncValue();
53918                         }
53919                     }
53920
53921                 }));
53922                 continue;
53923                     
53924                  
53925                 /*
53926                 tb.addField( new Roo.form.TextField({
53927                     name: i,
53928                     width: 100,
53929                     //allowBlank:false,
53930                     value: ''
53931                 }));
53932                 continue;
53933                 */
53934             }
53935             tb.addField( new Roo.form.TextField({
53936                 name: '-roo-edit-' + tlist[i].name,
53937                 attrname : tlist[i].name,
53938                 
53939                 width: item.width,
53940                 //allowBlank:true,
53941                 value: '',
53942                 listeners: {
53943                     'change' : function(f, nv, ov) {
53944                         
53945                          
53946                         tb.selectedNode.setAttribute(f.attrname, nv);
53947                         editorcore.syncValue();
53948                     }
53949                 }
53950             }));
53951              
53952         }
53953         
53954         var _this = this;
53955         var show_delete = !block || block.deleteTitle !== false;
53956         if(nm == 'BODY'){
53957             show_delete = false;
53958             tb.addSeparator();
53959         
53960             tb.addButton( {
53961                 text: 'Stylesheets',
53962
53963                 listeners : {
53964                     click : function ()
53965                     {
53966                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
53967                     }
53968                 }
53969             });
53970         }
53971         
53972         tb.addFill();
53973         if (show_delete) {
53974             tb.addButton({
53975                 text: block && block.deleteTitle ? block.deleteTitle  : 'Remove Block or Formating', // remove the tag, and puts the children outside...
53976         
53977                 listeners : {
53978                     click : function ()
53979                     {
53980                         var sn = tb.selectedNode;
53981                         if (block) {
53982                             sn = Roo.htmleditor.Block.factory(tb.selectedNode).removeNode();
53983                             
53984                         }
53985                         if (!sn) {
53986                             return;
53987                         }
53988                         var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
53989                         if (sn.hasAttribute('data-block')) {
53990                             stn =  sn.nextSibling || sn.previousSibling || sn.parentNode;
53991                             sn.parentNode.removeChild(sn);
53992                             
53993                         } else if (sn && sn.tagName != 'BODY') {
53994                             // remove and keep parents.
53995                             a = new Roo.htmleditor.FilterKeepChildren({tag : false});
53996                             a.replaceTag(sn);
53997                         }
53998                         
53999                         
54000                         var range = editorcore.createRange();
54001             
54002                         range.setStart(stn,0);
54003                         range.setEnd(stn,0); 
54004                         var selection = editorcore.getSelection();
54005                         selection.removeAllRanges();
54006                         selection.addRange(range);
54007                         
54008                         
54009                         //_this.updateToolbar(null, null, pn);
54010                         _this.updateToolbar(null, null, null);
54011                         _this.updateFooter(false);
54012                         
54013                     }
54014                 }
54015                 
54016                         
54017                     
54018                 
54019             });
54020         }    
54021         
54022         tb.el.on('click', function(e){
54023             e.preventDefault(); // what does this do?
54024         });
54025         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
54026         tb.el.hide();
54027         
54028         // dont need to disable them... as they will get hidden
54029         return tb;
54030          
54031         
54032     },
54033     buildFooter : function()
54034     {
54035         
54036         var fel = this.editor.wrap.createChild();
54037         this.footer = new Roo.Toolbar(fel);
54038         // toolbar has scrolly on left / right?
54039         var footDisp= new Roo.Toolbar.Fill();
54040         var _t = this;
54041         this.footer.add(
54042             {
54043                 text : '&lt;',
54044                 xtype: 'Button',
54045                 handler : function() {
54046                     _t.footDisp.scrollTo('left',0,true)
54047                 }
54048             }
54049         );
54050         this.footer.add( footDisp );
54051         this.footer.add( 
54052             {
54053                 text : '&gt;',
54054                 xtype: 'Button',
54055                 handler : function() {
54056                     // no animation..
54057                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
54058                 }
54059             }
54060         );
54061         var fel = Roo.get(footDisp.el);
54062         fel.addClass('x-editor-context');
54063         this.footDispWrap = fel; 
54064         this.footDispWrap.overflow  = 'hidden';
54065         
54066         this.footDisp = fel.createChild();
54067         this.footDispWrap.on('click', this.onContextClick, this)
54068         
54069         
54070     },
54071     // when the footer contect changes
54072     onContextClick : function (ev,dom)
54073     {
54074         ev.preventDefault();
54075         var  cn = dom.className;
54076         //Roo.log(cn);
54077         if (!cn.match(/x-ed-loc-/)) {
54078             return;
54079         }
54080         var n = cn.split('-').pop();
54081         var ans = this.footerEls;
54082         var sel = ans[n];
54083         
54084         this.editorcore.selectNode(sel);
54085         
54086         
54087         this.updateToolbar(null, null, sel);
54088         
54089         
54090     }
54091     
54092     
54093     
54094     
54095     
54096 });
54097
54098
54099
54100
54101
54102 /*
54103  * Based on:
54104  * Ext JS Library 1.1.1
54105  * Copyright(c) 2006-2007, Ext JS, LLC.
54106  *
54107  * Originally Released Under LGPL - original licence link has changed is not relivant.
54108  *
54109  * Fork - LGPL
54110  * <script type="text/javascript">
54111  */
54112  
54113 /**
54114  * @class Roo.form.BasicForm
54115  * @extends Roo.util.Observable
54116  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
54117  * @constructor
54118  * @param {String/HTMLElement/Roo.Element} el The form element or its id
54119  * @param {Object} config Configuration options
54120  */
54121 Roo.form.BasicForm = function(el, config){
54122     this.allItems = [];
54123     this.childForms = [];
54124     Roo.apply(this, config);
54125     /*
54126      * The Roo.form.Field items in this form.
54127      * @type MixedCollection
54128      */
54129      
54130      
54131     this.items = new Roo.util.MixedCollection(false, function(o){
54132         return o.id || (o.id = Roo.id());
54133     });
54134     this.addEvents({
54135         /**
54136          * @event beforeaction
54137          * Fires before any action is performed. Return false to cancel the action.
54138          * @param {Form} this
54139          * @param {Action} action The action to be performed
54140          */
54141         beforeaction: true,
54142         /**
54143          * @event actionfailed
54144          * Fires when an action fails.
54145          * @param {Form} this
54146          * @param {Action} action The action that failed
54147          */
54148         actionfailed : true,
54149         /**
54150          * @event actioncomplete
54151          * Fires when an action is completed.
54152          * @param {Form} this
54153          * @param {Action} action The action that completed
54154          */
54155         actioncomplete : true
54156     });
54157     if(el){
54158         this.initEl(el);
54159     }
54160     Roo.form.BasicForm.superclass.constructor.call(this);
54161     
54162     Roo.form.BasicForm.popover.apply();
54163 };
54164
54165 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
54166     /**
54167      * @cfg {String} method
54168      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
54169      */
54170     /**
54171      * @cfg {DataReader} reader
54172      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
54173      * This is optional as there is built-in support for processing JSON.
54174      */
54175     /**
54176      * @cfg {DataReader} errorReader
54177      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
54178      * This is completely optional as there is built-in support for processing JSON.
54179      */
54180     /**
54181      * @cfg {String} url
54182      * The URL to use for form actions if one isn't supplied in the action options.
54183      */
54184     /**
54185      * @cfg {Boolean} fileUpload
54186      * Set to true if this form is a file upload.
54187      */
54188      
54189     /**
54190      * @cfg {Object} baseParams
54191      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
54192      */
54193      /**
54194      
54195     /**
54196      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
54197      */
54198     timeout: 30,
54199
54200     // private
54201     activeAction : null,
54202
54203     /**
54204      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
54205      * or setValues() data instead of when the form was first created.
54206      */
54207     trackResetOnLoad : false,
54208     
54209     
54210     /**
54211      * childForms - used for multi-tab forms
54212      * @type {Array}
54213      */
54214     childForms : false,
54215     
54216     /**
54217      * allItems - full list of fields.
54218      * @type {Array}
54219      */
54220     allItems : false,
54221     
54222     /**
54223      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
54224      * element by passing it or its id or mask the form itself by passing in true.
54225      * @type Mixed
54226      */
54227     waitMsgTarget : false,
54228     
54229     /**
54230      * @type Boolean
54231      */
54232     disableMask : false,
54233     
54234     /**
54235      * @cfg {Boolean} errorMask Should the form be masked (and the active element highlighted on error - default false
54236      */
54237     errorMask : false,
54238     
54239     /**
54240      * @cfg {Number} maskOffset space around form element to mask if there is an error Default 100
54241      */
54242     maskOffset : 100,
54243
54244     // private
54245     initEl : function(el){
54246         this.el = Roo.get(el);
54247         this.id = this.el.id || Roo.id();
54248         this.el.on('submit', this.onSubmit, this);
54249         this.el.addClass('x-form');
54250     },
54251
54252     // private
54253     onSubmit : function(e){
54254         e.stopEvent();
54255     },
54256
54257     /**
54258      * Returns true if client-side validation on the form is successful.
54259      * @return Boolean
54260      */
54261     isValid : function(){
54262         var valid = true;
54263         var target = false;
54264         this.items.each(function(f){
54265             if(f.validate()){
54266                 return;
54267             }
54268             
54269             valid = false;
54270                 
54271             if(!target && f.el.isVisible(true)){
54272                 target = f;
54273             }
54274         });
54275         
54276         if(this.errorMask && !valid){
54277             Roo.form.BasicForm.popover.mask(this, target);
54278         }
54279         
54280         return valid;
54281     },
54282     /**
54283      * Returns array of invalid form fields.
54284      * @return Array
54285      */
54286     
54287     invalidFields : function()
54288     {
54289         var ret = [];
54290         this.items.each(function(f){
54291             if(f.validate()){
54292                 return;
54293             }
54294             ret.push(f);
54295             
54296         });
54297         
54298         return ret;
54299     },
54300     
54301     
54302     /**
54303      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
54304      * @return Boolean
54305      */
54306     isDirty : function(){
54307         var dirty = false;
54308         this.items.each(function(f){
54309            if(f.isDirty()){
54310                dirty = true;
54311                return false;
54312            }
54313         });
54314         return dirty;
54315     },
54316     
54317     /**
54318      * Returns true if any fields in this form have changed since their original load. (New version)
54319      * @return Boolean
54320      */
54321     
54322     hasChanged : function()
54323     {
54324         var dirty = false;
54325         this.items.each(function(f){
54326            if(f.hasChanged()){
54327                dirty = true;
54328                return false;
54329            }
54330         });
54331         return dirty;
54332         
54333     },
54334     /**
54335      * Resets all hasChanged to 'false' -
54336      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
54337      * So hasChanged storage is only to be used for this purpose
54338      * @return Boolean
54339      */
54340     resetHasChanged : function()
54341     {
54342         this.items.each(function(f){
54343            f.resetHasChanged();
54344         });
54345         
54346     },
54347     
54348     
54349     /**
54350      * Performs a predefined action (submit or load) or custom actions you define on this form.
54351      * @param {String} actionName The name of the action type
54352      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
54353      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
54354      * accept other config options):
54355      * <pre>
54356 Property          Type             Description
54357 ----------------  ---------------  ----------------------------------------------------------------------------------
54358 url               String           The url for the action (defaults to the form's url)
54359 method            String           The form method to use (defaults to the form's method, or POST if not defined)
54360 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
54361 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
54362                                    validate the form on the client (defaults to false)
54363      * </pre>
54364      * @return {BasicForm} this
54365      */
54366     doAction : function(action, options){
54367         if(typeof action == 'string'){
54368             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
54369         }
54370         if(this.fireEvent('beforeaction', this, action) !== false){
54371             this.beforeAction(action);
54372             action.run.defer(100, action);
54373         }
54374         return this;
54375     },
54376
54377     /**
54378      * Shortcut to do a submit action.
54379      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
54380      * @return {BasicForm} this
54381      */
54382     submit : function(options){
54383         this.doAction('submit', options);
54384         return this;
54385     },
54386
54387     /**
54388      * Shortcut to do a load action.
54389      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
54390      * @return {BasicForm} this
54391      */
54392     load : function(options){
54393         this.doAction('load', options);
54394         return this;
54395     },
54396
54397     /**
54398      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
54399      * @param {Record} record The record to edit
54400      * @return {BasicForm} this
54401      */
54402     updateRecord : function(record){
54403         record.beginEdit();
54404         var fs = record.fields;
54405         fs.each(function(f){
54406             var field = this.findField(f.name);
54407             if(field){
54408                 record.set(f.name, field.getValue());
54409             }
54410         }, this);
54411         record.endEdit();
54412         return this;
54413     },
54414
54415     /**
54416      * Loads an Roo.data.Record into this form.
54417      * @param {Record} record The record to load
54418      * @return {BasicForm} this
54419      */
54420     loadRecord : function(record){
54421         this.setValues(record.data);
54422         return this;
54423     },
54424
54425     // private
54426     beforeAction : function(action){
54427         var o = action.options;
54428         
54429         if(!this.disableMask) {
54430             if(this.waitMsgTarget === true){
54431                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
54432             }else if(this.waitMsgTarget){
54433                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
54434                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
54435             }else {
54436                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
54437             }
54438         }
54439         
54440          
54441     },
54442
54443     // private
54444     afterAction : function(action, success){
54445         this.activeAction = null;
54446         var o = action.options;
54447         
54448         if(!this.disableMask) {
54449             if(this.waitMsgTarget === true){
54450                 this.el.unmask();
54451             }else if(this.waitMsgTarget){
54452                 this.waitMsgTarget.unmask();
54453             }else{
54454                 Roo.MessageBox.updateProgress(1);
54455                 Roo.MessageBox.hide();
54456             }
54457         }
54458         
54459         if(success){
54460             if(o.reset){
54461                 this.reset();
54462             }
54463             Roo.callback(o.success, o.scope, [this, action]);
54464             this.fireEvent('actioncomplete', this, action);
54465             
54466         }else{
54467             
54468             // failure condition..
54469             // we have a scenario where updates need confirming.
54470             // eg. if a locking scenario exists..
54471             // we look for { errors : { needs_confirm : true }} in the response.
54472             if (
54473                 (typeof(action.result) != 'undefined')  &&
54474                 (typeof(action.result.errors) != 'undefined')  &&
54475                 (typeof(action.result.errors.needs_confirm) != 'undefined')
54476            ){
54477                 var _t = this;
54478                 Roo.MessageBox.confirm(
54479                     "Change requires confirmation",
54480                     action.result.errorMsg,
54481                     function(r) {
54482                         if (r != 'yes') {
54483                             return;
54484                         }
54485                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
54486                     }
54487                     
54488                 );
54489                 
54490                 
54491                 
54492                 return;
54493             }
54494             
54495             Roo.callback(o.failure, o.scope, [this, action]);
54496             // show an error message if no failed handler is set..
54497             if (!this.hasListener('actionfailed')) {
54498                 Roo.MessageBox.alert("Error",
54499                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
54500                         action.result.errorMsg :
54501                         "Saving Failed, please check your entries or try again"
54502                 );
54503             }
54504             
54505             this.fireEvent('actionfailed', this, action);
54506         }
54507         
54508     },
54509
54510     /**
54511      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
54512      * @param {String} id The value to search for
54513      * @return Field
54514      */
54515     findField : function(id){
54516         var field = this.items.get(id);
54517         if(!field){
54518             this.items.each(function(f){
54519                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
54520                     field = f;
54521                     return false;
54522                 }
54523             });
54524         }
54525         return field || null;
54526     },
54527
54528     /**
54529      * Add a secondary form to this one, 
54530      * Used to provide tabbed forms. One form is primary, with hidden values 
54531      * which mirror the elements from the other forms.
54532      * 
54533      * @param {Roo.form.Form} form to add.
54534      * 
54535      */
54536     addForm : function(form)
54537     {
54538        
54539         if (this.childForms.indexOf(form) > -1) {
54540             // already added..
54541             return;
54542         }
54543         this.childForms.push(form);
54544         var n = '';
54545         Roo.each(form.allItems, function (fe) {
54546             
54547             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
54548             if (this.findField(n)) { // already added..
54549                 return;
54550             }
54551             var add = new Roo.form.Hidden({
54552                 name : n
54553             });
54554             add.render(this.el);
54555             
54556             this.add( add );
54557         }, this);
54558         
54559     },
54560     /**
54561      * Mark fields in this form invalid in bulk.
54562      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
54563      * @return {BasicForm} this
54564      */
54565     markInvalid : function(errors){
54566         if(errors instanceof Array){
54567             for(var i = 0, len = errors.length; i < len; i++){
54568                 var fieldError = errors[i];
54569                 var f = this.findField(fieldError.id);
54570                 if(f){
54571                     f.markInvalid(fieldError.msg);
54572                 }
54573             }
54574         }else{
54575             var field, id;
54576             for(id in errors){
54577                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
54578                     field.markInvalid(errors[id]);
54579                 }
54580             }
54581         }
54582         Roo.each(this.childForms || [], function (f) {
54583             f.markInvalid(errors);
54584         });
54585         
54586         return this;
54587     },
54588
54589     /**
54590      * Set values for fields in this form in bulk.
54591      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
54592      * @return {BasicForm} this
54593      */
54594     setValues : function(values){
54595         if(values instanceof Array){ // array of objects
54596             for(var i = 0, len = values.length; i < len; i++){
54597                 var v = values[i];
54598                 var f = this.findField(v.id);
54599                 if(f){
54600                     f.setValue(v.value);
54601                     if(this.trackResetOnLoad){
54602                         f.originalValue = f.getValue();
54603                     }
54604                 }
54605             }
54606         }else{ // object hash
54607             var field, id;
54608             for(id in values){
54609                 if(typeof values[id] != 'function' && (field = this.findField(id))){
54610                     
54611                     
54612                     
54613                     
54614                     if (field.setFromData && 
54615                         field.valueField && 
54616                         field.displayField &&
54617                         // combos' with local stores can 
54618                         // be queried via setValue()
54619                         // to set their value..
54620                         (field.store && !field.store.isLocal)
54621                         ) {
54622                         // it's a combo
54623                         var sd = { };
54624                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
54625                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
54626                         field.setFromData(sd);
54627                         
54628                     } else if (field.inputType && field.inputType == 'radio') {
54629                         
54630                         field.setValue(values[id]);
54631                     } else {
54632                         field.setValue(values[id]);
54633                     }
54634                     
54635                     
54636                     if(this.trackResetOnLoad){
54637                         field.originalValue = field.getValue();
54638                     }
54639                 }
54640             }
54641         }
54642         this.resetHasChanged();
54643         
54644         
54645         Roo.each(this.childForms || [], function (f) {
54646             f.setValues(values);
54647             f.resetHasChanged();
54648         });
54649                 
54650         return this;
54651     },
54652  
54653     /**
54654      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
54655      * they are returned as an array.
54656      * @param {Boolean} asString (def)
54657      * @return {Object}
54658      */
54659     getValues : function(asString)
54660     {
54661         if (this.childForms) {
54662             // copy values from the child forms
54663             Roo.each(this.childForms, function (f) {
54664                 this.setValues(f.getFieldValues()); // get the full set of data, as we might be copying comboboxes from external into this one.
54665             }, this);
54666         }
54667         
54668         // use formdata
54669         if (typeof(FormData) != 'undefined' && asString !== true) {
54670             // this relies on a 'recent' version of chrome apparently...
54671             try {
54672                 var fd = (new FormData(this.el.dom)).entries();
54673                 var ret = {};
54674                 var ent = fd.next();
54675                 while (!ent.done) {
54676                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
54677                     ent = fd.next();
54678                 };
54679                 return ret;
54680             } catch(e) {
54681                 
54682             }
54683             
54684         }
54685         
54686         
54687         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
54688         if(asString === true){
54689             return fs;
54690         }
54691         return Roo.urlDecode(fs);
54692     },
54693     
54694     /**
54695      * Returns the fields in this form as an object with key/value pairs. 
54696      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
54697      * Normally this will not return readOnly data 
54698      * @param {Boolean} with_readonly return readonly field data.
54699      * @return {Object}
54700      */
54701     getFieldValues : function(with_readonly)
54702     {
54703         if (this.childForms) {
54704             // copy values from the child forms
54705             // should this call getFieldValues - probably not as we do not currently copy
54706             // hidden fields when we generate..
54707             Roo.each(this.childForms, function (f) {
54708                 this.setValues(f.getFieldValues());
54709             }, this);
54710         }
54711         
54712         var ret = {};
54713         this.items.each(function(f){
54714             
54715             if (f.readOnly && with_readonly !== true) {
54716                 return; // skip read only values. - this is in theory to stop 'old' values being copied over new ones
54717                         // if a subform contains a copy of them.
54718                         // if you have subforms with the same editable data, you will need to copy the data back
54719                         // and forth.
54720             }
54721             
54722             if (!f.getName()) {
54723                 return;
54724             }
54725             var v = f.getValue();
54726             if (f.inputType =='radio') {
54727                 if (typeof(ret[f.getName()]) == 'undefined') {
54728                     ret[f.getName()] = ''; // empty..
54729                 }
54730                 
54731                 if (!f.el.dom.checked) {
54732                     return;
54733                     
54734                 }
54735                 v = f.el.dom.value;
54736                 
54737             }
54738             
54739             // not sure if this supported any more..
54740             if ((typeof(v) == 'object') && f.getRawValue) {
54741                 v = f.getRawValue() ; // dates..
54742             }
54743             // combo boxes where name != hiddenName...
54744             if (f.name != f.getName()) {
54745                 ret[f.name] = f.getRawValue();
54746             }
54747             ret[f.getName()] = v;
54748         });
54749         
54750         return ret;
54751     },
54752
54753     /**
54754      * Clears all invalid messages in this form.
54755      * @return {BasicForm} this
54756      */
54757     clearInvalid : function(){
54758         this.items.each(function(f){
54759            f.clearInvalid();
54760         });
54761         
54762         Roo.each(this.childForms || [], function (f) {
54763             f.clearInvalid();
54764         });
54765         
54766         
54767         return this;
54768     },
54769
54770     /**
54771      * Resets this form.
54772      * @return {BasicForm} this
54773      */
54774     reset : function(){
54775         this.items.each(function(f){
54776             f.reset();
54777         });
54778         
54779         Roo.each(this.childForms || [], function (f) {
54780             f.reset();
54781         });
54782         this.resetHasChanged();
54783         
54784         return this;
54785     },
54786
54787     /**
54788      * Add Roo.form components to this form.
54789      * @param {Field} field1
54790      * @param {Field} field2 (optional)
54791      * @param {Field} etc (optional)
54792      * @return {BasicForm} this
54793      */
54794     add : function(){
54795         this.items.addAll(Array.prototype.slice.call(arguments, 0));
54796         return this;
54797     },
54798
54799
54800     /**
54801      * Removes a field from the items collection (does NOT remove its markup).
54802      * @param {Field} field
54803      * @return {BasicForm} this
54804      */
54805     remove : function(field){
54806         this.items.remove(field);
54807         return this;
54808     },
54809
54810     /**
54811      * Looks at the fields in this form, checks them for an id attribute,
54812      * and calls applyTo on the existing dom element with that id.
54813      * @return {BasicForm} this
54814      */
54815     render : function(){
54816         this.items.each(function(f){
54817             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
54818                 f.applyTo(f.id);
54819             }
54820         });
54821         return this;
54822     },
54823
54824     /**
54825      * Calls {@link Ext#apply} for all fields in this form with the passed object.
54826      * @param {Object} values
54827      * @return {BasicForm} this
54828      */
54829     applyToFields : function(o){
54830         this.items.each(function(f){
54831            Roo.apply(f, o);
54832         });
54833         return this;
54834     },
54835
54836     /**
54837      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
54838      * @param {Object} values
54839      * @return {BasicForm} this
54840      */
54841     applyIfToFields : function(o){
54842         this.items.each(function(f){
54843            Roo.applyIf(f, o);
54844         });
54845         return this;
54846     }
54847 });
54848
54849 // back compat
54850 Roo.BasicForm = Roo.form.BasicForm;
54851
54852 Roo.apply(Roo.form.BasicForm, {
54853     
54854     popover : {
54855         
54856         padding : 5,
54857         
54858         isApplied : false,
54859         
54860         isMasked : false,
54861         
54862         form : false,
54863         
54864         target : false,
54865         
54866         intervalID : false,
54867         
54868         maskEl : false,
54869         
54870         apply : function()
54871         {
54872             if(this.isApplied){
54873                 return;
54874             }
54875             
54876             this.maskEl = {
54877                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
54878                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
54879                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
54880                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
54881             };
54882             
54883             this.maskEl.top.enableDisplayMode("block");
54884             this.maskEl.left.enableDisplayMode("block");
54885             this.maskEl.bottom.enableDisplayMode("block");
54886             this.maskEl.right.enableDisplayMode("block");
54887             
54888             Roo.get(document.body).on('click', function(){
54889                 this.unmask();
54890             }, this);
54891             
54892             Roo.get(document.body).on('touchstart', function(){
54893                 this.unmask();
54894             }, this);
54895             
54896             this.isApplied = true
54897         },
54898         
54899         mask : function(form, target)
54900         {
54901             this.form = form;
54902             
54903             this.target = target;
54904             
54905             if(!this.form.errorMask || !target.el){
54906                 return;
54907             }
54908             
54909             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
54910             
54911             var ot = this.target.el.calcOffsetsTo(scrollable);
54912             
54913             var scrollTo = ot[1] - this.form.maskOffset;
54914             
54915             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
54916             
54917             scrollable.scrollTo('top', scrollTo);
54918             
54919             var el = this.target.wrap || this.target.el;
54920             
54921             var box = el.getBox();
54922             
54923             this.maskEl.top.setStyle('position', 'absolute');
54924             this.maskEl.top.setStyle('z-index', 10000);
54925             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
54926             this.maskEl.top.setLeft(0);
54927             this.maskEl.top.setTop(0);
54928             this.maskEl.top.show();
54929             
54930             this.maskEl.left.setStyle('position', 'absolute');
54931             this.maskEl.left.setStyle('z-index', 10000);
54932             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
54933             this.maskEl.left.setLeft(0);
54934             this.maskEl.left.setTop(box.y - this.padding);
54935             this.maskEl.left.show();
54936
54937             this.maskEl.bottom.setStyle('position', 'absolute');
54938             this.maskEl.bottom.setStyle('z-index', 10000);
54939             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
54940             this.maskEl.bottom.setLeft(0);
54941             this.maskEl.bottom.setTop(box.bottom + this.padding);
54942             this.maskEl.bottom.show();
54943
54944             this.maskEl.right.setStyle('position', 'absolute');
54945             this.maskEl.right.setStyle('z-index', 10000);
54946             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
54947             this.maskEl.right.setLeft(box.right + this.padding);
54948             this.maskEl.right.setTop(box.y - this.padding);
54949             this.maskEl.right.show();
54950
54951             this.intervalID = window.setInterval(function() {
54952                 Roo.form.BasicForm.popover.unmask();
54953             }, 10000);
54954
54955             window.onwheel = function(){ return false;};
54956             
54957             (function(){ this.isMasked = true; }).defer(500, this);
54958             
54959         },
54960         
54961         unmask : function()
54962         {
54963             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
54964                 return;
54965             }
54966             
54967             this.maskEl.top.setStyle('position', 'absolute');
54968             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
54969             this.maskEl.top.hide();
54970
54971             this.maskEl.left.setStyle('position', 'absolute');
54972             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
54973             this.maskEl.left.hide();
54974
54975             this.maskEl.bottom.setStyle('position', 'absolute');
54976             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
54977             this.maskEl.bottom.hide();
54978
54979             this.maskEl.right.setStyle('position', 'absolute');
54980             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
54981             this.maskEl.right.hide();
54982             
54983             window.onwheel = function(){ return true;};
54984             
54985             if(this.intervalID){
54986                 window.clearInterval(this.intervalID);
54987                 this.intervalID = false;
54988             }
54989             
54990             this.isMasked = false;
54991             
54992         }
54993         
54994     }
54995     
54996 });/*
54997  * Based on:
54998  * Ext JS Library 1.1.1
54999  * Copyright(c) 2006-2007, Ext JS, LLC.
55000  *
55001  * Originally Released Under LGPL - original licence link has changed is not relivant.
55002  *
55003  * Fork - LGPL
55004  * <script type="text/javascript">
55005  */
55006
55007 /**
55008  * @class Roo.form.Form
55009  * @extends Roo.form.BasicForm
55010  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
55011  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
55012  * @constructor
55013  * @param {Object} config Configuration options
55014  */
55015 Roo.form.Form = function(config){
55016     var xitems =  [];
55017     if (config.items) {
55018         xitems = config.items;
55019         delete config.items;
55020     }
55021    
55022     
55023     Roo.form.Form.superclass.constructor.call(this, null, config);
55024     this.url = this.url || this.action;
55025     if(!this.root){
55026         this.root = new Roo.form.Layout(Roo.applyIf({
55027             id: Roo.id()
55028         }, config));
55029     }
55030     this.active = this.root;
55031     /**
55032      * Array of all the buttons that have been added to this form via {@link addButton}
55033      * @type Array
55034      */
55035     this.buttons = [];
55036     this.allItems = [];
55037     this.addEvents({
55038         /**
55039          * @event clientvalidation
55040          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
55041          * @param {Form} this
55042          * @param {Boolean} valid true if the form has passed client-side validation
55043          */
55044         clientvalidation: true,
55045         /**
55046          * @event rendered
55047          * Fires when the form is rendered
55048          * @param {Roo.form.Form} form
55049          */
55050         rendered : true
55051     });
55052     
55053     if (this.progressUrl) {
55054             // push a hidden field onto the list of fields..
55055             this.addxtype( {
55056                     xns: Roo.form, 
55057                     xtype : 'Hidden', 
55058                     name : 'UPLOAD_IDENTIFIER' 
55059             });
55060         }
55061         
55062     
55063     Roo.each(xitems, this.addxtype, this);
55064     
55065 };
55066
55067 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
55068      /**
55069      * @cfg {Roo.Button} buttons[] buttons at bottom of form
55070      */
55071     
55072     /**
55073      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
55074      */
55075     /**
55076      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
55077      */
55078     /**
55079      * @cfg {String} buttonAlign (left|center|right)  Valid values are "left," "center" and "right" (defaults to "center")
55080      */
55081     buttonAlign:'center',
55082
55083     /**
55084      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
55085      */
55086     minButtonWidth:75,
55087
55088     /**
55089      * @cfg {String} labelAlign (left|top|right) Valid values are "left," "top" and "right" (defaults to "left").
55090      * This property cascades to child containers if not set.
55091      */
55092     labelAlign:'left',
55093
55094     /**
55095      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
55096      * fires a looping event with that state. This is required to bind buttons to the valid
55097      * state using the config value formBind:true on the button.
55098      */
55099     monitorValid : false,
55100
55101     /**
55102      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
55103      */
55104     monitorPoll : 200,
55105     
55106     /**
55107      * @cfg {String} progressUrl - Url to return progress data 
55108      */
55109     
55110     progressUrl : false,
55111     /**
55112      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
55113      * sending a formdata with extra parameters - eg uploaded elements.
55114      */
55115     
55116     formData : false,
55117     
55118     /**
55119      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
55120      * fields are added and the column is closed. If no fields are passed the column remains open
55121      * until end() is called.
55122      * @param {Object} config The config to pass to the column
55123      * @param {Field} field1 (optional)
55124      * @param {Field} field2 (optional)
55125      * @param {Field} etc (optional)
55126      * @return Column The column container object
55127      */
55128     column : function(c){
55129         var col = new Roo.form.Column(c);
55130         this.start(col);
55131         if(arguments.length > 1){ // duplicate code required because of Opera
55132             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
55133             this.end();
55134         }
55135         return col;
55136     },
55137
55138     /**
55139      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
55140      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
55141      * until end() is called.
55142      * @param {Object} config The config to pass to the fieldset
55143      * @param {Field} field1 (optional)
55144      * @param {Field} field2 (optional)
55145      * @param {Field} etc (optional)
55146      * @return FieldSet The fieldset container object
55147      */
55148     fieldset : function(c){
55149         var fs = new Roo.form.FieldSet(c);
55150         this.start(fs);
55151         if(arguments.length > 1){ // duplicate code required because of Opera
55152             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
55153             this.end();
55154         }
55155         return fs;
55156     },
55157
55158     /**
55159      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
55160      * fields are added and the container is closed. If no fields are passed the container remains open
55161      * until end() is called.
55162      * @param {Object} config The config to pass to the Layout
55163      * @param {Field} field1 (optional)
55164      * @param {Field} field2 (optional)
55165      * @param {Field} etc (optional)
55166      * @return Layout The container object
55167      */
55168     container : function(c){
55169         var l = new Roo.form.Layout(c);
55170         this.start(l);
55171         if(arguments.length > 1){ // duplicate code required because of Opera
55172             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
55173             this.end();
55174         }
55175         return l;
55176     },
55177
55178     /**
55179      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
55180      * @param {Object} container A Roo.form.Layout or subclass of Layout
55181      * @return {Form} this
55182      */
55183     start : function(c){
55184         // cascade label info
55185         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
55186         this.active.stack.push(c);
55187         c.ownerCt = this.active;
55188         this.active = c;
55189         return this;
55190     },
55191
55192     /**
55193      * Closes the current open container
55194      * @return {Form} this
55195      */
55196     end : function(){
55197         if(this.active == this.root){
55198             return this;
55199         }
55200         this.active = this.active.ownerCt;
55201         return this;
55202     },
55203
55204     /**
55205      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
55206      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
55207      * as the label of the field.
55208      * @param {Field} field1
55209      * @param {Field} field2 (optional)
55210      * @param {Field} etc. (optional)
55211      * @return {Form} this
55212      */
55213     add : function(){
55214         this.active.stack.push.apply(this.active.stack, arguments);
55215         this.allItems.push.apply(this.allItems,arguments);
55216         var r = [];
55217         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
55218             if(a[i].isFormField){
55219                 r.push(a[i]);
55220             }
55221         }
55222         if(r.length > 0){
55223             Roo.form.Form.superclass.add.apply(this, r);
55224         }
55225         return this;
55226     },
55227     
55228
55229     
55230     
55231     
55232      /**
55233      * Find any element that has been added to a form, using it's ID or name
55234      * This can include framesets, columns etc. along with regular fields..
55235      * @param {String} id - id or name to find.
55236      
55237      * @return {Element} e - or false if nothing found.
55238      */
55239     findbyId : function(id)
55240     {
55241         var ret = false;
55242         if (!id) {
55243             return ret;
55244         }
55245         Roo.each(this.allItems, function(f){
55246             if (f.id == id || f.name == id ){
55247                 ret = f;
55248                 return false;
55249             }
55250         });
55251         return ret;
55252     },
55253
55254     
55255     
55256     /**
55257      * Render this form into the passed container. This should only be called once!
55258      * @param {String/HTMLElement/Element} container The element this component should be rendered into
55259      * @return {Form} this
55260      */
55261     render : function(ct)
55262     {
55263         
55264         
55265         
55266         ct = Roo.get(ct);
55267         var o = this.autoCreate || {
55268             tag: 'form',
55269             method : this.method || 'POST',
55270             id : this.id || Roo.id()
55271         };
55272         this.initEl(ct.createChild(o));
55273
55274         this.root.render(this.el);
55275         
55276        
55277              
55278         this.items.each(function(f){
55279             f.render('x-form-el-'+f.id);
55280         });
55281
55282         if(this.buttons.length > 0){
55283             // tables are required to maintain order and for correct IE layout
55284             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
55285                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
55286                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
55287             }}, null, true);
55288             var tr = tb.getElementsByTagName('tr')[0];
55289             for(var i = 0, len = this.buttons.length; i < len; i++) {
55290                 var b = this.buttons[i];
55291                 var td = document.createElement('td');
55292                 td.className = 'x-form-btn-td';
55293                 b.render(tr.appendChild(td));
55294             }
55295         }
55296         if(this.monitorValid){ // initialize after render
55297             this.startMonitoring();
55298         }
55299         this.fireEvent('rendered', this);
55300         return this;
55301     },
55302
55303     /**
55304      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
55305      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
55306      * object or a valid Roo.DomHelper element config
55307      * @param {Function} handler The function called when the button is clicked
55308      * @param {Object} scope (optional) The scope of the handler function
55309      * @return {Roo.Button}
55310      */
55311     addButton : function(config, handler, scope){
55312         var bc = {
55313             handler: handler,
55314             scope: scope,
55315             minWidth: this.minButtonWidth,
55316             hideParent:true
55317         };
55318         if(typeof config == "string"){
55319             bc.text = config;
55320         }else{
55321             Roo.apply(bc, config);
55322         }
55323         var btn = new Roo.Button(null, bc);
55324         this.buttons.push(btn);
55325         return btn;
55326     },
55327
55328      /**
55329      * Adds a series of form elements (using the xtype property as the factory method.
55330      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
55331      * @param {Object} config 
55332      */
55333     
55334     addxtype : function()
55335     {
55336         var ar = Array.prototype.slice.call(arguments, 0);
55337         var ret = false;
55338         for(var i = 0; i < ar.length; i++) {
55339             if (!ar[i]) {
55340                 continue; // skip -- if this happends something invalid got sent, we 
55341                 // should ignore it, as basically that interface element will not show up
55342                 // and that should be pretty obvious!!
55343             }
55344             
55345             if (Roo.form[ar[i].xtype]) {
55346                 ar[i].form = this;
55347                 var fe = Roo.factory(ar[i], Roo.form);
55348                 if (!ret) {
55349                     ret = fe;
55350                 }
55351                 fe.form = this;
55352                 if (fe.store) {
55353                     fe.store.form = this;
55354                 }
55355                 if (fe.isLayout) {  
55356                          
55357                     this.start(fe);
55358                     this.allItems.push(fe);
55359                     if (fe.items && fe.addxtype) {
55360                         fe.addxtype.apply(fe, fe.items);
55361                         delete fe.items;
55362                     }
55363                      this.end();
55364                     continue;
55365                 }
55366                 
55367                 
55368                  
55369                 this.add(fe);
55370               //  console.log('adding ' + ar[i].xtype);
55371             }
55372             if (ar[i].xtype == 'Button') {  
55373                 //console.log('adding button');
55374                 //console.log(ar[i]);
55375                 this.addButton(ar[i]);
55376                 this.allItems.push(fe);
55377                 continue;
55378             }
55379             
55380             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
55381                 alert('end is not supported on xtype any more, use items');
55382             //    this.end();
55383             //    //console.log('adding end');
55384             }
55385             
55386         }
55387         return ret;
55388     },
55389     
55390     /**
55391      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
55392      * option "monitorValid"
55393      */
55394     startMonitoring : function(){
55395         if(!this.bound){
55396             this.bound = true;
55397             Roo.TaskMgr.start({
55398                 run : this.bindHandler,
55399                 interval : this.monitorPoll || 200,
55400                 scope: this
55401             });
55402         }
55403     },
55404
55405     /**
55406      * Stops monitoring of the valid state of this form
55407      */
55408     stopMonitoring : function(){
55409         this.bound = false;
55410     },
55411
55412     // private
55413     bindHandler : function(){
55414         if(!this.bound){
55415             return false; // stops binding
55416         }
55417         var valid = true;
55418         this.items.each(function(f){
55419             if(!f.isValid(true)){
55420                 valid = false;
55421                 return false;
55422             }
55423         });
55424         for(var i = 0, len = this.buttons.length; i < len; i++){
55425             var btn = this.buttons[i];
55426             if(btn.formBind === true && btn.disabled === valid){
55427                 btn.setDisabled(!valid);
55428             }
55429         }
55430         this.fireEvent('clientvalidation', this, valid);
55431     }
55432     
55433     
55434     
55435     
55436     
55437     
55438     
55439     
55440 });
55441
55442
55443 // back compat
55444 Roo.Form = Roo.form.Form;
55445 /*
55446  * Based on:
55447  * Ext JS Library 1.1.1
55448  * Copyright(c) 2006-2007, Ext JS, LLC.
55449  *
55450  * Originally Released Under LGPL - original licence link has changed is not relivant.
55451  *
55452  * Fork - LGPL
55453  * <script type="text/javascript">
55454  */
55455
55456 // as we use this in bootstrap.
55457 Roo.namespace('Roo.form');
55458  /**
55459  * @class Roo.form.Action
55460  * Internal Class used to handle form actions
55461  * @constructor
55462  * @param {Roo.form.BasicForm} el The form element or its id
55463  * @param {Object} config Configuration options
55464  */
55465
55466  
55467  
55468 // define the action interface
55469 Roo.form.Action = function(form, options){
55470     this.form = form;
55471     this.options = options || {};
55472 };
55473 /**
55474  * Client Validation Failed
55475  * @const 
55476  */
55477 Roo.form.Action.CLIENT_INVALID = 'client';
55478 /**
55479  * Server Validation Failed
55480  * @const 
55481  */
55482 Roo.form.Action.SERVER_INVALID = 'server';
55483  /**
55484  * Connect to Server Failed
55485  * @const 
55486  */
55487 Roo.form.Action.CONNECT_FAILURE = 'connect';
55488 /**
55489  * Reading Data from Server Failed
55490  * @const 
55491  */
55492 Roo.form.Action.LOAD_FAILURE = 'load';
55493
55494 Roo.form.Action.prototype = {
55495     type : 'default',
55496     failureType : undefined,
55497     response : undefined,
55498     result : undefined,
55499
55500     // interface method
55501     run : function(options){
55502
55503     },
55504
55505     // interface method
55506     success : function(response){
55507
55508     },
55509
55510     // interface method
55511     handleResponse : function(response){
55512
55513     },
55514
55515     // default connection failure
55516     failure : function(response){
55517         
55518         this.response = response;
55519         this.failureType = Roo.form.Action.CONNECT_FAILURE;
55520         this.form.afterAction(this, false);
55521     },
55522
55523     processResponse : function(response){
55524         this.response = response;
55525         if(!response.responseText){
55526             return true;
55527         }
55528         this.result = this.handleResponse(response);
55529         return this.result;
55530     },
55531
55532     // utility functions used internally
55533     getUrl : function(appendParams){
55534         var url = this.options.url || this.form.url || this.form.el.dom.action;
55535         if(appendParams){
55536             var p = this.getParams();
55537             if(p){
55538                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
55539             }
55540         }
55541         return url;
55542     },
55543
55544     getMethod : function(){
55545         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
55546     },
55547
55548     getParams : function(){
55549         var bp = this.form.baseParams;
55550         var p = this.options.params;
55551         if(p){
55552             if(typeof p == "object"){
55553                 p = Roo.urlEncode(Roo.applyIf(p, bp));
55554             }else if(typeof p == 'string' && bp){
55555                 p += '&' + Roo.urlEncode(bp);
55556             }
55557         }else if(bp){
55558             p = Roo.urlEncode(bp);
55559         }
55560         return p;
55561     },
55562
55563     createCallback : function(){
55564         return {
55565             success: this.success,
55566             failure: this.failure,
55567             scope: this,
55568             timeout: (this.form.timeout*1000),
55569             upload: this.form.fileUpload ? this.success : undefined
55570         };
55571     }
55572 };
55573
55574 Roo.form.Action.Submit = function(form, options){
55575     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
55576 };
55577
55578 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
55579     type : 'submit',
55580
55581     haveProgress : false,
55582     uploadComplete : false,
55583     
55584     // uploadProgress indicator.
55585     uploadProgress : function()
55586     {
55587         if (!this.form.progressUrl) {
55588             return;
55589         }
55590         
55591         if (!this.haveProgress) {
55592             Roo.MessageBox.progress("Uploading", "Uploading");
55593         }
55594         if (this.uploadComplete) {
55595            Roo.MessageBox.hide();
55596            return;
55597         }
55598         
55599         this.haveProgress = true;
55600    
55601         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
55602         
55603         var c = new Roo.data.Connection();
55604         c.request({
55605             url : this.form.progressUrl,
55606             params: {
55607                 id : uid
55608             },
55609             method: 'GET',
55610             success : function(req){
55611                //console.log(data);
55612                 var rdata = false;
55613                 var edata;
55614                 try  {
55615                    rdata = Roo.decode(req.responseText)
55616                 } catch (e) {
55617                     Roo.log("Invalid data from server..");
55618                     Roo.log(edata);
55619                     return;
55620                 }
55621                 if (!rdata || !rdata.success) {
55622                     Roo.log(rdata);
55623                     Roo.MessageBox.alert(Roo.encode(rdata));
55624                     return;
55625                 }
55626                 var data = rdata.data;
55627                 
55628                 if (this.uploadComplete) {
55629                    Roo.MessageBox.hide();
55630                    return;
55631                 }
55632                    
55633                 if (data){
55634                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
55635                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
55636                     );
55637                 }
55638                 this.uploadProgress.defer(2000,this);
55639             },
55640        
55641             failure: function(data) {
55642                 Roo.log('progress url failed ');
55643                 Roo.log(data);
55644             },
55645             scope : this
55646         });
55647            
55648     },
55649     
55650     
55651     run : function()
55652     {
55653         // run get Values on the form, so it syncs any secondary forms.
55654         this.form.getValues();
55655         
55656         var o = this.options;
55657         var method = this.getMethod();
55658         var isPost = method == 'POST';
55659         if(o.clientValidation === false || this.form.isValid()){
55660             
55661             if (this.form.progressUrl) {
55662                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
55663                     (new Date() * 1) + '' + Math.random());
55664                     
55665             } 
55666             
55667             
55668             Roo.Ajax.request(Roo.apply(this.createCallback(), {
55669                 form:this.form.el.dom,
55670                 url:this.getUrl(!isPost),
55671                 method: method,
55672                 params:isPost ? this.getParams() : null,
55673                 isUpload: this.form.fileUpload,
55674                 formData : this.form.formData
55675             }));
55676             
55677             this.uploadProgress();
55678
55679         }else if (o.clientValidation !== false){ // client validation failed
55680             this.failureType = Roo.form.Action.CLIENT_INVALID;
55681             this.form.afterAction(this, false);
55682         }
55683     },
55684
55685     success : function(response)
55686     {
55687         this.uploadComplete= true;
55688         if (this.haveProgress) {
55689             Roo.MessageBox.hide();
55690         }
55691         
55692         
55693         var result = this.processResponse(response);
55694         if(result === true || result.success){
55695             this.form.afterAction(this, true);
55696             return;
55697         }
55698         if(result.errors){
55699             this.form.markInvalid(result.errors);
55700             this.failureType = Roo.form.Action.SERVER_INVALID;
55701         }
55702         this.form.afterAction(this, false);
55703     },
55704     failure : function(response)
55705     {
55706         this.uploadComplete= true;
55707         if (this.haveProgress) {
55708             Roo.MessageBox.hide();
55709         }
55710         
55711         this.response = response;
55712         this.failureType = Roo.form.Action.CONNECT_FAILURE;
55713         this.form.afterAction(this, false);
55714     },
55715     
55716     handleResponse : function(response){
55717         if(this.form.errorReader){
55718             var rs = this.form.errorReader.read(response);
55719             var errors = [];
55720             if(rs.records){
55721                 for(var i = 0, len = rs.records.length; i < len; i++) {
55722                     var r = rs.records[i];
55723                     errors[i] = r.data;
55724                 }
55725             }
55726             if(errors.length < 1){
55727                 errors = null;
55728             }
55729             return {
55730                 success : rs.success,
55731                 errors : errors
55732             };
55733         }
55734         var ret = false;
55735         try {
55736             var rt = response.responseText;
55737             if (rt.match(/^\<!--\[CDATA\[/)) {
55738                 rt = rt.replace(/^\<!--\[CDATA\[/,'');
55739                 rt = rt.replace(/\]\]--\>$/,'');
55740             }
55741             
55742             ret = Roo.decode(rt);
55743         } catch (e) {
55744             ret = {
55745                 success: false,
55746                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
55747                 errors : []
55748             };
55749         }
55750         return ret;
55751         
55752     }
55753 });
55754
55755
55756 Roo.form.Action.Load = function(form, options){
55757     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
55758     this.reader = this.form.reader;
55759 };
55760
55761 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
55762     type : 'load',
55763
55764     run : function(){
55765         
55766         Roo.Ajax.request(Roo.apply(
55767                 this.createCallback(), {
55768                     method:this.getMethod(),
55769                     url:this.getUrl(false),
55770                     params:this.getParams()
55771         }));
55772     },
55773
55774     success : function(response){
55775         
55776         var result = this.processResponse(response);
55777         if(result === true || !result.success || !result.data){
55778             this.failureType = Roo.form.Action.LOAD_FAILURE;
55779             this.form.afterAction(this, false);
55780             return;
55781         }
55782         this.form.clearInvalid();
55783         this.form.setValues(result.data);
55784         this.form.afterAction(this, true);
55785     },
55786
55787     handleResponse : function(response){
55788         if(this.form.reader){
55789             var rs = this.form.reader.read(response);
55790             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
55791             return {
55792                 success : rs.success,
55793                 data : data
55794             };
55795         }
55796         return Roo.decode(response.responseText);
55797     }
55798 });
55799
55800 Roo.form.Action.ACTION_TYPES = {
55801     'load' : Roo.form.Action.Load,
55802     'submit' : Roo.form.Action.Submit
55803 };/*
55804  * Based on:
55805  * Ext JS Library 1.1.1
55806  * Copyright(c) 2006-2007, Ext JS, LLC.
55807  *
55808  * Originally Released Under LGPL - original licence link has changed is not relivant.
55809  *
55810  * Fork - LGPL
55811  * <script type="text/javascript">
55812  */
55813  
55814 /**
55815  * @class Roo.form.Layout
55816  * @extends Roo.Component
55817  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
55818  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
55819  * @constructor
55820  * @param {Object} config Configuration options
55821  */
55822 Roo.form.Layout = function(config){
55823     var xitems = [];
55824     if (config.items) {
55825         xitems = config.items;
55826         delete config.items;
55827     }
55828     Roo.form.Layout.superclass.constructor.call(this, config);
55829     this.stack = [];
55830     Roo.each(xitems, this.addxtype, this);
55831      
55832 };
55833
55834 Roo.extend(Roo.form.Layout, Roo.Component, {
55835     /**
55836      * @cfg {String/Object} autoCreate
55837      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
55838      */
55839     /**
55840      * @cfg {String/Object/Function} style
55841      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
55842      * a function which returns such a specification.
55843      */
55844     /**
55845      * @cfg {String} labelAlign (left|top|right)
55846      * Valid values are "left," "top" and "right" (defaults to "left")
55847      */
55848     /**
55849      * @cfg {Number} labelWidth
55850      * Fixed width in pixels of all field labels (defaults to undefined)
55851      */
55852     /**
55853      * @cfg {Boolean} clear
55854      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
55855      */
55856     clear : true,
55857     /**
55858      * @cfg {String} labelSeparator
55859      * The separator to use after field labels (defaults to ':')
55860      */
55861     labelSeparator : ':',
55862     /**
55863      * @cfg {Boolean} hideLabels
55864      * True to suppress the display of field labels in this layout (defaults to false)
55865      */
55866     hideLabels : false,
55867
55868     // private
55869     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
55870     
55871     isLayout : true,
55872     
55873     // private
55874     onRender : function(ct, position){
55875         if(this.el){ // from markup
55876             this.el = Roo.get(this.el);
55877         }else {  // generate
55878             var cfg = this.getAutoCreate();
55879             this.el = ct.createChild(cfg, position);
55880         }
55881         if(this.style){
55882             this.el.applyStyles(this.style);
55883         }
55884         if(this.labelAlign){
55885             this.el.addClass('x-form-label-'+this.labelAlign);
55886         }
55887         if(this.hideLabels){
55888             this.labelStyle = "display:none";
55889             this.elementStyle = "padding-left:0;";
55890         }else{
55891             if(typeof this.labelWidth == 'number'){
55892                 this.labelStyle = "width:"+this.labelWidth+"px;";
55893                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
55894             }
55895             if(this.labelAlign == 'top'){
55896                 this.labelStyle = "width:auto;";
55897                 this.elementStyle = "padding-left:0;";
55898             }
55899         }
55900         var stack = this.stack;
55901         var slen = stack.length;
55902         if(slen > 0){
55903             if(!this.fieldTpl){
55904                 var t = new Roo.Template(
55905                     '<div class="x-form-item {5}">',
55906                         '<label for="{0}" style="{2}">{1}{4}</label>',
55907                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
55908                         '</div>',
55909                     '</div><div class="x-form-clear-left"></div>'
55910                 );
55911                 t.disableFormats = true;
55912                 t.compile();
55913                 Roo.form.Layout.prototype.fieldTpl = t;
55914             }
55915             for(var i = 0; i < slen; i++) {
55916                 if(stack[i].isFormField){
55917                     this.renderField(stack[i]);
55918                 }else{
55919                     this.renderComponent(stack[i]);
55920                 }
55921             }
55922         }
55923         if(this.clear){
55924             this.el.createChild({cls:'x-form-clear'});
55925         }
55926     },
55927
55928     // private
55929     renderField : function(f){
55930         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
55931                f.id, //0
55932                f.fieldLabel, //1
55933                f.labelStyle||this.labelStyle||'', //2
55934                this.elementStyle||'', //3
55935                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
55936                f.itemCls||this.itemCls||''  //5
55937        ], true).getPrevSibling());
55938     },
55939
55940     // private
55941     renderComponent : function(c){
55942         c.render(c.isLayout ? this.el : this.el.createChild());    
55943     },
55944     /**
55945      * Adds a object form elements (using the xtype property as the factory method.)
55946      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
55947      * @param {Object} config 
55948      */
55949     addxtype : function(o)
55950     {
55951         // create the lement.
55952         o.form = this.form;
55953         var fe = Roo.factory(o, Roo.form);
55954         this.form.allItems.push(fe);
55955         this.stack.push(fe);
55956         
55957         if (fe.isFormField) {
55958             this.form.items.add(fe);
55959         }
55960          
55961         return fe;
55962     }
55963 });
55964
55965
55966 /**
55967  * @class Roo.form.Column
55968  * @extends Roo.form.Layout
55969  * @children Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
55970  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
55971  * @constructor
55972  * @param {Object} config Configuration options
55973  */
55974 Roo.form.Column = function(config){
55975     Roo.form.Column.superclass.constructor.call(this, config);
55976 };
55977
55978 Roo.extend(Roo.form.Column, Roo.form.Layout, {
55979     /**
55980      * @cfg {Number/String} width
55981      * The fixed width of the column in pixels or CSS value (defaults to "auto")
55982      */
55983     /**
55984      * @cfg {String/Object} autoCreate
55985      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
55986      */
55987
55988     // private
55989     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
55990
55991     // private
55992     onRender : function(ct, position){
55993         Roo.form.Column.superclass.onRender.call(this, ct, position);
55994         if(this.width){
55995             this.el.setWidth(this.width);
55996         }
55997     }
55998 });
55999
56000 /**
56001  * @class Roo.form.Row
56002  * @extends Roo.form.Layout
56003  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
56004  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
56005  * @constructor
56006  * @param {Object} config Configuration options
56007  */
56008
56009  
56010 Roo.form.Row = function(config){
56011     Roo.form.Row.superclass.constructor.call(this, config);
56012 };
56013  
56014 Roo.extend(Roo.form.Row, Roo.form.Layout, {
56015       /**
56016      * @cfg {Number/String} width
56017      * The fixed width of the column in pixels or CSS value (defaults to "auto")
56018      */
56019     /**
56020      * @cfg {Number/String} height
56021      * The fixed height of the column in pixels or CSS value (defaults to "auto")
56022      */
56023     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
56024     
56025     padWidth : 20,
56026     // private
56027     onRender : function(ct, position){
56028         //console.log('row render');
56029         if(!this.rowTpl){
56030             var t = new Roo.Template(
56031                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
56032                     '<label for="{0}" style="{2}">{1}{4}</label>',
56033                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
56034                     '</div>',
56035                 '</div>'
56036             );
56037             t.disableFormats = true;
56038             t.compile();
56039             Roo.form.Layout.prototype.rowTpl = t;
56040         }
56041         this.fieldTpl = this.rowTpl;
56042         
56043         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
56044         var labelWidth = 100;
56045         
56046         if ((this.labelAlign != 'top')) {
56047             if (typeof this.labelWidth == 'number') {
56048                 labelWidth = this.labelWidth
56049             }
56050             this.padWidth =  20 + labelWidth;
56051             
56052         }
56053         
56054         Roo.form.Column.superclass.onRender.call(this, ct, position);
56055         if(this.width){
56056             this.el.setWidth(this.width);
56057         }
56058         if(this.height){
56059             this.el.setHeight(this.height);
56060         }
56061     },
56062     
56063     // private
56064     renderField : function(f){
56065         f.fieldEl = this.fieldTpl.append(this.el, [
56066                f.id, f.fieldLabel,
56067                f.labelStyle||this.labelStyle||'',
56068                this.elementStyle||'',
56069                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
56070                f.itemCls||this.itemCls||'',
56071                f.width ? f.width + this.padWidth : 160 + this.padWidth
56072        ],true);
56073     }
56074 });
56075  
56076
56077 /**
56078  * @class Roo.form.FieldSet
56079  * @extends Roo.form.Layout
56080  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
56081  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
56082  * @constructor
56083  * @param {Object} config Configuration options
56084  */
56085 Roo.form.FieldSet = function(config){
56086     Roo.form.FieldSet.superclass.constructor.call(this, config);
56087 };
56088
56089 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
56090     /**
56091      * @cfg {String} legend
56092      * The text to display as the legend for the FieldSet (defaults to '')
56093      */
56094     /**
56095      * @cfg {String/Object} autoCreate
56096      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
56097      */
56098
56099     // private
56100     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
56101
56102     // private
56103     onRender : function(ct, position){
56104         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
56105         if(this.legend){
56106             this.setLegend(this.legend);
56107         }
56108     },
56109
56110     // private
56111     setLegend : function(text){
56112         if(this.rendered){
56113             this.el.child('legend').update(text);
56114         }
56115     }
56116 });/*
56117  * Based on:
56118  * Ext JS Library 1.1.1
56119  * Copyright(c) 2006-2007, Ext JS, LLC.
56120  *
56121  * Originally Released Under LGPL - original licence link has changed is not relivant.
56122  *
56123  * Fork - LGPL
56124  * <script type="text/javascript">
56125  */
56126 /**
56127  * @class Roo.form.VTypes
56128  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
56129  * @static
56130  */
56131 Roo.form.VTypes = function(){
56132     // closure these in so they are only created once.
56133     var alpha = /^[a-zA-Z_]+$/;
56134     var alphanum = /^[a-zA-Z0-9_]+$/;
56135     var email = /^([\w'-]+)(\.[\w'-]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
56136     var url = /^(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
56137     var urlWeb = /^((https?):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
56138
56139     // All these messages and functions are configurable
56140     return {
56141         /**
56142          * The function used to validate email addresses
56143          * @param {String} value The email address
56144          */
56145         email : function(v){
56146             return email.test(v);
56147         },
56148         /**
56149          * The error text to display when the email validation function returns false
56150          * @type String
56151          */
56152         emailText : 'This field should be an e-mail address in the format "user@domain.com"',
56153         /**
56154          * The keystroke filter mask to be applied on email input
56155          * @type RegExp
56156          */
56157         emailMask : /[a-z0-9_\.\-@]/i,
56158
56159         /**
56160          * The function used to validate URLs
56161          * @param {String} value The URL
56162          */
56163         url : function(v){
56164             return url.test(v);
56165         },
56166         /**
56167          * The funciton used to validate URLs (only allow schemes 'https' and 'http')
56168          * @param {String} v The URL
56169          */
56170         urlWeb : function(v) {
56171             return urlWeb.test(v);
56172         },
56173         /**
56174          * The error text to display when the url validation function returns false
56175          * @type String
56176          */
56177         urlText : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
56178         
56179         /**
56180          * The function used to validate alpha values
56181          * @param {String} value The value
56182          */
56183         alpha : function(v){
56184             return alpha.test(v);
56185         },
56186         /**
56187          * The error text to display when the alpha validation function returns false
56188          * @type String
56189          */
56190         alphaText : 'This field should only contain letters and _',
56191         /**
56192          * The keystroke filter mask to be applied on alpha input
56193          * @type RegExp
56194          */
56195         alphaMask : /[a-z_]/i,
56196
56197         /**
56198          * The function used to validate alphanumeric values
56199          * @param {String} value The value
56200          */
56201         alphanum : function(v){
56202             return alphanum.test(v);
56203         },
56204         /**
56205          * The error text to display when the alphanumeric validation function returns false
56206          * @type String
56207          */
56208         alphanumText : 'This field should only contain letters, numbers and _',
56209         /**
56210          * The keystroke filter mask to be applied on alphanumeric input
56211          * @type RegExp
56212          */
56213         alphanumMask : /[a-z0-9_]/i
56214     };
56215 }();//<script type="text/javascript">
56216
56217 /**
56218  * @class Roo.form.FCKeditor
56219  * @extends Roo.form.TextArea
56220  * Wrapper around the FCKEditor http://www.fckeditor.net
56221  * @constructor
56222  * Creates a new FCKeditor
56223  * @param {Object} config Configuration options
56224  */
56225 Roo.form.FCKeditor = function(config){
56226     Roo.form.FCKeditor.superclass.constructor.call(this, config);
56227     this.addEvents({
56228          /**
56229          * @event editorinit
56230          * Fired when the editor is initialized - you can add extra handlers here..
56231          * @param {FCKeditor} this
56232          * @param {Object} the FCK object.
56233          */
56234         editorinit : true
56235     });
56236     
56237     
56238 };
56239 Roo.form.FCKeditor.editors = { };
56240 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
56241 {
56242     //defaultAutoCreate : {
56243     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
56244     //},
56245     // private
56246     /**
56247      * @cfg {Object} fck options - see fck manual for details.
56248      */
56249     fckconfig : false,
56250     
56251     /**
56252      * @cfg {Object} fck toolbar set (Basic or Default)
56253      */
56254     toolbarSet : 'Basic',
56255     /**
56256      * @cfg {Object} fck BasePath
56257      */ 
56258     basePath : '/fckeditor/',
56259     
56260     
56261     frame : false,
56262     
56263     value : '',
56264     
56265    
56266     onRender : function(ct, position)
56267     {
56268         if(!this.el){
56269             this.defaultAutoCreate = {
56270                 tag: "textarea",
56271                 style:"width:300px;height:60px;",
56272                 autocomplete: "new-password"
56273             };
56274         }
56275         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
56276         /*
56277         if(this.grow){
56278             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
56279             if(this.preventScrollbars){
56280                 this.el.setStyle("overflow", "hidden");
56281             }
56282             this.el.setHeight(this.growMin);
56283         }
56284         */
56285         //console.log('onrender' + this.getId() );
56286         Roo.form.FCKeditor.editors[this.getId()] = this;
56287          
56288
56289         this.replaceTextarea() ;
56290         
56291     },
56292     
56293     getEditor : function() {
56294         return this.fckEditor;
56295     },
56296     /**
56297      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
56298      * @param {Mixed} value The value to set
56299      */
56300     
56301     
56302     setValue : function(value)
56303     {
56304         //console.log('setValue: ' + value);
56305         
56306         if(typeof(value) == 'undefined') { // not sure why this is happending...
56307             return;
56308         }
56309         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
56310         
56311         //if(!this.el || !this.getEditor()) {
56312         //    this.value = value;
56313             //this.setValue.defer(100,this,[value]);    
56314         //    return;
56315         //} 
56316         
56317         if(!this.getEditor()) {
56318             return;
56319         }
56320         
56321         this.getEditor().SetData(value);
56322         
56323         //
56324
56325     },
56326
56327     /**
56328      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
56329      * @return {Mixed} value The field value
56330      */
56331     getValue : function()
56332     {
56333         
56334         if (this.frame && this.frame.dom.style.display == 'none') {
56335             return Roo.form.FCKeditor.superclass.getValue.call(this);
56336         }
56337         
56338         if(!this.el || !this.getEditor()) {
56339            
56340            // this.getValue.defer(100,this); 
56341             return this.value;
56342         }
56343        
56344         
56345         var value=this.getEditor().GetData();
56346         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
56347         return Roo.form.FCKeditor.superclass.getValue.call(this);
56348         
56349
56350     },
56351
56352     /**
56353      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
56354      * @return {Mixed} value The field value
56355      */
56356     getRawValue : function()
56357     {
56358         if (this.frame && this.frame.dom.style.display == 'none') {
56359             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
56360         }
56361         
56362         if(!this.el || !this.getEditor()) {
56363             //this.getRawValue.defer(100,this); 
56364             return this.value;
56365             return;
56366         }
56367         
56368         
56369         
56370         var value=this.getEditor().GetData();
56371         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
56372         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
56373          
56374     },
56375     
56376     setSize : function(w,h) {
56377         
56378         
56379         
56380         //if (this.frame && this.frame.dom.style.display == 'none') {
56381         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
56382         //    return;
56383         //}
56384         //if(!this.el || !this.getEditor()) {
56385         //    this.setSize.defer(100,this, [w,h]); 
56386         //    return;
56387         //}
56388         
56389         
56390         
56391         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
56392         
56393         this.frame.dom.setAttribute('width', w);
56394         this.frame.dom.setAttribute('height', h);
56395         this.frame.setSize(w,h);
56396         
56397     },
56398     
56399     toggleSourceEdit : function(value) {
56400         
56401       
56402          
56403         this.el.dom.style.display = value ? '' : 'none';
56404         this.frame.dom.style.display = value ?  'none' : '';
56405         
56406     },
56407     
56408     
56409     focus: function(tag)
56410     {
56411         if (this.frame.dom.style.display == 'none') {
56412             return Roo.form.FCKeditor.superclass.focus.call(this);
56413         }
56414         if(!this.el || !this.getEditor()) {
56415             this.focus.defer(100,this, [tag]); 
56416             return;
56417         }
56418         
56419         
56420         
56421         
56422         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
56423         this.getEditor().Focus();
56424         if (tgs.length) {
56425             if (!this.getEditor().Selection.GetSelection()) {
56426                 this.focus.defer(100,this, [tag]); 
56427                 return;
56428             }
56429             
56430             
56431             var r = this.getEditor().EditorDocument.createRange();
56432             r.setStart(tgs[0],0);
56433             r.setEnd(tgs[0],0);
56434             this.getEditor().Selection.GetSelection().removeAllRanges();
56435             this.getEditor().Selection.GetSelection().addRange(r);
56436             this.getEditor().Focus();
56437         }
56438         
56439     },
56440     
56441     
56442     
56443     replaceTextarea : function()
56444     {
56445         if ( document.getElementById( this.getId() + '___Frame' ) ) {
56446             return ;
56447         }
56448         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
56449         //{
56450             // We must check the elements firstly using the Id and then the name.
56451         var oTextarea = document.getElementById( this.getId() );
56452         
56453         var colElementsByName = document.getElementsByName( this.getId() ) ;
56454          
56455         oTextarea.style.display = 'none' ;
56456
56457         if ( oTextarea.tabIndex ) {            
56458             this.TabIndex = oTextarea.tabIndex ;
56459         }
56460         
56461         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
56462         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
56463         this.frame = Roo.get(this.getId() + '___Frame')
56464     },
56465     
56466     _getConfigHtml : function()
56467     {
56468         var sConfig = '' ;
56469
56470         for ( var o in this.fckconfig ) {
56471             sConfig += sConfig.length > 0  ? '&amp;' : '';
56472             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
56473         }
56474
56475         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
56476     },
56477     
56478     
56479     _getIFrameHtml : function()
56480     {
56481         var sFile = 'fckeditor.html' ;
56482         /* no idea what this is about..
56483         try
56484         {
56485             if ( (/fcksource=true/i).test( window.top.location.search ) )
56486                 sFile = 'fckeditor.original.html' ;
56487         }
56488         catch (e) { 
56489         */
56490
56491         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
56492         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
56493         
56494         
56495         var html = '<iframe id="' + this.getId() +
56496             '___Frame" src="' + sLink +
56497             '" width="' + this.width +
56498             '" height="' + this.height + '"' +
56499             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
56500             ' frameborder="0" scrolling="no"></iframe>' ;
56501
56502         return html ;
56503     },
56504     
56505     _insertHtmlBefore : function( html, element )
56506     {
56507         if ( element.insertAdjacentHTML )       {
56508             // IE
56509             element.insertAdjacentHTML( 'beforeBegin', html ) ;
56510         } else { // Gecko
56511             var oRange = document.createRange() ;
56512             oRange.setStartBefore( element ) ;
56513             var oFragment = oRange.createContextualFragment( html );
56514             element.parentNode.insertBefore( oFragment, element ) ;
56515         }
56516     }
56517     
56518     
56519   
56520     
56521     
56522     
56523     
56524
56525 });
56526
56527 //Roo.reg('fckeditor', Roo.form.FCKeditor);
56528
56529 function FCKeditor_OnComplete(editorInstance){
56530     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
56531     f.fckEditor = editorInstance;
56532     //console.log("loaded");
56533     f.fireEvent('editorinit', f, editorInstance);
56534
56535   
56536
56537  
56538
56539
56540
56541
56542
56543
56544
56545
56546
56547
56548
56549
56550
56551
56552
56553 //<script type="text/javascript">
56554 /**
56555  * @class Roo.form.GridField
56556  * @extends Roo.form.Field
56557  * Embed a grid (or editable grid into a form)
56558  * STATUS ALPHA
56559  * 
56560  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
56561  * it needs 
56562  * xgrid.store = Roo.data.Store
56563  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
56564  * xgrid.store.reader = Roo.data.JsonReader 
56565  * 
56566  * 
56567  * @constructor
56568  * Creates a new GridField
56569  * @param {Object} config Configuration options
56570  */
56571 Roo.form.GridField = function(config){
56572     Roo.form.GridField.superclass.constructor.call(this, config);
56573      
56574 };
56575
56576 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
56577     /**
56578      * @cfg {Number} width  - used to restrict width of grid..
56579      */
56580     width : 100,
56581     /**
56582      * @cfg {Number} height - used to restrict height of grid..
56583      */
56584     height : 50,
56585      /**
56586      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
56587          * 
56588          *}
56589      */
56590     xgrid : false, 
56591     /**
56592      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
56593      * {tag: "input", type: "checkbox", autocomplete: "off"})
56594      */
56595    // defaultAutoCreate : { tag: 'div' },
56596     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
56597     /**
56598      * @cfg {String} addTitle Text to include for adding a title.
56599      */
56600     addTitle : false,
56601     //
56602     onResize : function(){
56603         Roo.form.Field.superclass.onResize.apply(this, arguments);
56604     },
56605
56606     initEvents : function(){
56607         // Roo.form.Checkbox.superclass.initEvents.call(this);
56608         // has no events...
56609        
56610     },
56611
56612
56613     getResizeEl : function(){
56614         return this.wrap;
56615     },
56616
56617     getPositionEl : function(){
56618         return this.wrap;
56619     },
56620
56621     // private
56622     onRender : function(ct, position){
56623         
56624         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
56625         var style = this.style;
56626         delete this.style;
56627         
56628         Roo.form.GridField.superclass.onRender.call(this, ct, position);
56629         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
56630         this.viewEl = this.wrap.createChild({ tag: 'div' });
56631         if (style) {
56632             this.viewEl.applyStyles(style);
56633         }
56634         if (this.width) {
56635             this.viewEl.setWidth(this.width);
56636         }
56637         if (this.height) {
56638             this.viewEl.setHeight(this.height);
56639         }
56640         //if(this.inputValue !== undefined){
56641         //this.setValue(this.value);
56642         
56643         
56644         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
56645         
56646         
56647         this.grid.render();
56648         this.grid.getDataSource().on('remove', this.refreshValue, this);
56649         this.grid.getDataSource().on('update', this.refreshValue, this);
56650         this.grid.on('afteredit', this.refreshValue, this);
56651  
56652     },
56653      
56654     
56655     /**
56656      * Sets the value of the item. 
56657      * @param {String} either an object  or a string..
56658      */
56659     setValue : function(v){
56660         //this.value = v;
56661         v = v || []; // empty set..
56662         // this does not seem smart - it really only affects memoryproxy grids..
56663         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
56664             var ds = this.grid.getDataSource();
56665             // assumes a json reader..
56666             var data = {}
56667             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
56668             ds.loadData( data);
56669         }
56670         // clear selection so it does not get stale.
56671         if (this.grid.sm) { 
56672             this.grid.sm.clearSelections();
56673         }
56674         
56675         Roo.form.GridField.superclass.setValue.call(this, v);
56676         this.refreshValue();
56677         // should load data in the grid really....
56678     },
56679     
56680     // private
56681     refreshValue: function() {
56682          var val = [];
56683         this.grid.getDataSource().each(function(r) {
56684             val.push(r.data);
56685         });
56686         this.el.dom.value = Roo.encode(val);
56687     }
56688     
56689      
56690     
56691     
56692 });/*
56693  * Based on:
56694  * Ext JS Library 1.1.1
56695  * Copyright(c) 2006-2007, Ext JS, LLC.
56696  *
56697  * Originally Released Under LGPL - original licence link has changed is not relivant.
56698  *
56699  * Fork - LGPL
56700  * <script type="text/javascript">
56701  */
56702 /**
56703  * @class Roo.form.DisplayField
56704  * @extends Roo.form.Field
56705  * A generic Field to display non-editable data.
56706  * @cfg {Boolean} closable (true|false) default false
56707  * @constructor
56708  * Creates a new Display Field item.
56709  * @param {Object} config Configuration options
56710  */
56711 Roo.form.DisplayField = function(config){
56712     Roo.form.DisplayField.superclass.constructor.call(this, config);
56713     
56714     this.addEvents({
56715         /**
56716          * @event close
56717          * Fires after the click the close btn
56718              * @param {Roo.form.DisplayField} this
56719              */
56720         close : true
56721     });
56722 };
56723
56724 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
56725     inputType:      'hidden',
56726     allowBlank:     true,
56727     readOnly:         true,
56728     
56729  
56730     /**
56731      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
56732      */
56733     focusClass : undefined,
56734     /**
56735      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
56736      */
56737     fieldClass: 'x-form-field',
56738     
56739      /**
56740      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
56741      */
56742     valueRenderer: undefined,
56743     
56744     width: 100,
56745     /**
56746      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
56747      * {tag: "input", type: "checkbox", autocomplete: "off"})
56748      */
56749      
56750  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
56751  
56752     closable : false,
56753     
56754     onResize : function(){
56755         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
56756         
56757     },
56758
56759     initEvents : function(){
56760         // Roo.form.Checkbox.superclass.initEvents.call(this);
56761         // has no events...
56762         
56763         if(this.closable){
56764             this.closeEl.on('click', this.onClose, this);
56765         }
56766        
56767     },
56768
56769
56770     getResizeEl : function(){
56771         return this.wrap;
56772     },
56773
56774     getPositionEl : function(){
56775         return this.wrap;
56776     },
56777
56778     // private
56779     onRender : function(ct, position){
56780         
56781         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
56782         //if(this.inputValue !== undefined){
56783         this.wrap = this.el.wrap();
56784         
56785         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
56786         
56787         if(this.closable){
56788             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
56789         }
56790         
56791         if (this.bodyStyle) {
56792             this.viewEl.applyStyles(this.bodyStyle);
56793         }
56794         //this.viewEl.setStyle('padding', '2px');
56795         
56796         this.setValue(this.value);
56797         
56798     },
56799 /*
56800     // private
56801     initValue : Roo.emptyFn,
56802
56803   */
56804
56805         // private
56806     onClick : function(){
56807         
56808     },
56809
56810     /**
56811      * Sets the checked state of the checkbox.
56812      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
56813      */
56814     setValue : function(v){
56815         this.value = v;
56816         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
56817         // this might be called before we have a dom element..
56818         if (!this.viewEl) {
56819             return;
56820         }
56821         this.viewEl.dom.innerHTML = html;
56822         Roo.form.DisplayField.superclass.setValue.call(this, v);
56823
56824     },
56825     
56826     onClose : function(e)
56827     {
56828         e.preventDefault();
56829         
56830         this.fireEvent('close', this);
56831     }
56832 });/*
56833  * 
56834  * Licence- LGPL
56835  * 
56836  */
56837
56838 /**
56839  * @class Roo.form.DayPicker
56840  * @extends Roo.form.Field
56841  * A Day picker show [M] [T] [W] ....
56842  * @constructor
56843  * Creates a new Day Picker
56844  * @param {Object} config Configuration options
56845  */
56846 Roo.form.DayPicker= function(config){
56847     Roo.form.DayPicker.superclass.constructor.call(this, config);
56848      
56849 };
56850
56851 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
56852     /**
56853      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
56854      */
56855     focusClass : undefined,
56856     /**
56857      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
56858      */
56859     fieldClass: "x-form-field",
56860    
56861     /**
56862      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
56863      * {tag: "input", type: "checkbox", autocomplete: "off"})
56864      */
56865     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
56866     
56867    
56868     actionMode : 'viewEl', 
56869     //
56870     // private
56871  
56872     inputType : 'hidden',
56873     
56874      
56875     inputElement: false, // real input element?
56876     basedOn: false, // ????
56877     
56878     isFormField: true, // not sure where this is needed!!!!
56879
56880     onResize : function(){
56881         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
56882         if(!this.boxLabel){
56883             this.el.alignTo(this.wrap, 'c-c');
56884         }
56885     },
56886
56887     initEvents : function(){
56888         Roo.form.Checkbox.superclass.initEvents.call(this);
56889         this.el.on("click", this.onClick,  this);
56890         this.el.on("change", this.onClick,  this);
56891     },
56892
56893
56894     getResizeEl : function(){
56895         return this.wrap;
56896     },
56897
56898     getPositionEl : function(){
56899         return this.wrap;
56900     },
56901
56902     
56903     // private
56904     onRender : function(ct, position){
56905         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
56906        
56907         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
56908         
56909         var r1 = '<table><tr>';
56910         var r2 = '<tr class="x-form-daypick-icons">';
56911         for (var i=0; i < 7; i++) {
56912             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
56913             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
56914         }
56915         
56916         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
56917         viewEl.select('img').on('click', this.onClick, this);
56918         this.viewEl = viewEl;   
56919         
56920         
56921         // this will not work on Chrome!!!
56922         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
56923         this.el.on('propertychange', this.setFromHidden,  this);  //ie
56924         
56925         
56926           
56927
56928     },
56929
56930     // private
56931     initValue : Roo.emptyFn,
56932
56933     /**
56934      * Returns the checked state of the checkbox.
56935      * @return {Boolean} True if checked, else false
56936      */
56937     getValue : function(){
56938         return this.el.dom.value;
56939         
56940     },
56941
56942         // private
56943     onClick : function(e){ 
56944         //this.setChecked(!this.checked);
56945         Roo.get(e.target).toggleClass('x-menu-item-checked');
56946         this.refreshValue();
56947         //if(this.el.dom.checked != this.checked){
56948         //    this.setValue(this.el.dom.checked);
56949        // }
56950     },
56951     
56952     // private
56953     refreshValue : function()
56954     {
56955         var val = '';
56956         this.viewEl.select('img',true).each(function(e,i,n)  {
56957             val += e.is(".x-menu-item-checked") ? String(n) : '';
56958         });
56959         this.setValue(val, true);
56960     },
56961
56962     /**
56963      * Sets the checked state of the checkbox.
56964      * On is always based on a string comparison between inputValue and the param.
56965      * @param {Boolean/String} value - the value to set 
56966      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
56967      */
56968     setValue : function(v,suppressEvent){
56969         if (!this.el.dom) {
56970             return;
56971         }
56972         var old = this.el.dom.value ;
56973         this.el.dom.value = v;
56974         if (suppressEvent) {
56975             return ;
56976         }
56977          
56978         // update display..
56979         this.viewEl.select('img',true).each(function(e,i,n)  {
56980             
56981             var on = e.is(".x-menu-item-checked");
56982             var newv = v.indexOf(String(n)) > -1;
56983             if (on != newv) {
56984                 e.toggleClass('x-menu-item-checked');
56985             }
56986             
56987         });
56988         
56989         
56990         this.fireEvent('change', this, v, old);
56991         
56992         
56993     },
56994    
56995     // handle setting of hidden value by some other method!!?!?
56996     setFromHidden: function()
56997     {
56998         if(!this.el){
56999             return;
57000         }
57001         //console.log("SET FROM HIDDEN");
57002         //alert('setFrom hidden');
57003         this.setValue(this.el.dom.value);
57004     },
57005     
57006     onDestroy : function()
57007     {
57008         if(this.viewEl){
57009             Roo.get(this.viewEl).remove();
57010         }
57011          
57012         Roo.form.DayPicker.superclass.onDestroy.call(this);
57013     }
57014
57015 });/*
57016  * RooJS Library 1.1.1
57017  * Copyright(c) 2008-2011  Alan Knowles
57018  *
57019  * License - LGPL
57020  */
57021  
57022
57023 /**
57024  * @class Roo.form.ComboCheck
57025  * @extends Roo.form.ComboBox
57026  * A combobox for multiple select items.
57027  *
57028  * FIXME - could do with a reset button..
57029  * 
57030  * @constructor
57031  * Create a new ComboCheck
57032  * @param {Object} config Configuration options
57033  */
57034 Roo.form.ComboCheck = function(config){
57035     Roo.form.ComboCheck.superclass.constructor.call(this, config);
57036     // should verify some data...
57037     // like
57038     // hiddenName = required..
57039     // displayField = required
57040     // valudField == required
57041     var req= [ 'hiddenName', 'displayField', 'valueField' ];
57042     var _t = this;
57043     Roo.each(req, function(e) {
57044         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
57045             throw "Roo.form.ComboCheck : missing value for: " + e;
57046         }
57047     });
57048     
57049     
57050 };
57051
57052 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
57053      
57054      
57055     editable : false,
57056      
57057     selectedClass: 'x-menu-item-checked', 
57058     
57059     // private
57060     onRender : function(ct, position){
57061         var _t = this;
57062         
57063         
57064         
57065         if(!this.tpl){
57066             var cls = 'x-combo-list';
57067
57068             
57069             this.tpl =  new Roo.Template({
57070                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
57071                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
57072                    '<span>{' + this.displayField + '}</span>' +
57073                     '</div>' 
57074                 
57075             });
57076         }
57077  
57078         
57079         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
57080         this.view.singleSelect = false;
57081         this.view.multiSelect = true;
57082         this.view.toggleSelect = true;
57083         this.pageTb.add(new Roo.Toolbar.Fill(),{
57084             
57085             text: 'Select All',
57086             handler: function() {
57087                 _t.selectAll();
57088             }
57089         },
57090         {
57091             text: 'Done',
57092             handler: function() {
57093                 _t.collapse();
57094             }
57095         });
57096     },
57097     
57098     cleanLeadingSpace : function(e)
57099     {
57100         // this is disabled, as it retriggers setvalue on blur
57101         return;
57102     },
57103     doForce : function() {
57104         // no idea what this did, but it blanks out our values.
57105         return;
57106     },
57107     onViewOver : function(e, t){
57108         // do nothing...
57109         return;
57110         
57111     },
57112     
57113     onViewClick : function(doFocus,index){
57114         return;
57115         
57116     },
57117     select: function () {
57118         //Roo.log("SELECT CALLED");
57119     },
57120      
57121     selectByValue : function(xv, scrollIntoView){
57122         var ar = this.getValueArray();
57123         var sels = [];
57124         
57125         Roo.each(ar, function(v) {
57126             if(v === undefined || v === null){
57127                 return;
57128             }
57129             var r = this.findRecord(this.valueField, v);
57130             if(r){
57131                 sels.push(this.store.indexOf(r))
57132                 
57133             }
57134         },this);
57135         this.view.select(sels);
57136         return false;
57137     },
57138     
57139     selectAll : function()
57140     {
57141         var sels = [];
57142         this.store.each(function(r,i) {
57143             sels.push(i);
57144         });
57145         this.view.select(sels);
57146         this.collapse();
57147         return false;
57148
57149     },
57150     
57151     onSelect : function(record, index){
57152        // Roo.log("onselect Called");
57153        // this is only called by the clear button now..
57154         this.view.clearSelections();
57155         this.setValue('[]');
57156         if (this.value != this.valueBefore) {
57157             this.fireEvent('change', this, this.value, this.valueBefore);
57158             this.valueBefore = this.value;
57159         }
57160     },
57161     getValueArray : function()
57162     {
57163         var ar = [] ;
57164         
57165         try {
57166             //Roo.log(this.value);
57167             if (typeof(this.value) == 'undefined') {
57168                 return [];
57169             }
57170             var ar = Roo.decode(this.value);
57171             return  ar instanceof Array ? ar : []; //?? valid?
57172             
57173         } catch(e) {
57174             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
57175             return [];
57176         }
57177          
57178     },
57179     expand : function ()
57180     {
57181         
57182         Roo.form.ComboCheck.superclass.expand.call(this);
57183         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
57184         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
57185         
57186
57187     },
57188     
57189     collapse : function(){
57190         Roo.form.ComboCheck.superclass.collapse.call(this);
57191         var sl = this.view.getSelectedIndexes();
57192         var st = this.store;
57193         var nv = [];
57194         var tv = [];
57195         var r;
57196         Roo.each(sl, function(i) {
57197             r = st.getAt(i);
57198             nv.push(r.get(this.valueField));
57199         },this);
57200         this.setValue(Roo.encode(nv));
57201         if (this.value != this.valueBefore) {
57202
57203             this.fireEvent('change', this, this.value, this.valueBefore);
57204             this.valueBefore = this.value;
57205         }
57206         
57207     },
57208     
57209     setValue : function(v){
57210         // Roo.log(v);
57211         this.value = v;
57212         
57213         var vals = this.getValueArray();
57214         var tv = [];
57215         Roo.each(vals, function(k) {
57216             var r = this.findRecord(this.valueField, k);
57217             if(r){
57218                 tv.push(r.data[this.displayField]);
57219             }else if(this.valueNotFoundText !== undefined){
57220                 tv.push( this.valueNotFoundText );
57221             }
57222         },this);
57223        // Roo.log(tv);
57224         
57225         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
57226         this.hiddenField.value = v;
57227         this.value = v;
57228     }
57229     
57230 });/*
57231  * Based on:
57232  * Ext JS Library 1.1.1
57233  * Copyright(c) 2006-2007, Ext JS, LLC.
57234  *
57235  * Originally Released Under LGPL - original licence link has changed is not relivant.
57236  *
57237  * Fork - LGPL
57238  * <script type="text/javascript">
57239  */
57240  
57241 /**
57242  * @class Roo.form.Signature
57243  * @extends Roo.form.Field
57244  * Signature field.  
57245  * @constructor
57246  * 
57247  * @param {Object} config Configuration options
57248  */
57249
57250 Roo.form.Signature = function(config){
57251     Roo.form.Signature.superclass.constructor.call(this, config);
57252     
57253     this.addEvents({// not in used??
57254          /**
57255          * @event confirm
57256          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
57257              * @param {Roo.form.Signature} combo This combo box
57258              */
57259         'confirm' : true,
57260         /**
57261          * @event reset
57262          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
57263              * @param {Roo.form.ComboBox} combo This combo box
57264              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
57265              */
57266         'reset' : true
57267     });
57268 };
57269
57270 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
57271     /**
57272      * @cfg {Object} labels Label to use when rendering a form.
57273      * defaults to 
57274      * labels : { 
57275      *      clear : "Clear",
57276      *      confirm : "Confirm"
57277      *  }
57278      */
57279     labels : { 
57280         clear : "Clear",
57281         confirm : "Confirm"
57282     },
57283     /**
57284      * @cfg {Number} width The signature panel width (defaults to 300)
57285      */
57286     width: 300,
57287     /**
57288      * @cfg {Number} height The signature panel height (defaults to 100)
57289      */
57290     height : 100,
57291     /**
57292      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
57293      */
57294     allowBlank : false,
57295     
57296     //private
57297     // {Object} signPanel The signature SVG panel element (defaults to {})
57298     signPanel : {},
57299     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
57300     isMouseDown : false,
57301     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
57302     isConfirmed : false,
57303     // {String} signatureTmp SVG mapping string (defaults to empty string)
57304     signatureTmp : '',
57305     
57306     
57307     defaultAutoCreate : { // modified by initCompnoent..
57308         tag: "input",
57309         type:"hidden"
57310     },
57311
57312     // private
57313     onRender : function(ct, position){
57314         
57315         Roo.form.Signature.superclass.onRender.call(this, ct, position);
57316         
57317         this.wrap = this.el.wrap({
57318             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
57319         });
57320         
57321         this.createToolbar(this);
57322         this.signPanel = this.wrap.createChild({
57323                 tag: 'div',
57324                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
57325             }, this.el
57326         );
57327             
57328         this.svgID = Roo.id();
57329         this.svgEl = this.signPanel.createChild({
57330               xmlns : 'http://www.w3.org/2000/svg',
57331               tag : 'svg',
57332               id : this.svgID + "-svg",
57333               width: this.width,
57334               height: this.height,
57335               viewBox: '0 0 '+this.width+' '+this.height,
57336               cn : [
57337                 {
57338                     tag: "rect",
57339                     id: this.svgID + "-svg-r",
57340                     width: this.width,
57341                     height: this.height,
57342                     fill: "#ffa"
57343                 },
57344                 {
57345                     tag: "line",
57346                     id: this.svgID + "-svg-l",
57347                     x1: "0", // start
57348                     y1: (this.height*0.8), // start set the line in 80% of height
57349                     x2: this.width, // end
57350                     y2: (this.height*0.8), // end set the line in 80% of height
57351                     'stroke': "#666",
57352                     'stroke-width': "1",
57353                     'stroke-dasharray': "3",
57354                     'shape-rendering': "crispEdges",
57355                     'pointer-events': "none"
57356                 },
57357                 {
57358                     tag: "path",
57359                     id: this.svgID + "-svg-p",
57360                     'stroke': "navy",
57361                     'stroke-width': "3",
57362                     'fill': "none",
57363                     'pointer-events': 'none'
57364                 }
57365               ]
57366         });
57367         this.createSVG();
57368         this.svgBox = this.svgEl.dom.getScreenCTM();
57369     },
57370     createSVG : function(){ 
57371         var svg = this.signPanel;
57372         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
57373         var t = this;
57374
57375         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
57376         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
57377         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
57378         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
57379         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
57380         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
57381         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
57382         
57383     },
57384     isTouchEvent : function(e){
57385         return e.type.match(/^touch/);
57386     },
57387     getCoords : function (e) {
57388         var pt    = this.svgEl.dom.createSVGPoint();
57389         pt.x = e.clientX; 
57390         pt.y = e.clientY;
57391         if (this.isTouchEvent(e)) {
57392             pt.x =  e.targetTouches[0].clientX;
57393             pt.y = e.targetTouches[0].clientY;
57394         }
57395         var a = this.svgEl.dom.getScreenCTM();
57396         var b = a.inverse();
57397         var mx = pt.matrixTransform(b);
57398         return mx.x + ',' + mx.y;
57399     },
57400     //mouse event headler 
57401     down : function (e) {
57402         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
57403         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
57404         
57405         this.isMouseDown = true;
57406         
57407         e.preventDefault();
57408     },
57409     move : function (e) {
57410         if (this.isMouseDown) {
57411             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
57412             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
57413         }
57414         
57415         e.preventDefault();
57416     },
57417     up : function (e) {
57418         this.isMouseDown = false;
57419         var sp = this.signatureTmp.split(' ');
57420         
57421         if(sp.length > 1){
57422             if(!sp[sp.length-2].match(/^L/)){
57423                 sp.pop();
57424                 sp.pop();
57425                 sp.push("");
57426                 this.signatureTmp = sp.join(" ");
57427             }
57428         }
57429         if(this.getValue() != this.signatureTmp){
57430             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
57431             this.isConfirmed = false;
57432         }
57433         e.preventDefault();
57434     },
57435     
57436     /**
57437      * Protected method that will not generally be called directly. It
57438      * is called when the editor creates its toolbar. Override this method if you need to
57439      * add custom toolbar buttons.
57440      * @param {HtmlEditor} editor
57441      */
57442     createToolbar : function(editor){
57443          function btn(id, toggle, handler){
57444             var xid = fid + '-'+ id ;
57445             return {
57446                 id : xid,
57447                 cmd : id,
57448                 cls : 'x-btn-icon x-edit-'+id,
57449                 enableToggle:toggle !== false,
57450                 scope: editor, // was editor...
57451                 handler:handler||editor.relayBtnCmd,
57452                 clickEvent:'mousedown',
57453                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
57454                 tabIndex:-1
57455             };
57456         }
57457         
57458         
57459         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
57460         this.tb = tb;
57461         this.tb.add(
57462            {
57463                 cls : ' x-signature-btn x-signature-'+id,
57464                 scope: editor, // was editor...
57465                 handler: this.reset,
57466                 clickEvent:'mousedown',
57467                 text: this.labels.clear
57468             },
57469             {
57470                  xtype : 'Fill',
57471                  xns: Roo.Toolbar
57472             }, 
57473             {
57474                 cls : '  x-signature-btn x-signature-'+id,
57475                 scope: editor, // was editor...
57476                 handler: this.confirmHandler,
57477                 clickEvent:'mousedown',
57478                 text: this.labels.confirm
57479             }
57480         );
57481     
57482     },
57483     //public
57484     /**
57485      * when user is clicked confirm then show this image.....
57486      * 
57487      * @return {String} Image Data URI
57488      */
57489     getImageDataURI : function(){
57490         var svg = this.svgEl.dom.parentNode.innerHTML;
57491         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
57492         return src; 
57493     },
57494     /**
57495      * 
57496      * @return {Boolean} this.isConfirmed
57497      */
57498     getConfirmed : function(){
57499         return this.isConfirmed;
57500     },
57501     /**
57502      * 
57503      * @return {Number} this.width
57504      */
57505     getWidth : function(){
57506         return this.width;
57507     },
57508     /**
57509      * 
57510      * @return {Number} this.height
57511      */
57512     getHeight : function(){
57513         return this.height;
57514     },
57515     // private
57516     getSignature : function(){
57517         return this.signatureTmp;
57518     },
57519     // private
57520     reset : function(){
57521         this.signatureTmp = '';
57522         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
57523         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
57524         this.isConfirmed = false;
57525         Roo.form.Signature.superclass.reset.call(this);
57526     },
57527     setSignature : function(s){
57528         this.signatureTmp = s;
57529         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
57530         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
57531         this.setValue(s);
57532         this.isConfirmed = false;
57533         Roo.form.Signature.superclass.reset.call(this);
57534     }, 
57535     test : function(){
57536 //        Roo.log(this.signPanel.dom.contentWindow.up())
57537     },
57538     //private
57539     setConfirmed : function(){
57540         
57541         
57542         
57543 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
57544     },
57545     // private
57546     confirmHandler : function(){
57547         if(!this.getSignature()){
57548             return;
57549         }
57550         
57551         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
57552         this.setValue(this.getSignature());
57553         this.isConfirmed = true;
57554         
57555         this.fireEvent('confirm', this);
57556     },
57557     // private
57558     // Subclasses should provide the validation implementation by overriding this
57559     validateValue : function(value){
57560         if(this.allowBlank){
57561             return true;
57562         }
57563         
57564         if(this.isConfirmed){
57565             return true;
57566         }
57567         return false;
57568     }
57569 });/*
57570  * Based on:
57571  * Ext JS Library 1.1.1
57572  * Copyright(c) 2006-2007, Ext JS, LLC.
57573  *
57574  * Originally Released Under LGPL - original licence link has changed is not relivant.
57575  *
57576  * Fork - LGPL
57577  * <script type="text/javascript">
57578  */
57579  
57580
57581 /**
57582  * @class Roo.form.ComboBox
57583  * @extends Roo.form.TriggerField
57584  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
57585  * @constructor
57586  * Create a new ComboBox.
57587  * @param {Object} config Configuration options
57588  */
57589 Roo.form.Select = function(config){
57590     Roo.form.Select.superclass.constructor.call(this, config);
57591      
57592 };
57593
57594 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
57595     /**
57596      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
57597      */
57598     /**
57599      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
57600      * rendering into an Roo.Editor, defaults to false)
57601      */
57602     /**
57603      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
57604      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
57605      */
57606     /**
57607      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
57608      */
57609     /**
57610      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
57611      * the dropdown list (defaults to undefined, with no header element)
57612      */
57613
57614      /**
57615      * @cfg {String/Roo.Template} tpl The template to use to render the output
57616      */
57617      
57618     // private
57619     defaultAutoCreate : {tag: "select"  },
57620     /**
57621      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
57622      */
57623     listWidth: undefined,
57624     /**
57625      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
57626      * mode = 'remote' or 'text' if mode = 'local')
57627      */
57628     displayField: undefined,
57629     /**
57630      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
57631      * mode = 'remote' or 'value' if mode = 'local'). 
57632      * Note: use of a valueField requires the user make a selection
57633      * in order for a value to be mapped.
57634      */
57635     valueField: undefined,
57636     
57637     
57638     /**
57639      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
57640      * field's data value (defaults to the underlying DOM element's name)
57641      */
57642     hiddenName: undefined,
57643     /**
57644      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
57645      */
57646     listClass: '',
57647     /**
57648      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
57649      */
57650     selectedClass: 'x-combo-selected',
57651     /**
57652      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
57653      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
57654      * which displays a downward arrow icon).
57655      */
57656     triggerClass : 'x-form-arrow-trigger',
57657     /**
57658      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
57659      */
57660     shadow:'sides',
57661     /**
57662      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
57663      * anchor positions (defaults to 'tl-bl')
57664      */
57665     listAlign: 'tl-bl?',
57666     /**
57667      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
57668      */
57669     maxHeight: 300,
57670     /**
57671      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
57672      * query specified by the allQuery config option (defaults to 'query')
57673      */
57674     triggerAction: 'query',
57675     /**
57676      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
57677      * (defaults to 4, does not apply if editable = false)
57678      */
57679     minChars : 4,
57680     /**
57681      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
57682      * delay (typeAheadDelay) if it matches a known value (defaults to false)
57683      */
57684     typeAhead: false,
57685     /**
57686      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
57687      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
57688      */
57689     queryDelay: 500,
57690     /**
57691      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
57692      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
57693      */
57694     pageSize: 0,
57695     /**
57696      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
57697      * when editable = true (defaults to false)
57698      */
57699     selectOnFocus:false,
57700     /**
57701      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
57702      */
57703     queryParam: 'query',
57704     /**
57705      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
57706      * when mode = 'remote' (defaults to 'Loading...')
57707      */
57708     loadingText: 'Loading...',
57709     /**
57710      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
57711      */
57712     resizable: false,
57713     /**
57714      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
57715      */
57716     handleHeight : 8,
57717     /**
57718      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
57719      * traditional select (defaults to true)
57720      */
57721     editable: true,
57722     /**
57723      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
57724      */
57725     allQuery: '',
57726     /**
57727      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
57728      */
57729     mode: 'remote',
57730     /**
57731      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
57732      * listWidth has a higher value)
57733      */
57734     minListWidth : 70,
57735     /**
57736      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
57737      * allow the user to set arbitrary text into the field (defaults to false)
57738      */
57739     forceSelection:false,
57740     /**
57741      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
57742      * if typeAhead = true (defaults to 250)
57743      */
57744     typeAheadDelay : 250,
57745     /**
57746      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
57747      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
57748      */
57749     valueNotFoundText : undefined,
57750     
57751     /**
57752      * @cfg {String} defaultValue The value displayed after loading the store.
57753      */
57754     defaultValue: '',
57755     
57756     /**
57757      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
57758      */
57759     blockFocus : false,
57760     
57761     /**
57762      * @cfg {Boolean} disableClear Disable showing of clear button.
57763      */
57764     disableClear : false,
57765     /**
57766      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
57767      */
57768     alwaysQuery : false,
57769     
57770     //private
57771     addicon : false,
57772     editicon: false,
57773     
57774     // element that contains real text value.. (when hidden is used..)
57775      
57776     // private
57777     onRender : function(ct, position){
57778         Roo.form.Field.prototype.onRender.call(this, ct, position);
57779         
57780         if(this.store){
57781             this.store.on('beforeload', this.onBeforeLoad, this);
57782             this.store.on('load', this.onLoad, this);
57783             this.store.on('loadexception', this.onLoadException, this);
57784             this.store.load({});
57785         }
57786         
57787         
57788         
57789     },
57790
57791     // private
57792     initEvents : function(){
57793         //Roo.form.ComboBox.superclass.initEvents.call(this);
57794  
57795     },
57796
57797     onDestroy : function(){
57798        
57799         if(this.store){
57800             this.store.un('beforeload', this.onBeforeLoad, this);
57801             this.store.un('load', this.onLoad, this);
57802             this.store.un('loadexception', this.onLoadException, this);
57803         }
57804         //Roo.form.ComboBox.superclass.onDestroy.call(this);
57805     },
57806
57807     // private
57808     fireKey : function(e){
57809         if(e.isNavKeyPress() && !this.list.isVisible()){
57810             this.fireEvent("specialkey", this, e);
57811         }
57812     },
57813
57814     // private
57815     onResize: function(w, h){
57816         
57817         return; 
57818     
57819         
57820     },
57821
57822     /**
57823      * Allow or prevent the user from directly editing the field text.  If false is passed,
57824      * the user will only be able to select from the items defined in the dropdown list.  This method
57825      * is the runtime equivalent of setting the 'editable' config option at config time.
57826      * @param {Boolean} value True to allow the user to directly edit the field text
57827      */
57828     setEditable : function(value){
57829          
57830     },
57831
57832     // private
57833     onBeforeLoad : function(){
57834         
57835         Roo.log("Select before load");
57836         return;
57837     
57838         this.innerList.update(this.loadingText ?
57839                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
57840         //this.restrictHeight();
57841         this.selectedIndex = -1;
57842     },
57843
57844     // private
57845     onLoad : function(){
57846
57847     
57848         var dom = this.el.dom;
57849         dom.innerHTML = '';
57850          var od = dom.ownerDocument;
57851          
57852         if (this.emptyText) {
57853             var op = od.createElement('option');
57854             op.setAttribute('value', '');
57855             op.innerHTML = String.format('{0}', this.emptyText);
57856             dom.appendChild(op);
57857         }
57858         if(this.store.getCount() > 0){
57859            
57860             var vf = this.valueField;
57861             var df = this.displayField;
57862             this.store.data.each(function(r) {
57863                 // which colmsn to use... testing - cdoe / title..
57864                 var op = od.createElement('option');
57865                 op.setAttribute('value', r.data[vf]);
57866                 op.innerHTML = String.format('{0}', r.data[df]);
57867                 dom.appendChild(op);
57868             });
57869             if (typeof(this.defaultValue != 'undefined')) {
57870                 this.setValue(this.defaultValue);
57871             }
57872             
57873              
57874         }else{
57875             //this.onEmptyResults();
57876         }
57877         //this.el.focus();
57878     },
57879     // private
57880     onLoadException : function()
57881     {
57882         dom.innerHTML = '';
57883             
57884         Roo.log("Select on load exception");
57885         return;
57886     
57887         this.collapse();
57888         Roo.log(this.store.reader.jsonData);
57889         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
57890             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
57891         }
57892         
57893         
57894     },
57895     // private
57896     onTypeAhead : function(){
57897          
57898     },
57899
57900     // private
57901     onSelect : function(record, index){
57902         Roo.log('on select?');
57903         return;
57904         if(this.fireEvent('beforeselect', this, record, index) !== false){
57905             this.setFromData(index > -1 ? record.data : false);
57906             this.collapse();
57907             this.fireEvent('select', this, record, index);
57908         }
57909     },
57910
57911     /**
57912      * Returns the currently selected field value or empty string if no value is set.
57913      * @return {String} value The selected value
57914      */
57915     getValue : function(){
57916         var dom = this.el.dom;
57917         this.value = dom.options[dom.selectedIndex].value;
57918         return this.value;
57919         
57920     },
57921
57922     /**
57923      * Clears any text/value currently set in the field
57924      */
57925     clearValue : function(){
57926         this.value = '';
57927         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
57928         
57929     },
57930
57931     /**
57932      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
57933      * will be displayed in the field.  If the value does not match the data value of an existing item,
57934      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
57935      * Otherwise the field will be blank (although the value will still be set).
57936      * @param {String} value The value to match
57937      */
57938     setValue : function(v){
57939         var d = this.el.dom;
57940         for (var i =0; i < d.options.length;i++) {
57941             if (v == d.options[i].value) {
57942                 d.selectedIndex = i;
57943                 this.value = v;
57944                 return;
57945             }
57946         }
57947         this.clearValue();
57948     },
57949     /**
57950      * @property {Object} the last set data for the element
57951      */
57952     
57953     lastData : false,
57954     /**
57955      * Sets the value of the field based on a object which is related to the record format for the store.
57956      * @param {Object} value the value to set as. or false on reset?
57957      */
57958     setFromData : function(o){
57959         Roo.log('setfrom data?');
57960          
57961         
57962         
57963     },
57964     // private
57965     reset : function(){
57966         this.clearValue();
57967     },
57968     // private
57969     findRecord : function(prop, value){
57970         
57971         return false;
57972     
57973         var record;
57974         if(this.store.getCount() > 0){
57975             this.store.each(function(r){
57976                 if(r.data[prop] == value){
57977                     record = r;
57978                     return false;
57979                 }
57980                 return true;
57981             });
57982         }
57983         return record;
57984     },
57985     
57986     getName: function()
57987     {
57988         // returns hidden if it's set..
57989         if (!this.rendered) {return ''};
57990         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
57991         
57992     },
57993      
57994
57995     
57996
57997     // private
57998     onEmptyResults : function(){
57999         Roo.log('empty results');
58000         //this.collapse();
58001     },
58002
58003     /**
58004      * Returns true if the dropdown list is expanded, else false.
58005      */
58006     isExpanded : function(){
58007         return false;
58008     },
58009
58010     /**
58011      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
58012      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
58013      * @param {String} value The data value of the item to select
58014      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
58015      * selected item if it is not currently in view (defaults to true)
58016      * @return {Boolean} True if the value matched an item in the list, else false
58017      */
58018     selectByValue : function(v, scrollIntoView){
58019         Roo.log('select By Value');
58020         return false;
58021     
58022         if(v !== undefined && v !== null){
58023             var r = this.findRecord(this.valueField || this.displayField, v);
58024             if(r){
58025                 this.select(this.store.indexOf(r), scrollIntoView);
58026                 return true;
58027             }
58028         }
58029         return false;
58030     },
58031
58032     /**
58033      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
58034      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
58035      * @param {Number} index The zero-based index of the list item to select
58036      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
58037      * selected item if it is not currently in view (defaults to true)
58038      */
58039     select : function(index, scrollIntoView){
58040         Roo.log('select ');
58041         return  ;
58042         
58043         this.selectedIndex = index;
58044         this.view.select(index);
58045         if(scrollIntoView !== false){
58046             var el = this.view.getNode(index);
58047             if(el){
58048                 this.innerList.scrollChildIntoView(el, false);
58049             }
58050         }
58051     },
58052
58053       
58054
58055     // private
58056     validateBlur : function(){
58057         
58058         return;
58059         
58060     },
58061
58062     // private
58063     initQuery : function(){
58064         this.doQuery(this.getRawValue());
58065     },
58066
58067     // private
58068     doForce : function(){
58069         if(this.el.dom.value.length > 0){
58070             this.el.dom.value =
58071                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
58072              
58073         }
58074     },
58075
58076     /**
58077      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
58078      * query allowing the query action to be canceled if needed.
58079      * @param {String} query The SQL query to execute
58080      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
58081      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
58082      * saved in the current store (defaults to false)
58083      */
58084     doQuery : function(q, forceAll){
58085         
58086         Roo.log('doQuery?');
58087         if(q === undefined || q === null){
58088             q = '';
58089         }
58090         var qe = {
58091             query: q,
58092             forceAll: forceAll,
58093             combo: this,
58094             cancel:false
58095         };
58096         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
58097             return false;
58098         }
58099         q = qe.query;
58100         forceAll = qe.forceAll;
58101         if(forceAll === true || (q.length >= this.minChars)){
58102             if(this.lastQuery != q || this.alwaysQuery){
58103                 this.lastQuery = q;
58104                 if(this.mode == 'local'){
58105                     this.selectedIndex = -1;
58106                     if(forceAll){
58107                         this.store.clearFilter();
58108                     }else{
58109                         this.store.filter(this.displayField, q);
58110                     }
58111                     this.onLoad();
58112                 }else{
58113                     this.store.baseParams[this.queryParam] = q;
58114                     this.store.load({
58115                         params: this.getParams(q)
58116                     });
58117                     this.expand();
58118                 }
58119             }else{
58120                 this.selectedIndex = -1;
58121                 this.onLoad();   
58122             }
58123         }
58124     },
58125
58126     // private
58127     getParams : function(q){
58128         var p = {};
58129         //p[this.queryParam] = q;
58130         if(this.pageSize){
58131             p.start = 0;
58132             p.limit = this.pageSize;
58133         }
58134         return p;
58135     },
58136
58137     /**
58138      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
58139      */
58140     collapse : function(){
58141         
58142     },
58143
58144     // private
58145     collapseIf : function(e){
58146         
58147     },
58148
58149     /**
58150      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
58151      */
58152     expand : function(){
58153         
58154     } ,
58155
58156     // private
58157      
58158
58159     /** 
58160     * @cfg {Boolean} grow 
58161     * @hide 
58162     */
58163     /** 
58164     * @cfg {Number} growMin 
58165     * @hide 
58166     */
58167     /** 
58168     * @cfg {Number} growMax 
58169     * @hide 
58170     */
58171     /**
58172      * @hide
58173      * @method autoSize
58174      */
58175     
58176     setWidth : function()
58177     {
58178         
58179     },
58180     getResizeEl : function(){
58181         return this.el;
58182     }
58183 });//<script type="text/javasscript">
58184  
58185
58186 /**
58187  * @class Roo.DDView
58188  * A DnD enabled version of Roo.View.
58189  * @param {Element/String} container The Element in which to create the View.
58190  * @param {String} tpl The template string used to create the markup for each element of the View
58191  * @param {Object} config The configuration properties. These include all the config options of
58192  * {@link Roo.View} plus some specific to this class.<br>
58193  * <p>
58194  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
58195  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
58196  * <p>
58197  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
58198 .x-view-drag-insert-above {
58199         border-top:1px dotted #3366cc;
58200 }
58201 .x-view-drag-insert-below {
58202         border-bottom:1px dotted #3366cc;
58203 }
58204 </code></pre>
58205  * 
58206  */
58207  
58208 Roo.DDView = function(container, tpl, config) {
58209     Roo.DDView.superclass.constructor.apply(this, arguments);
58210     this.getEl().setStyle("outline", "0px none");
58211     this.getEl().unselectable();
58212     if (this.dragGroup) {
58213         this.setDraggable(this.dragGroup.split(","));
58214     }
58215     if (this.dropGroup) {
58216         this.setDroppable(this.dropGroup.split(","));
58217     }
58218     if (this.deletable) {
58219         this.setDeletable();
58220     }
58221     this.isDirtyFlag = false;
58222         this.addEvents({
58223                 "drop" : true
58224         });
58225 };
58226
58227 Roo.extend(Roo.DDView, Roo.View, {
58228 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
58229 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
58230 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
58231 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
58232
58233         isFormField: true,
58234
58235         reset: Roo.emptyFn,
58236         
58237         clearInvalid: Roo.form.Field.prototype.clearInvalid,
58238
58239         validate: function() {
58240                 return true;
58241         },
58242         
58243         destroy: function() {
58244                 this.purgeListeners();
58245                 this.getEl.removeAllListeners();
58246                 this.getEl().remove();
58247                 if (this.dragZone) {
58248                         if (this.dragZone.destroy) {
58249                                 this.dragZone.destroy();
58250                         }
58251                 }
58252                 if (this.dropZone) {
58253                         if (this.dropZone.destroy) {
58254                                 this.dropZone.destroy();
58255                         }
58256                 }
58257         },
58258
58259 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
58260         getName: function() {
58261                 return this.name;
58262         },
58263
58264 /**     Loads the View from a JSON string representing the Records to put into the Store. */
58265         setValue: function(v) {
58266                 if (!this.store) {
58267                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
58268                 }
58269                 var data = {};
58270                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
58271                 this.store.proxy = new Roo.data.MemoryProxy(data);
58272                 this.store.load();
58273         },
58274
58275 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
58276         getValue: function() {
58277                 var result = '(';
58278                 this.store.each(function(rec) {
58279                         result += rec.id + ',';
58280                 });
58281                 return result.substr(0, result.length - 1) + ')';
58282         },
58283         
58284         getIds: function() {
58285                 var i = 0, result = new Array(this.store.getCount());
58286                 this.store.each(function(rec) {
58287                         result[i++] = rec.id;
58288                 });
58289                 return result;
58290         },
58291         
58292         isDirty: function() {
58293                 return this.isDirtyFlag;
58294         },
58295
58296 /**
58297  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
58298  *      whole Element becomes the target, and this causes the drop gesture to append.
58299  */
58300     getTargetFromEvent : function(e) {
58301                 var target = e.getTarget();
58302                 while ((target !== null) && (target.parentNode != this.el.dom)) {
58303                 target = target.parentNode;
58304                 }
58305                 if (!target) {
58306                         target = this.el.dom.lastChild || this.el.dom;
58307                 }
58308                 return target;
58309     },
58310
58311 /**
58312  *      Create the drag data which consists of an object which has the property "ddel" as
58313  *      the drag proxy element. 
58314  */
58315     getDragData : function(e) {
58316         var target = this.findItemFromChild(e.getTarget());
58317                 if(target) {
58318                         this.handleSelection(e);
58319                         var selNodes = this.getSelectedNodes();
58320             var dragData = {
58321                 source: this,
58322                 copy: this.copy || (this.allowCopy && e.ctrlKey),
58323                 nodes: selNodes,
58324                 records: []
58325                         };
58326                         var selectedIndices = this.getSelectedIndexes();
58327                         for (var i = 0; i < selectedIndices.length; i++) {
58328                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
58329                         }
58330                         if (selNodes.length == 1) {
58331                                 dragData.ddel = target.cloneNode(true); // the div element
58332                         } else {
58333                                 var div = document.createElement('div'); // create the multi element drag "ghost"
58334                                 div.className = 'multi-proxy';
58335                                 for (var i = 0, len = selNodes.length; i < len; i++) {
58336                                         div.appendChild(selNodes[i].cloneNode(true));
58337                                 }
58338                                 dragData.ddel = div;
58339                         }
58340             //console.log(dragData)
58341             //console.log(dragData.ddel.innerHTML)
58342                         return dragData;
58343                 }
58344         //console.log('nodragData')
58345                 return false;
58346     },
58347     
58348 /**     Specify to which ddGroup items in this DDView may be dragged. */
58349     setDraggable: function(ddGroup) {
58350         if (ddGroup instanceof Array) {
58351                 Roo.each(ddGroup, this.setDraggable, this);
58352                 return;
58353         }
58354         if (this.dragZone) {
58355                 this.dragZone.addToGroup(ddGroup);
58356         } else {
58357                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
58358                                 containerScroll: true,
58359                                 ddGroup: ddGroup 
58360
58361                         });
58362 //                      Draggability implies selection. DragZone's mousedown selects the element.
58363                         if (!this.multiSelect) { this.singleSelect = true; }
58364
58365 //                      Wire the DragZone's handlers up to methods in *this*
58366                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
58367                 }
58368     },
58369
58370 /**     Specify from which ddGroup this DDView accepts drops. */
58371     setDroppable: function(ddGroup) {
58372         if (ddGroup instanceof Array) {
58373                 Roo.each(ddGroup, this.setDroppable, this);
58374                 return;
58375         }
58376         if (this.dropZone) {
58377                 this.dropZone.addToGroup(ddGroup);
58378         } else {
58379                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
58380                                 containerScroll: true,
58381                                 ddGroup: ddGroup
58382                         });
58383
58384 //                      Wire the DropZone's handlers up to methods in *this*
58385                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
58386                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
58387                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
58388                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
58389                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
58390                 }
58391     },
58392
58393 /**     Decide whether to drop above or below a View node. */
58394     getDropPoint : function(e, n, dd){
58395         if (n == this.el.dom) { return "above"; }
58396                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
58397                 var c = t + (b - t) / 2;
58398                 var y = Roo.lib.Event.getPageY(e);
58399                 if(y <= c) {
58400                         return "above";
58401                 }else{
58402                         return "below";
58403                 }
58404     },
58405
58406     onNodeEnter : function(n, dd, e, data){
58407                 return false;
58408     },
58409     
58410     onNodeOver : function(n, dd, e, data){
58411                 var pt = this.getDropPoint(e, n, dd);
58412                 // set the insert point style on the target node
58413                 var dragElClass = this.dropNotAllowed;
58414                 if (pt) {
58415                         var targetElClass;
58416                         if (pt == "above"){
58417                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
58418                                 targetElClass = "x-view-drag-insert-above";
58419                         } else {
58420                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
58421                                 targetElClass = "x-view-drag-insert-below";
58422                         }
58423                         if (this.lastInsertClass != targetElClass){
58424                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
58425                                 this.lastInsertClass = targetElClass;
58426                         }
58427                 }
58428                 return dragElClass;
58429         },
58430
58431     onNodeOut : function(n, dd, e, data){
58432                 this.removeDropIndicators(n);
58433     },
58434
58435     onNodeDrop : function(n, dd, e, data){
58436         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
58437                 return false;
58438         }
58439         var pt = this.getDropPoint(e, n, dd);
58440                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
58441                 if (pt == "below") { insertAt++; }
58442                 for (var i = 0; i < data.records.length; i++) {
58443                         var r = data.records[i];
58444                         var dup = this.store.getById(r.id);
58445                         if (dup && (dd != this.dragZone)) {
58446                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
58447                         } else {
58448                                 if (data.copy) {
58449                                         this.store.insert(insertAt++, r.copy());
58450                                 } else {
58451                                         data.source.isDirtyFlag = true;
58452                                         r.store.remove(r);
58453                                         this.store.insert(insertAt++, r);
58454                                 }
58455                                 this.isDirtyFlag = true;
58456                         }
58457                 }
58458                 this.dragZone.cachedTarget = null;
58459                 return true;
58460     },
58461
58462     removeDropIndicators : function(n){
58463                 if(n){
58464                         Roo.fly(n).removeClass([
58465                                 "x-view-drag-insert-above",
58466                                 "x-view-drag-insert-below"]);
58467                         this.lastInsertClass = "_noclass";
58468                 }
58469     },
58470
58471 /**
58472  *      Utility method. Add a delete option to the DDView's context menu.
58473  *      @param {String} imageUrl The URL of the "delete" icon image.
58474  */
58475         setDeletable: function(imageUrl) {
58476                 if (!this.singleSelect && !this.multiSelect) {
58477                         this.singleSelect = true;
58478                 }
58479                 var c = this.getContextMenu();
58480                 this.contextMenu.on("itemclick", function(item) {
58481                         switch (item.id) {
58482                                 case "delete":
58483                                         this.remove(this.getSelectedIndexes());
58484                                         break;
58485                         }
58486                 }, this);
58487                 this.contextMenu.add({
58488                         icon: imageUrl,
58489                         id: "delete",
58490                         text: 'Delete'
58491                 });
58492         },
58493         
58494 /**     Return the context menu for this DDView. */
58495         getContextMenu: function() {
58496                 if (!this.contextMenu) {
58497 //                      Create the View's context menu
58498                         this.contextMenu = new Roo.menu.Menu({
58499                                 id: this.id + "-contextmenu"
58500                         });
58501                         this.el.on("contextmenu", this.showContextMenu, this);
58502                 }
58503                 return this.contextMenu;
58504         },
58505         
58506         disableContextMenu: function() {
58507                 if (this.contextMenu) {
58508                         this.el.un("contextmenu", this.showContextMenu, this);
58509                 }
58510         },
58511
58512         showContextMenu: function(e, item) {
58513         item = this.findItemFromChild(e.getTarget());
58514                 if (item) {
58515                         e.stopEvent();
58516                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
58517                         this.contextMenu.showAt(e.getXY());
58518             }
58519     },
58520
58521 /**
58522  *      Remove {@link Roo.data.Record}s at the specified indices.
58523  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
58524  */
58525     remove: function(selectedIndices) {
58526                 selectedIndices = [].concat(selectedIndices);
58527                 for (var i = 0; i < selectedIndices.length; i++) {
58528                         var rec = this.store.getAt(selectedIndices[i]);
58529                         this.store.remove(rec);
58530                 }
58531     },
58532
58533 /**
58534  *      Double click fires the event, but also, if this is draggable, and there is only one other
58535  *      related DropZone, it transfers the selected node.
58536  */
58537     onDblClick : function(e){
58538         var item = this.findItemFromChild(e.getTarget());
58539         if(item){
58540             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
58541                 return false;
58542             }
58543             if (this.dragGroup) {
58544                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
58545                     while (targets.indexOf(this.dropZone) > -1) {
58546                             targets.remove(this.dropZone);
58547                                 }
58548                     if (targets.length == 1) {
58549                                         this.dragZone.cachedTarget = null;
58550                         var el = Roo.get(targets[0].getEl());
58551                         var box = el.getBox(true);
58552                         targets[0].onNodeDrop(el.dom, {
58553                                 target: el.dom,
58554                                 xy: [box.x, box.y + box.height - 1]
58555                         }, null, this.getDragData(e));
58556                     }
58557                 }
58558         }
58559     },
58560     
58561     handleSelection: function(e) {
58562                 this.dragZone.cachedTarget = null;
58563         var item = this.findItemFromChild(e.getTarget());
58564         if (!item) {
58565                 this.clearSelections(true);
58566                 return;
58567         }
58568                 if (item && (this.multiSelect || this.singleSelect)){
58569                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
58570                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
58571                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
58572                                 this.unselect(item);
58573                         } else {
58574                                 this.select(item, this.multiSelect && e.ctrlKey);
58575                                 this.lastSelection = item;
58576                         }
58577                 }
58578     },
58579
58580     onItemClick : function(item, index, e){
58581                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
58582                         return false;
58583                 }
58584                 return true;
58585     },
58586
58587     unselect : function(nodeInfo, suppressEvent){
58588                 var node = this.getNode(nodeInfo);
58589                 if(node && this.isSelected(node)){
58590                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
58591                                 Roo.fly(node).removeClass(this.selectedClass);
58592                                 this.selections.remove(node);
58593                                 if(!suppressEvent){
58594                                         this.fireEvent("selectionchange", this, this.selections);
58595                                 }
58596                         }
58597                 }
58598     }
58599 });
58600 /*
58601  * Based on:
58602  * Ext JS Library 1.1.1
58603  * Copyright(c) 2006-2007, Ext JS, LLC.
58604  *
58605  * Originally Released Under LGPL - original licence link has changed is not relivant.
58606  *
58607  * Fork - LGPL
58608  * <script type="text/javascript">
58609  */
58610  
58611 /**
58612  * @class Roo.LayoutManager
58613  * @extends Roo.util.Observable
58614  * Base class for layout managers.
58615  */
58616 Roo.LayoutManager = function(container, config){
58617     Roo.LayoutManager.superclass.constructor.call(this);
58618     this.el = Roo.get(container);
58619     // ie scrollbar fix
58620     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
58621         document.body.scroll = "no";
58622     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
58623         this.el.position('relative');
58624     }
58625     this.id = this.el.id;
58626     this.el.addClass("x-layout-container");
58627     /** false to disable window resize monitoring @type Boolean */
58628     this.monitorWindowResize = true;
58629     this.regions = {};
58630     this.addEvents({
58631         /**
58632          * @event layout
58633          * Fires when a layout is performed. 
58634          * @param {Roo.LayoutManager} this
58635          */
58636         "layout" : true,
58637         /**
58638          * @event regionresized
58639          * Fires when the user resizes a region. 
58640          * @param {Roo.LayoutRegion} region The resized region
58641          * @param {Number} newSize The new size (width for east/west, height for north/south)
58642          */
58643         "regionresized" : true,
58644         /**
58645          * @event regioncollapsed
58646          * Fires when a region is collapsed. 
58647          * @param {Roo.LayoutRegion} region The collapsed region
58648          */
58649         "regioncollapsed" : true,
58650         /**
58651          * @event regionexpanded
58652          * Fires when a region is expanded.  
58653          * @param {Roo.LayoutRegion} region The expanded region
58654          */
58655         "regionexpanded" : true
58656     });
58657     this.updating = false;
58658     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
58659 };
58660
58661 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
58662     /**
58663      * Returns true if this layout is currently being updated
58664      * @return {Boolean}
58665      */
58666     isUpdating : function(){
58667         return this.updating; 
58668     },
58669     
58670     /**
58671      * Suspend the LayoutManager from doing auto-layouts while
58672      * making multiple add or remove calls
58673      */
58674     beginUpdate : function(){
58675         this.updating = true;    
58676     },
58677     
58678     /**
58679      * Restore auto-layouts and optionally disable the manager from performing a layout
58680      * @param {Boolean} noLayout true to disable a layout update 
58681      */
58682     endUpdate : function(noLayout){
58683         this.updating = false;
58684         if(!noLayout){
58685             this.layout();
58686         }    
58687     },
58688     
58689     layout: function(){
58690         
58691     },
58692     
58693     onRegionResized : function(region, newSize){
58694         this.fireEvent("regionresized", region, newSize);
58695         this.layout();
58696     },
58697     
58698     onRegionCollapsed : function(region){
58699         this.fireEvent("regioncollapsed", region);
58700     },
58701     
58702     onRegionExpanded : function(region){
58703         this.fireEvent("regionexpanded", region);
58704     },
58705         
58706     /**
58707      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
58708      * performs box-model adjustments.
58709      * @return {Object} The size as an object {width: (the width), height: (the height)}
58710      */
58711     getViewSize : function(){
58712         var size;
58713         if(this.el.dom != document.body){
58714             size = this.el.getSize();
58715         }else{
58716             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
58717         }
58718         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
58719         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
58720         return size;
58721     },
58722     
58723     /**
58724      * Returns the Element this layout is bound to.
58725      * @return {Roo.Element}
58726      */
58727     getEl : function(){
58728         return this.el;
58729     },
58730     
58731     /**
58732      * Returns the specified region.
58733      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
58734      * @return {Roo.LayoutRegion}
58735      */
58736     getRegion : function(target){
58737         return this.regions[target.toLowerCase()];
58738     },
58739     
58740     onWindowResize : function(){
58741         if(this.monitorWindowResize){
58742             this.layout();
58743         }
58744     }
58745 });/*
58746  * Based on:
58747  * Ext JS Library 1.1.1
58748  * Copyright(c) 2006-2007, Ext JS, LLC.
58749  *
58750  * Originally Released Under LGPL - original licence link has changed is not relivant.
58751  *
58752  * Fork - LGPL
58753  * <script type="text/javascript">
58754  */
58755 /**
58756  * @class Roo.BorderLayout
58757  * @extends Roo.LayoutManager
58758  * @children Roo.ContentPanel
58759  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
58760  * please see: <br><br>
58761  * <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>
58762  * <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>
58763  * Example:
58764  <pre><code>
58765  var layout = new Roo.BorderLayout(document.body, {
58766     north: {
58767         initialSize: 25,
58768         titlebar: false
58769     },
58770     west: {
58771         split:true,
58772         initialSize: 200,
58773         minSize: 175,
58774         maxSize: 400,
58775         titlebar: true,
58776         collapsible: true
58777     },
58778     east: {
58779         split:true,
58780         initialSize: 202,
58781         minSize: 175,
58782         maxSize: 400,
58783         titlebar: true,
58784         collapsible: true
58785     },
58786     south: {
58787         split:true,
58788         initialSize: 100,
58789         minSize: 100,
58790         maxSize: 200,
58791         titlebar: true,
58792         collapsible: true
58793     },
58794     center: {
58795         titlebar: true,
58796         autoScroll:true,
58797         resizeTabs: true,
58798         minTabWidth: 50,
58799         preferredTabWidth: 150
58800     }
58801 });
58802
58803 // shorthand
58804 var CP = Roo.ContentPanel;
58805
58806 layout.beginUpdate();
58807 layout.add("north", new CP("north", "North"));
58808 layout.add("south", new CP("south", {title: "South", closable: true}));
58809 layout.add("west", new CP("west", {title: "West"}));
58810 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
58811 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
58812 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
58813 layout.getRegion("center").showPanel("center1");
58814 layout.endUpdate();
58815 </code></pre>
58816
58817 <b>The container the layout is rendered into can be either the body element or any other element.
58818 If it is not the body element, the container needs to either be an absolute positioned element,
58819 or you will need to add "position:relative" to the css of the container.  You will also need to specify
58820 the container size if it is not the body element.</b>
58821
58822 * @constructor
58823 * Create a new BorderLayout
58824 * @param {String/HTMLElement/Element} container The container this layout is bound to
58825 * @param {Object} config Configuration options
58826  */
58827 Roo.BorderLayout = function(container, config){
58828     config = config || {};
58829     Roo.BorderLayout.superclass.constructor.call(this, container, config);
58830     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
58831     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
58832         var target = this.factory.validRegions[i];
58833         if(config[target]){
58834             this.addRegion(target, config[target]);
58835         }
58836     }
58837 };
58838
58839 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
58840         
58841         /**
58842          * @cfg {Roo.LayoutRegion} east
58843          */
58844         /**
58845          * @cfg {Roo.LayoutRegion} west
58846          */
58847         /**
58848          * @cfg {Roo.LayoutRegion} north
58849          */
58850         /**
58851          * @cfg {Roo.LayoutRegion} south
58852          */
58853         /**
58854          * @cfg {Roo.LayoutRegion} center
58855          */
58856     /**
58857      * Creates and adds a new region if it doesn't already exist.
58858      * @param {String} target The target region key (north, south, east, west or center).
58859      * @param {Object} config The regions config object
58860      * @return {BorderLayoutRegion} The new region
58861      */
58862     addRegion : function(target, config){
58863         if(!this.regions[target]){
58864             var r = this.factory.create(target, this, config);
58865             this.bindRegion(target, r);
58866         }
58867         return this.regions[target];
58868     },
58869
58870     // private (kinda)
58871     bindRegion : function(name, r){
58872         this.regions[name] = r;
58873         r.on("visibilitychange", this.layout, this);
58874         r.on("paneladded", this.layout, this);
58875         r.on("panelremoved", this.layout, this);
58876         r.on("invalidated", this.layout, this);
58877         r.on("resized", this.onRegionResized, this);
58878         r.on("collapsed", this.onRegionCollapsed, this);
58879         r.on("expanded", this.onRegionExpanded, this);
58880     },
58881
58882     /**
58883      * Performs a layout update.
58884      */
58885     layout : function(){
58886         if(this.updating) {
58887             return;
58888         }
58889         var size = this.getViewSize();
58890         var w = size.width;
58891         var h = size.height;
58892         var centerW = w;
58893         var centerH = h;
58894         var centerY = 0;
58895         var centerX = 0;
58896         //var x = 0, y = 0;
58897
58898         var rs = this.regions;
58899         var north = rs["north"];
58900         var south = rs["south"]; 
58901         var west = rs["west"];
58902         var east = rs["east"];
58903         var center = rs["center"];
58904         //if(this.hideOnLayout){ // not supported anymore
58905             //c.el.setStyle("display", "none");
58906         //}
58907         if(north && north.isVisible()){
58908             var b = north.getBox();
58909             var m = north.getMargins();
58910             b.width = w - (m.left+m.right);
58911             b.x = m.left;
58912             b.y = m.top;
58913             centerY = b.height + b.y + m.bottom;
58914             centerH -= centerY;
58915             north.updateBox(this.safeBox(b));
58916         }
58917         if(south && south.isVisible()){
58918             var b = south.getBox();
58919             var m = south.getMargins();
58920             b.width = w - (m.left+m.right);
58921             b.x = m.left;
58922             var totalHeight = (b.height + m.top + m.bottom);
58923             b.y = h - totalHeight + m.top;
58924             centerH -= totalHeight;
58925             south.updateBox(this.safeBox(b));
58926         }
58927         if(west && west.isVisible()){
58928             var b = west.getBox();
58929             var m = west.getMargins();
58930             b.height = centerH - (m.top+m.bottom);
58931             b.x = m.left;
58932             b.y = centerY + m.top;
58933             var totalWidth = (b.width + m.left + m.right);
58934             centerX += totalWidth;
58935             centerW -= totalWidth;
58936             west.updateBox(this.safeBox(b));
58937         }
58938         if(east && east.isVisible()){
58939             var b = east.getBox();
58940             var m = east.getMargins();
58941             b.height = centerH - (m.top+m.bottom);
58942             var totalWidth = (b.width + m.left + m.right);
58943             b.x = w - totalWidth + m.left;
58944             b.y = centerY + m.top;
58945             centerW -= totalWidth;
58946             east.updateBox(this.safeBox(b));
58947         }
58948         if(center){
58949             var m = center.getMargins();
58950             var centerBox = {
58951                 x: centerX + m.left,
58952                 y: centerY + m.top,
58953                 width: centerW - (m.left+m.right),
58954                 height: centerH - (m.top+m.bottom)
58955             };
58956             //if(this.hideOnLayout){
58957                 //center.el.setStyle("display", "block");
58958             //}
58959             center.updateBox(this.safeBox(centerBox));
58960         }
58961         this.el.repaint();
58962         this.fireEvent("layout", this);
58963     },
58964
58965     // private
58966     safeBox : function(box){
58967         box.width = Math.max(0, box.width);
58968         box.height = Math.max(0, box.height);
58969         return box;
58970     },
58971
58972     /**
58973      * Adds a ContentPanel (or subclass) to this layout.
58974      * @param {String} target The target region key (north, south, east, west or center).
58975      * @param {Roo.ContentPanel} panel The panel to add
58976      * @return {Roo.ContentPanel} The added panel
58977      */
58978     add : function(target, panel){
58979          
58980         target = target.toLowerCase();
58981         return this.regions[target].add(panel);
58982     },
58983
58984     /**
58985      * Remove a ContentPanel (or subclass) to this layout.
58986      * @param {String} target The target region key (north, south, east, west or center).
58987      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
58988      * @return {Roo.ContentPanel} The removed panel
58989      */
58990     remove : function(target, panel){
58991         target = target.toLowerCase();
58992         return this.regions[target].remove(panel);
58993     },
58994
58995     /**
58996      * Searches all regions for a panel with the specified id
58997      * @param {String} panelId
58998      * @return {Roo.ContentPanel} The panel or null if it wasn't found
58999      */
59000     findPanel : function(panelId){
59001         var rs = this.regions;
59002         for(var target in rs){
59003             if(typeof rs[target] != "function"){
59004                 var p = rs[target].getPanel(panelId);
59005                 if(p){
59006                     return p;
59007                 }
59008             }
59009         }
59010         return null;
59011     },
59012
59013     /**
59014      * Searches all regions for a panel with the specified id and activates (shows) it.
59015      * @param {String/ContentPanel} panelId The panels id or the panel itself
59016      * @return {Roo.ContentPanel} The shown panel or null
59017      */
59018     showPanel : function(panelId) {
59019       var rs = this.regions;
59020       for(var target in rs){
59021          var r = rs[target];
59022          if(typeof r != "function"){
59023             if(r.hasPanel(panelId)){
59024                return r.showPanel(panelId);
59025             }
59026          }
59027       }
59028       return null;
59029    },
59030
59031    /**
59032      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
59033      * @param {Roo.state.Provider} provider (optional) An alternate state provider
59034      */
59035     restoreState : function(provider){
59036         if(!provider){
59037             provider = Roo.state.Manager;
59038         }
59039         var sm = new Roo.LayoutStateManager();
59040         sm.init(this, provider);
59041     },
59042
59043     /**
59044      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
59045      * object should contain properties for each region to add ContentPanels to, and each property's value should be
59046      * a valid ContentPanel config object.  Example:
59047      * <pre><code>
59048 // Create the main layout
59049 var layout = new Roo.BorderLayout('main-ct', {
59050     west: {
59051         split:true,
59052         minSize: 175,
59053         titlebar: true
59054     },
59055     center: {
59056         title:'Components'
59057     }
59058 }, 'main-ct');
59059
59060 // Create and add multiple ContentPanels at once via configs
59061 layout.batchAdd({
59062    west: {
59063        id: 'source-files',
59064        autoCreate:true,
59065        title:'Ext Source Files',
59066        autoScroll:true,
59067        fitToFrame:true
59068    },
59069    center : {
59070        el: cview,
59071        autoScroll:true,
59072        fitToFrame:true,
59073        toolbar: tb,
59074        resizeEl:'cbody'
59075    }
59076 });
59077 </code></pre>
59078      * @param {Object} regions An object containing ContentPanel configs by region name
59079      */
59080     batchAdd : function(regions){
59081         this.beginUpdate();
59082         for(var rname in regions){
59083             var lr = this.regions[rname];
59084             if(lr){
59085                 this.addTypedPanels(lr, regions[rname]);
59086             }
59087         }
59088         this.endUpdate();
59089     },
59090
59091     // private
59092     addTypedPanels : function(lr, ps){
59093         if(typeof ps == 'string'){
59094             lr.add(new Roo.ContentPanel(ps));
59095         }
59096         else if(ps instanceof Array){
59097             for(var i =0, len = ps.length; i < len; i++){
59098                 this.addTypedPanels(lr, ps[i]);
59099             }
59100         }
59101         else if(!ps.events){ // raw config?
59102             var el = ps.el;
59103             delete ps.el; // prevent conflict
59104             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
59105         }
59106         else {  // panel object assumed!
59107             lr.add(ps);
59108         }
59109     },
59110     /**
59111      * Adds a xtype elements to the layout.
59112      * <pre><code>
59113
59114 layout.addxtype({
59115        xtype : 'ContentPanel',
59116        region: 'west',
59117        items: [ .... ]
59118    }
59119 );
59120
59121 layout.addxtype({
59122         xtype : 'NestedLayoutPanel',
59123         region: 'west',
59124         layout: {
59125            center: { },
59126            west: { }   
59127         },
59128         items : [ ... list of content panels or nested layout panels.. ]
59129    }
59130 );
59131 </code></pre>
59132      * @param {Object} cfg Xtype definition of item to add.
59133      */
59134     addxtype : function(cfg)
59135     {
59136         // basically accepts a pannel...
59137         // can accept a layout region..!?!?
59138         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
59139         
59140         if (!cfg.xtype.match(/Panel$/)) {
59141             return false;
59142         }
59143         var ret = false;
59144         
59145         if (typeof(cfg.region) == 'undefined') {
59146             Roo.log("Failed to add Panel, region was not set");
59147             Roo.log(cfg);
59148             return false;
59149         }
59150         var region = cfg.region;
59151         delete cfg.region;
59152         
59153           
59154         var xitems = [];
59155         if (cfg.items) {
59156             xitems = cfg.items;
59157             delete cfg.items;
59158         }
59159         var nb = false;
59160         
59161         switch(cfg.xtype) 
59162         {
59163             case 'ContentPanel':  // ContentPanel (el, cfg)
59164             case 'ScrollPanel':  // ContentPanel (el, cfg)
59165             case 'ViewPanel': 
59166                 if(cfg.autoCreate) {
59167                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
59168                 } else {
59169                     var el = this.el.createChild();
59170                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
59171                 }
59172                 
59173                 this.add(region, ret);
59174                 break;
59175             
59176             
59177             case 'TreePanel': // our new panel!
59178                 cfg.el = this.el.createChild();
59179                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
59180                 this.add(region, ret);
59181                 break;
59182             
59183             case 'NestedLayoutPanel': 
59184                 // create a new Layout (which is  a Border Layout...
59185                 var el = this.el.createChild();
59186                 var clayout = cfg.layout;
59187                 delete cfg.layout;
59188                 clayout.items   = clayout.items  || [];
59189                 // replace this exitems with the clayout ones..
59190                 xitems = clayout.items;
59191                  
59192                 
59193                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
59194                     cfg.background = false;
59195                 }
59196                 var layout = new Roo.BorderLayout(el, clayout);
59197                 
59198                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
59199                 //console.log('adding nested layout panel '  + cfg.toSource());
59200                 this.add(region, ret);
59201                 nb = {}; /// find first...
59202                 break;
59203                 
59204             case 'GridPanel': 
59205             
59206                 // needs grid and region
59207                 
59208                 //var el = this.getRegion(region).el.createChild();
59209                 var el = this.el.createChild();
59210                 // create the grid first...
59211                 
59212                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
59213                 delete cfg.grid;
59214                 if (region == 'center' && this.active ) {
59215                     cfg.background = false;
59216                 }
59217                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
59218                 
59219                 this.add(region, ret);
59220                 if (cfg.background) {
59221                     ret.on('activate', function(gp) {
59222                         if (!gp.grid.rendered) {
59223                             gp.grid.render();
59224                         }
59225                     });
59226                 } else {
59227                     grid.render();
59228                 }
59229                 break;
59230            
59231            
59232            
59233                 
59234                 
59235                 
59236             default:
59237                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
59238                     
59239                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
59240                     this.add(region, ret);
59241                 } else {
59242                 
59243                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
59244                     return null;
59245                 }
59246                 
59247              // GridPanel (grid, cfg)
59248             
59249         }
59250         this.beginUpdate();
59251         // add children..
59252         var region = '';
59253         var abn = {};
59254         Roo.each(xitems, function(i)  {
59255             region = nb && i.region ? i.region : false;
59256             
59257             var add = ret.addxtype(i);
59258            
59259             if (region) {
59260                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
59261                 if (!i.background) {
59262                     abn[region] = nb[region] ;
59263                 }
59264             }
59265             
59266         });
59267         this.endUpdate();
59268
59269         // make the last non-background panel active..
59270         //if (nb) { Roo.log(abn); }
59271         if (nb) {
59272             
59273             for(var r in abn) {
59274                 region = this.getRegion(r);
59275                 if (region) {
59276                     // tried using nb[r], but it does not work..
59277                      
59278                     region.showPanel(abn[r]);
59279                    
59280                 }
59281             }
59282         }
59283         return ret;
59284         
59285     }
59286 });
59287
59288 /**
59289  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
59290  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
59291  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
59292  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
59293  * <pre><code>
59294 // shorthand
59295 var CP = Roo.ContentPanel;
59296
59297 var layout = Roo.BorderLayout.create({
59298     north: {
59299         initialSize: 25,
59300         titlebar: false,
59301         panels: [new CP("north", "North")]
59302     },
59303     west: {
59304         split:true,
59305         initialSize: 200,
59306         minSize: 175,
59307         maxSize: 400,
59308         titlebar: true,
59309         collapsible: true,
59310         panels: [new CP("west", {title: "West"})]
59311     },
59312     east: {
59313         split:true,
59314         initialSize: 202,
59315         minSize: 175,
59316         maxSize: 400,
59317         titlebar: true,
59318         collapsible: true,
59319         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
59320     },
59321     south: {
59322         split:true,
59323         initialSize: 100,
59324         minSize: 100,
59325         maxSize: 200,
59326         titlebar: true,
59327         collapsible: true,
59328         panels: [new CP("south", {title: "South", closable: true})]
59329     },
59330     center: {
59331         titlebar: true,
59332         autoScroll:true,
59333         resizeTabs: true,
59334         minTabWidth: 50,
59335         preferredTabWidth: 150,
59336         panels: [
59337             new CP("center1", {title: "Close Me", closable: true}),
59338             new CP("center2", {title: "Center Panel", closable: false})
59339         ]
59340     }
59341 }, document.body);
59342
59343 layout.getRegion("center").showPanel("center1");
59344 </code></pre>
59345  * @param config
59346  * @param targetEl
59347  */
59348 Roo.BorderLayout.create = function(config, targetEl){
59349     var layout = new Roo.BorderLayout(targetEl || document.body, config);
59350     layout.beginUpdate();
59351     var regions = Roo.BorderLayout.RegionFactory.validRegions;
59352     for(var j = 0, jlen = regions.length; j < jlen; j++){
59353         var lr = regions[j];
59354         if(layout.regions[lr] && config[lr].panels){
59355             var r = layout.regions[lr];
59356             var ps = config[lr].panels;
59357             layout.addTypedPanels(r, ps);
59358         }
59359     }
59360     layout.endUpdate();
59361     return layout;
59362 };
59363
59364 // private
59365 Roo.BorderLayout.RegionFactory = {
59366     // private
59367     validRegions : ["north","south","east","west","center"],
59368
59369     // private
59370     create : function(target, mgr, config){
59371         target = target.toLowerCase();
59372         if(config.lightweight || config.basic){
59373             return new Roo.BasicLayoutRegion(mgr, config, target);
59374         }
59375         switch(target){
59376             case "north":
59377                 return new Roo.NorthLayoutRegion(mgr, config);
59378             case "south":
59379                 return new Roo.SouthLayoutRegion(mgr, config);
59380             case "east":
59381                 return new Roo.EastLayoutRegion(mgr, config);
59382             case "west":
59383                 return new Roo.WestLayoutRegion(mgr, config);
59384             case "center":
59385                 return new Roo.CenterLayoutRegion(mgr, config);
59386         }
59387         throw 'Layout region "'+target+'" not supported.';
59388     }
59389 };/*
59390  * Based on:
59391  * Ext JS Library 1.1.1
59392  * Copyright(c) 2006-2007, Ext JS, LLC.
59393  *
59394  * Originally Released Under LGPL - original licence link has changed is not relivant.
59395  *
59396  * Fork - LGPL
59397  * <script type="text/javascript">
59398  */
59399  
59400 /**
59401  * @class Roo.BasicLayoutRegion
59402  * @extends Roo.util.Observable
59403  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
59404  * and does not have a titlebar, tabs or any other features. All it does is size and position 
59405  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
59406  */
59407 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
59408     this.mgr = mgr;
59409     this.position  = pos;
59410     this.events = {
59411         /**
59412          * @scope Roo.BasicLayoutRegion
59413          */
59414         
59415         /**
59416          * @event beforeremove
59417          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
59418          * @param {Roo.LayoutRegion} this
59419          * @param {Roo.ContentPanel} panel The panel
59420          * @param {Object} e The cancel event object
59421          */
59422         "beforeremove" : true,
59423         /**
59424          * @event invalidated
59425          * Fires when the layout for this region is changed.
59426          * @param {Roo.LayoutRegion} this
59427          */
59428         "invalidated" : true,
59429         /**
59430          * @event visibilitychange
59431          * Fires when this region is shown or hidden 
59432          * @param {Roo.LayoutRegion} this
59433          * @param {Boolean} visibility true or false
59434          */
59435         "visibilitychange" : true,
59436         /**
59437          * @event paneladded
59438          * Fires when a panel is added. 
59439          * @param {Roo.LayoutRegion} this
59440          * @param {Roo.ContentPanel} panel The panel
59441          */
59442         "paneladded" : true,
59443         /**
59444          * @event panelremoved
59445          * Fires when a panel is removed. 
59446          * @param {Roo.LayoutRegion} this
59447          * @param {Roo.ContentPanel} panel The panel
59448          */
59449         "panelremoved" : true,
59450         /**
59451          * @event beforecollapse
59452          * Fires when this region before collapse.
59453          * @param {Roo.LayoutRegion} this
59454          */
59455         "beforecollapse" : true,
59456         /**
59457          * @event collapsed
59458          * Fires when this region is collapsed.
59459          * @param {Roo.LayoutRegion} this
59460          */
59461         "collapsed" : true,
59462         /**
59463          * @event expanded
59464          * Fires when this region is expanded.
59465          * @param {Roo.LayoutRegion} this
59466          */
59467         "expanded" : true,
59468         /**
59469          * @event slideshow
59470          * Fires when this region is slid into view.
59471          * @param {Roo.LayoutRegion} this
59472          */
59473         "slideshow" : true,
59474         /**
59475          * @event slidehide
59476          * Fires when this region slides out of view. 
59477          * @param {Roo.LayoutRegion} this
59478          */
59479         "slidehide" : true,
59480         /**
59481          * @event panelactivated
59482          * Fires when a panel is activated. 
59483          * @param {Roo.LayoutRegion} this
59484          * @param {Roo.ContentPanel} panel The activated panel
59485          */
59486         "panelactivated" : true,
59487         /**
59488          * @event resized
59489          * Fires when the user resizes this region. 
59490          * @param {Roo.LayoutRegion} this
59491          * @param {Number} newSize The new size (width for east/west, height for north/south)
59492          */
59493         "resized" : true
59494     };
59495     /** A collection of panels in this region. @type Roo.util.MixedCollection */
59496     this.panels = new Roo.util.MixedCollection();
59497     this.panels.getKey = this.getPanelId.createDelegate(this);
59498     this.box = null;
59499     this.activePanel = null;
59500     // ensure listeners are added...
59501     
59502     if (config.listeners || config.events) {
59503         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
59504             listeners : config.listeners || {},
59505             events : config.events || {}
59506         });
59507     }
59508     
59509     if(skipConfig !== true){
59510         this.applyConfig(config);
59511     }
59512 };
59513
59514 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
59515     getPanelId : function(p){
59516         return p.getId();
59517     },
59518     
59519     applyConfig : function(config){
59520         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
59521         this.config = config;
59522         
59523     },
59524     
59525     /**
59526      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
59527      * the width, for horizontal (north, south) the height.
59528      * @param {Number} newSize The new width or height
59529      */
59530     resizeTo : function(newSize){
59531         var el = this.el ? this.el :
59532                  (this.activePanel ? this.activePanel.getEl() : null);
59533         if(el){
59534             switch(this.position){
59535                 case "east":
59536                 case "west":
59537                     el.setWidth(newSize);
59538                     this.fireEvent("resized", this, newSize);
59539                 break;
59540                 case "north":
59541                 case "south":
59542                     el.setHeight(newSize);
59543                     this.fireEvent("resized", this, newSize);
59544                 break;                
59545             }
59546         }
59547     },
59548     
59549     getBox : function(){
59550         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
59551     },
59552     
59553     getMargins : function(){
59554         return this.margins;
59555     },
59556     
59557     updateBox : function(box){
59558         this.box = box;
59559         var el = this.activePanel.getEl();
59560         el.dom.style.left = box.x + "px";
59561         el.dom.style.top = box.y + "px";
59562         this.activePanel.setSize(box.width, box.height);
59563     },
59564     
59565     /**
59566      * Returns the container element for this region.
59567      * @return {Roo.Element}
59568      */
59569     getEl : function(){
59570         return this.activePanel;
59571     },
59572     
59573     /**
59574      * Returns true if this region is currently visible.
59575      * @return {Boolean}
59576      */
59577     isVisible : function(){
59578         return this.activePanel ? true : false;
59579     },
59580     
59581     setActivePanel : function(panel){
59582         panel = this.getPanel(panel);
59583         if(this.activePanel && this.activePanel != panel){
59584             this.activePanel.setActiveState(false);
59585             this.activePanel.getEl().setLeftTop(-10000,-10000);
59586         }
59587         this.activePanel = panel;
59588         panel.setActiveState(true);
59589         if(this.box){
59590             panel.setSize(this.box.width, this.box.height);
59591         }
59592         this.fireEvent("panelactivated", this, panel);
59593         this.fireEvent("invalidated");
59594     },
59595     
59596     /**
59597      * Show the specified panel.
59598      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
59599      * @return {Roo.ContentPanel} The shown panel or null
59600      */
59601     showPanel : function(panel){
59602         if(panel = this.getPanel(panel)){
59603             this.setActivePanel(panel);
59604         }
59605         return panel;
59606     },
59607     
59608     /**
59609      * Get the active panel for this region.
59610      * @return {Roo.ContentPanel} The active panel or null
59611      */
59612     getActivePanel : function(){
59613         return this.activePanel;
59614     },
59615     
59616     /**
59617      * Add the passed ContentPanel(s)
59618      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
59619      * @return {Roo.ContentPanel} The panel added (if only one was added)
59620      */
59621     add : function(panel){
59622         if(arguments.length > 1){
59623             for(var i = 0, len = arguments.length; i < len; i++) {
59624                 this.add(arguments[i]);
59625             }
59626             return null;
59627         }
59628         if(this.hasPanel(panel)){
59629             this.showPanel(panel);
59630             return panel;
59631         }
59632         var el = panel.getEl();
59633         if(el.dom.parentNode != this.mgr.el.dom){
59634             this.mgr.el.dom.appendChild(el.dom);
59635         }
59636         if(panel.setRegion){
59637             panel.setRegion(this);
59638         }
59639         this.panels.add(panel);
59640         el.setStyle("position", "absolute");
59641         if(!panel.background){
59642             this.setActivePanel(panel);
59643             if(this.config.initialSize && this.panels.getCount()==1){
59644                 this.resizeTo(this.config.initialSize);
59645             }
59646         }
59647         this.fireEvent("paneladded", this, panel);
59648         return panel;
59649     },
59650     
59651     /**
59652      * Returns true if the panel is in this region.
59653      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
59654      * @return {Boolean}
59655      */
59656     hasPanel : function(panel){
59657         if(typeof panel == "object"){ // must be panel obj
59658             panel = panel.getId();
59659         }
59660         return this.getPanel(panel) ? true : false;
59661     },
59662     
59663     /**
59664      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
59665      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
59666      * @param {Boolean} preservePanel Overrides the config preservePanel option
59667      * @return {Roo.ContentPanel} The panel that was removed
59668      */
59669     remove : function(panel, preservePanel){
59670         panel = this.getPanel(panel);
59671         if(!panel){
59672             return null;
59673         }
59674         var e = {};
59675         this.fireEvent("beforeremove", this, panel, e);
59676         if(e.cancel === true){
59677             return null;
59678         }
59679         var panelId = panel.getId();
59680         this.panels.removeKey(panelId);
59681         return panel;
59682     },
59683     
59684     /**
59685      * Returns the panel specified or null if it's not in this region.
59686      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
59687      * @return {Roo.ContentPanel}
59688      */
59689     getPanel : function(id){
59690         if(typeof id == "object"){ // must be panel obj
59691             return id;
59692         }
59693         return this.panels.get(id);
59694     },
59695     
59696     /**
59697      * Returns this regions position (north/south/east/west/center).
59698      * @return {String} 
59699      */
59700     getPosition: function(){
59701         return this.position;    
59702     }
59703 });/*
59704  * Based on:
59705  * Ext JS Library 1.1.1
59706  * Copyright(c) 2006-2007, Ext JS, LLC.
59707  *
59708  * Originally Released Under LGPL - original licence link has changed is not relivant.
59709  *
59710  * Fork - LGPL
59711  * <script type="text/javascript">
59712  */
59713  
59714 /**
59715  * @class Roo.LayoutRegion
59716  * @extends Roo.BasicLayoutRegion
59717  * This class represents a region in a layout manager.
59718  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
59719  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
59720  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
59721  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
59722  * @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})
59723  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
59724  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
59725  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
59726  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
59727  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
59728  * @cfg {String}    title           The title for the region (overrides panel titles)
59729  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
59730  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
59731  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
59732  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
59733  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
59734  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
59735  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
59736  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
59737  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
59738  * @cfg {Boolean}   showPin         True to show a pin button
59739  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
59740  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
59741  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
59742  * @cfg {Number}    width           For East/West panels
59743  * @cfg {Number}    height          For North/South panels
59744  * @cfg {Boolean}   split           To show the splitter
59745  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
59746  */
59747 Roo.LayoutRegion = function(mgr, config, pos){
59748     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
59749     var dh = Roo.DomHelper;
59750     /** This region's container element 
59751     * @type Roo.Element */
59752     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
59753     /** This region's title element 
59754     * @type Roo.Element */
59755
59756     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
59757         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
59758         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
59759     ]}, true);
59760     this.titleEl.enableDisplayMode();
59761     /** This region's title text element 
59762     * @type HTMLElement */
59763     this.titleTextEl = this.titleEl.dom.firstChild;
59764     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
59765     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
59766     this.closeBtn.enableDisplayMode();
59767     this.closeBtn.on("click", this.closeClicked, this);
59768     this.closeBtn.hide();
59769
59770     this.createBody(config);
59771     this.visible = true;
59772     this.collapsed = false;
59773
59774     if(config.hideWhenEmpty){
59775         this.hide();
59776         this.on("paneladded", this.validateVisibility, this);
59777         this.on("panelremoved", this.validateVisibility, this);
59778     }
59779     this.applyConfig(config);
59780 };
59781
59782 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
59783
59784     createBody : function(){
59785         /** This region's body element 
59786         * @type Roo.Element */
59787         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
59788     },
59789
59790     applyConfig : function(c){
59791         if(c.collapsible && this.position != "center" && !this.collapsedEl){
59792             var dh = Roo.DomHelper;
59793             if(c.titlebar !== false){
59794                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
59795                 this.collapseBtn.on("click", this.collapse, this);
59796                 this.collapseBtn.enableDisplayMode();
59797
59798                 if(c.showPin === true || this.showPin){
59799                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
59800                     this.stickBtn.enableDisplayMode();
59801                     this.stickBtn.on("click", this.expand, this);
59802                     this.stickBtn.hide();
59803                 }
59804             }
59805             /** This region's collapsed element
59806             * @type Roo.Element */
59807             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
59808                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
59809             ]}, true);
59810             if(c.floatable !== false){
59811                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
59812                this.collapsedEl.on("click", this.collapseClick, this);
59813             }
59814
59815             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
59816                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
59817                    id: "message", unselectable: "on", style:{"float":"left"}});
59818                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
59819              }
59820             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
59821             this.expandBtn.on("click", this.expand, this);
59822         }
59823         if(this.collapseBtn){
59824             this.collapseBtn.setVisible(c.collapsible == true);
59825         }
59826         this.cmargins = c.cmargins || this.cmargins ||
59827                          (this.position == "west" || this.position == "east" ?
59828                              {top: 0, left: 2, right:2, bottom: 0} :
59829                              {top: 2, left: 0, right:0, bottom: 2});
59830         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
59831         this.bottomTabs = c.tabPosition != "top";
59832         this.autoScroll = c.autoScroll || false;
59833         if(this.autoScroll){
59834             this.bodyEl.setStyle("overflow", "auto");
59835         }else{
59836             this.bodyEl.setStyle("overflow", "hidden");
59837         }
59838         //if(c.titlebar !== false){
59839             if((!c.titlebar && !c.title) || c.titlebar === false){
59840                 this.titleEl.hide();
59841             }else{
59842                 this.titleEl.show();
59843                 if(c.title){
59844                     this.titleTextEl.innerHTML = c.title;
59845                 }
59846             }
59847         //}
59848         this.duration = c.duration || .30;
59849         this.slideDuration = c.slideDuration || .45;
59850         this.config = c;
59851         if(c.collapsed){
59852             this.collapse(true);
59853         }
59854         if(c.hidden){
59855             this.hide();
59856         }
59857     },
59858     /**
59859      * Returns true if this region is currently visible.
59860      * @return {Boolean}
59861      */
59862     isVisible : function(){
59863         return this.visible;
59864     },
59865
59866     /**
59867      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
59868      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
59869      */
59870     setCollapsedTitle : function(title){
59871         title = title || "&#160;";
59872         if(this.collapsedTitleTextEl){
59873             this.collapsedTitleTextEl.innerHTML = title;
59874         }
59875     },
59876
59877     getBox : function(){
59878         var b;
59879         if(!this.collapsed){
59880             b = this.el.getBox(false, true);
59881         }else{
59882             b = this.collapsedEl.getBox(false, true);
59883         }
59884         return b;
59885     },
59886
59887     getMargins : function(){
59888         return this.collapsed ? this.cmargins : this.margins;
59889     },
59890
59891     highlight : function(){
59892         this.el.addClass("x-layout-panel-dragover");
59893     },
59894
59895     unhighlight : function(){
59896         this.el.removeClass("x-layout-panel-dragover");
59897     },
59898
59899     updateBox : function(box){
59900         this.box = box;
59901         if(!this.collapsed){
59902             this.el.dom.style.left = box.x + "px";
59903             this.el.dom.style.top = box.y + "px";
59904             this.updateBody(box.width, box.height);
59905         }else{
59906             this.collapsedEl.dom.style.left = box.x + "px";
59907             this.collapsedEl.dom.style.top = box.y + "px";
59908             this.collapsedEl.setSize(box.width, box.height);
59909         }
59910         if(this.tabs){
59911             this.tabs.autoSizeTabs();
59912         }
59913     },
59914
59915     updateBody : function(w, h){
59916         if(w !== null){
59917             this.el.setWidth(w);
59918             w -= this.el.getBorderWidth("rl");
59919             if(this.config.adjustments){
59920                 w += this.config.adjustments[0];
59921             }
59922         }
59923         if(h !== null){
59924             this.el.setHeight(h);
59925             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
59926             h -= this.el.getBorderWidth("tb");
59927             if(this.config.adjustments){
59928                 h += this.config.adjustments[1];
59929             }
59930             this.bodyEl.setHeight(h);
59931             if(this.tabs){
59932                 h = this.tabs.syncHeight(h);
59933             }
59934         }
59935         if(this.panelSize){
59936             w = w !== null ? w : this.panelSize.width;
59937             h = h !== null ? h : this.panelSize.height;
59938         }
59939         if(this.activePanel){
59940             var el = this.activePanel.getEl();
59941             w = w !== null ? w : el.getWidth();
59942             h = h !== null ? h : el.getHeight();
59943             this.panelSize = {width: w, height: h};
59944             this.activePanel.setSize(w, h);
59945         }
59946         if(Roo.isIE && this.tabs){
59947             this.tabs.el.repaint();
59948         }
59949     },
59950
59951     /**
59952      * Returns the container element for this region.
59953      * @return {Roo.Element}
59954      */
59955     getEl : function(){
59956         return this.el;
59957     },
59958
59959     /**
59960      * Hides this region.
59961      */
59962     hide : function(){
59963         if(!this.collapsed){
59964             this.el.dom.style.left = "-2000px";
59965             this.el.hide();
59966         }else{
59967             this.collapsedEl.dom.style.left = "-2000px";
59968             this.collapsedEl.hide();
59969         }
59970         this.visible = false;
59971         this.fireEvent("visibilitychange", this, false);
59972     },
59973
59974     /**
59975      * Shows this region if it was previously hidden.
59976      */
59977     show : function(){
59978         if(!this.collapsed){
59979             this.el.show();
59980         }else{
59981             this.collapsedEl.show();
59982         }
59983         this.visible = true;
59984         this.fireEvent("visibilitychange", this, true);
59985     },
59986
59987     closeClicked : function(){
59988         if(this.activePanel){
59989             this.remove(this.activePanel);
59990         }
59991     },
59992
59993     collapseClick : function(e){
59994         if(this.isSlid){
59995            e.stopPropagation();
59996            this.slideIn();
59997         }else{
59998            e.stopPropagation();
59999            this.slideOut();
60000         }
60001     },
60002
60003     /**
60004      * Collapses this region.
60005      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
60006      */
60007     collapse : function(skipAnim, skipCheck){
60008         if(this.collapsed) {
60009             return;
60010         }
60011         
60012         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
60013             
60014             this.collapsed = true;
60015             if(this.split){
60016                 this.split.el.hide();
60017             }
60018             if(this.config.animate && skipAnim !== true){
60019                 this.fireEvent("invalidated", this);
60020                 this.animateCollapse();
60021             }else{
60022                 this.el.setLocation(-20000,-20000);
60023                 this.el.hide();
60024                 this.collapsedEl.show();
60025                 this.fireEvent("collapsed", this);
60026                 this.fireEvent("invalidated", this);
60027             }
60028         }
60029         
60030     },
60031
60032     animateCollapse : function(){
60033         // overridden
60034     },
60035
60036     /**
60037      * Expands this region if it was previously collapsed.
60038      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
60039      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
60040      */
60041     expand : function(e, skipAnim){
60042         if(e) {
60043             e.stopPropagation();
60044         }
60045         if(!this.collapsed || this.el.hasActiveFx()) {
60046             return;
60047         }
60048         if(this.isSlid){
60049             this.afterSlideIn();
60050             skipAnim = true;
60051         }
60052         this.collapsed = false;
60053         if(this.config.animate && skipAnim !== true){
60054             this.animateExpand();
60055         }else{
60056             this.el.show();
60057             if(this.split){
60058                 this.split.el.show();
60059             }
60060             this.collapsedEl.setLocation(-2000,-2000);
60061             this.collapsedEl.hide();
60062             this.fireEvent("invalidated", this);
60063             this.fireEvent("expanded", this);
60064         }
60065     },
60066
60067     animateExpand : function(){
60068         // overridden
60069     },
60070
60071     initTabs : function()
60072     {
60073         this.bodyEl.setStyle("overflow", "hidden");
60074         var ts = new Roo.TabPanel(
60075                 this.bodyEl.dom,
60076                 {
60077                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
60078                     disableTooltips: this.config.disableTabTips,
60079                     toolbar : this.config.toolbar
60080                 }
60081         );
60082         if(this.config.hideTabs){
60083             ts.stripWrap.setDisplayed(false);
60084         }
60085         this.tabs = ts;
60086         ts.resizeTabs = this.config.resizeTabs === true;
60087         ts.minTabWidth = this.config.minTabWidth || 40;
60088         ts.maxTabWidth = this.config.maxTabWidth || 250;
60089         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
60090         ts.monitorResize = false;
60091         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
60092         ts.bodyEl.addClass('x-layout-tabs-body');
60093         this.panels.each(this.initPanelAsTab, this);
60094     },
60095
60096     initPanelAsTab : function(panel){
60097         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
60098                     this.config.closeOnTab && panel.isClosable());
60099         if(panel.tabTip !== undefined){
60100             ti.setTooltip(panel.tabTip);
60101         }
60102         ti.on("activate", function(){
60103               this.setActivePanel(panel);
60104         }, this);
60105         if(this.config.closeOnTab){
60106             ti.on("beforeclose", function(t, e){
60107                 e.cancel = true;
60108                 this.remove(panel);
60109             }, this);
60110         }
60111         return ti;
60112     },
60113
60114     updatePanelTitle : function(panel, title){
60115         if(this.activePanel == panel){
60116             this.updateTitle(title);
60117         }
60118         if(this.tabs){
60119             var ti = this.tabs.getTab(panel.getEl().id);
60120             ti.setText(title);
60121             if(panel.tabTip !== undefined){
60122                 ti.setTooltip(panel.tabTip);
60123             }
60124         }
60125     },
60126
60127     updateTitle : function(title){
60128         if(this.titleTextEl && !this.config.title){
60129             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
60130         }
60131     },
60132
60133     setActivePanel : function(panel){
60134         panel = this.getPanel(panel);
60135         if(this.activePanel && this.activePanel != panel){
60136             this.activePanel.setActiveState(false);
60137         }
60138         this.activePanel = panel;
60139         panel.setActiveState(true);
60140         if(this.panelSize){
60141             panel.setSize(this.panelSize.width, this.panelSize.height);
60142         }
60143         if(this.closeBtn){
60144             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
60145         }
60146         this.updateTitle(panel.getTitle());
60147         if(this.tabs){
60148             this.fireEvent("invalidated", this);
60149         }
60150         this.fireEvent("panelactivated", this, panel);
60151     },
60152
60153     /**
60154      * Shows the specified panel.
60155      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
60156      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
60157      */
60158     showPanel : function(panel)
60159     {
60160         panel = this.getPanel(panel);
60161         if(panel){
60162             if(this.tabs){
60163                 var tab = this.tabs.getTab(panel.getEl().id);
60164                 if(tab.isHidden()){
60165                     this.tabs.unhideTab(tab.id);
60166                 }
60167                 tab.activate();
60168             }else{
60169                 this.setActivePanel(panel);
60170             }
60171         }
60172         return panel;
60173     },
60174
60175     /**
60176      * Get the active panel for this region.
60177      * @return {Roo.ContentPanel} The active panel or null
60178      */
60179     getActivePanel : function(){
60180         return this.activePanel;
60181     },
60182
60183     validateVisibility : function(){
60184         if(this.panels.getCount() < 1){
60185             this.updateTitle("&#160;");
60186             this.closeBtn.hide();
60187             this.hide();
60188         }else{
60189             if(!this.isVisible()){
60190                 this.show();
60191             }
60192         }
60193     },
60194
60195     /**
60196      * Adds the passed ContentPanel(s) to this region.
60197      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
60198      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
60199      */
60200     add : function(panel){
60201         if(arguments.length > 1){
60202             for(var i = 0, len = arguments.length; i < len; i++) {
60203                 this.add(arguments[i]);
60204             }
60205             return null;
60206         }
60207         if(this.hasPanel(panel)){
60208             this.showPanel(panel);
60209             return panel;
60210         }
60211         panel.setRegion(this);
60212         this.panels.add(panel);
60213         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
60214             this.bodyEl.dom.appendChild(panel.getEl().dom);
60215             if(panel.background !== true){
60216                 this.setActivePanel(panel);
60217             }
60218             this.fireEvent("paneladded", this, panel);
60219             return panel;
60220         }
60221         if(!this.tabs){
60222             this.initTabs();
60223         }else{
60224             this.initPanelAsTab(panel);
60225         }
60226         if(panel.background !== true){
60227             this.tabs.activate(panel.getEl().id);
60228         }
60229         this.fireEvent("paneladded", this, panel);
60230         return panel;
60231     },
60232
60233     /**
60234      * Hides the tab for the specified panel.
60235      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
60236      */
60237     hidePanel : function(panel){
60238         if(this.tabs && (panel = this.getPanel(panel))){
60239             this.tabs.hideTab(panel.getEl().id);
60240         }
60241     },
60242
60243     /**
60244      * Unhides the tab for a previously hidden panel.
60245      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
60246      */
60247     unhidePanel : function(panel){
60248         if(this.tabs && (panel = this.getPanel(panel))){
60249             this.tabs.unhideTab(panel.getEl().id);
60250         }
60251     },
60252
60253     clearPanels : function(){
60254         while(this.panels.getCount() > 0){
60255              this.remove(this.panels.first());
60256         }
60257     },
60258
60259     /**
60260      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
60261      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
60262      * @param {Boolean} preservePanel Overrides the config preservePanel option
60263      * @return {Roo.ContentPanel} The panel that was removed
60264      */
60265     remove : function(panel, preservePanel){
60266         panel = this.getPanel(panel);
60267         if(!panel){
60268             return null;
60269         }
60270         var e = {};
60271         this.fireEvent("beforeremove", this, panel, e);
60272         if(e.cancel === true){
60273             return null;
60274         }
60275         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
60276         var panelId = panel.getId();
60277         this.panels.removeKey(panelId);
60278         if(preservePanel){
60279             document.body.appendChild(panel.getEl().dom);
60280         }
60281         if(this.tabs){
60282             this.tabs.removeTab(panel.getEl().id);
60283         }else if (!preservePanel){
60284             this.bodyEl.dom.removeChild(panel.getEl().dom);
60285         }
60286         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
60287             var p = this.panels.first();
60288             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
60289             tempEl.appendChild(p.getEl().dom);
60290             this.bodyEl.update("");
60291             this.bodyEl.dom.appendChild(p.getEl().dom);
60292             tempEl = null;
60293             this.updateTitle(p.getTitle());
60294             this.tabs = null;
60295             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
60296             this.setActivePanel(p);
60297         }
60298         panel.setRegion(null);
60299         if(this.activePanel == panel){
60300             this.activePanel = null;
60301         }
60302         if(this.config.autoDestroy !== false && preservePanel !== true){
60303             try{panel.destroy();}catch(e){}
60304         }
60305         this.fireEvent("panelremoved", this, panel);
60306         return panel;
60307     },
60308
60309     /**
60310      * Returns the TabPanel component used by this region
60311      * @return {Roo.TabPanel}
60312      */
60313     getTabs : function(){
60314         return this.tabs;
60315     },
60316
60317     createTool : function(parentEl, className){
60318         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
60319             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
60320         btn.addClassOnOver("x-layout-tools-button-over");
60321         return btn;
60322     }
60323 });/*
60324  * Based on:
60325  * Ext JS Library 1.1.1
60326  * Copyright(c) 2006-2007, Ext JS, LLC.
60327  *
60328  * Originally Released Under LGPL - original licence link has changed is not relivant.
60329  *
60330  * Fork - LGPL
60331  * <script type="text/javascript">
60332  */
60333  
60334
60335
60336 /**
60337  * @class Roo.SplitLayoutRegion
60338  * @extends Roo.LayoutRegion
60339  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
60340  */
60341 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
60342     this.cursor = cursor;
60343     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
60344 };
60345
60346 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
60347     splitTip : "Drag to resize.",
60348     collapsibleSplitTip : "Drag to resize. Double click to hide.",
60349     useSplitTips : false,
60350
60351     applyConfig : function(config){
60352         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
60353         if(config.split){
60354             if(!this.split){
60355                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
60356                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
60357                 /** The SplitBar for this region 
60358                 * @type Roo.SplitBar */
60359                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
60360                 this.split.on("moved", this.onSplitMove, this);
60361                 this.split.useShim = config.useShim === true;
60362                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
60363                 if(this.useSplitTips){
60364                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
60365                 }
60366                 if(config.collapsible){
60367                     this.split.el.on("dblclick", this.collapse,  this);
60368                 }
60369             }
60370             if(typeof config.minSize != "undefined"){
60371                 this.split.minSize = config.minSize;
60372             }
60373             if(typeof config.maxSize != "undefined"){
60374                 this.split.maxSize = config.maxSize;
60375             }
60376             if(config.hideWhenEmpty || config.hidden || config.collapsed){
60377                 this.hideSplitter();
60378             }
60379         }
60380     },
60381
60382     getHMaxSize : function(){
60383          var cmax = this.config.maxSize || 10000;
60384          var center = this.mgr.getRegion("center");
60385          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
60386     },
60387
60388     getVMaxSize : function(){
60389          var cmax = this.config.maxSize || 10000;
60390          var center = this.mgr.getRegion("center");
60391          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
60392     },
60393
60394     onSplitMove : function(split, newSize){
60395         this.fireEvent("resized", this, newSize);
60396     },
60397     
60398     /** 
60399      * Returns the {@link Roo.SplitBar} for this region.
60400      * @return {Roo.SplitBar}
60401      */
60402     getSplitBar : function(){
60403         return this.split;
60404     },
60405     
60406     hide : function(){
60407         this.hideSplitter();
60408         Roo.SplitLayoutRegion.superclass.hide.call(this);
60409     },
60410
60411     hideSplitter : function(){
60412         if(this.split){
60413             this.split.el.setLocation(-2000,-2000);
60414             this.split.el.hide();
60415         }
60416     },
60417
60418     show : function(){
60419         if(this.split){
60420             this.split.el.show();
60421         }
60422         Roo.SplitLayoutRegion.superclass.show.call(this);
60423     },
60424     
60425     beforeSlide: function(){
60426         if(Roo.isGecko){// firefox overflow auto bug workaround
60427             this.bodyEl.clip();
60428             if(this.tabs) {
60429                 this.tabs.bodyEl.clip();
60430             }
60431             if(this.activePanel){
60432                 this.activePanel.getEl().clip();
60433                 
60434                 if(this.activePanel.beforeSlide){
60435                     this.activePanel.beforeSlide();
60436                 }
60437             }
60438         }
60439     },
60440     
60441     afterSlide : function(){
60442         if(Roo.isGecko){// firefox overflow auto bug workaround
60443             this.bodyEl.unclip();
60444             if(this.tabs) {
60445                 this.tabs.bodyEl.unclip();
60446             }
60447             if(this.activePanel){
60448                 this.activePanel.getEl().unclip();
60449                 if(this.activePanel.afterSlide){
60450                     this.activePanel.afterSlide();
60451                 }
60452             }
60453         }
60454     },
60455
60456     initAutoHide : function(){
60457         if(this.autoHide !== false){
60458             if(!this.autoHideHd){
60459                 var st = new Roo.util.DelayedTask(this.slideIn, this);
60460                 this.autoHideHd = {
60461                     "mouseout": function(e){
60462                         if(!e.within(this.el, true)){
60463                             st.delay(500);
60464                         }
60465                     },
60466                     "mouseover" : function(e){
60467                         st.cancel();
60468                     },
60469                     scope : this
60470                 };
60471             }
60472             this.el.on(this.autoHideHd);
60473         }
60474     },
60475
60476     clearAutoHide : function(){
60477         if(this.autoHide !== false){
60478             this.el.un("mouseout", this.autoHideHd.mouseout);
60479             this.el.un("mouseover", this.autoHideHd.mouseover);
60480         }
60481     },
60482
60483     clearMonitor : function(){
60484         Roo.get(document).un("click", this.slideInIf, this);
60485     },
60486
60487     // these names are backwards but not changed for compat
60488     slideOut : function(){
60489         if(this.isSlid || this.el.hasActiveFx()){
60490             return;
60491         }
60492         this.isSlid = true;
60493         if(this.collapseBtn){
60494             this.collapseBtn.hide();
60495         }
60496         this.closeBtnState = this.closeBtn.getStyle('display');
60497         this.closeBtn.hide();
60498         if(this.stickBtn){
60499             this.stickBtn.show();
60500         }
60501         this.el.show();
60502         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
60503         this.beforeSlide();
60504         this.el.setStyle("z-index", 10001);
60505         this.el.slideIn(this.getSlideAnchor(), {
60506             callback: function(){
60507                 this.afterSlide();
60508                 this.initAutoHide();
60509                 Roo.get(document).on("click", this.slideInIf, this);
60510                 this.fireEvent("slideshow", this);
60511             },
60512             scope: this,
60513             block: true
60514         });
60515     },
60516
60517     afterSlideIn : function(){
60518         this.clearAutoHide();
60519         this.isSlid = false;
60520         this.clearMonitor();
60521         this.el.setStyle("z-index", "");
60522         if(this.collapseBtn){
60523             this.collapseBtn.show();
60524         }
60525         this.closeBtn.setStyle('display', this.closeBtnState);
60526         if(this.stickBtn){
60527             this.stickBtn.hide();
60528         }
60529         this.fireEvent("slidehide", this);
60530     },
60531
60532     slideIn : function(cb){
60533         if(!this.isSlid || this.el.hasActiveFx()){
60534             Roo.callback(cb);
60535             return;
60536         }
60537         this.isSlid = false;
60538         this.beforeSlide();
60539         this.el.slideOut(this.getSlideAnchor(), {
60540             callback: function(){
60541                 this.el.setLeftTop(-10000, -10000);
60542                 this.afterSlide();
60543                 this.afterSlideIn();
60544                 Roo.callback(cb);
60545             },
60546             scope: this,
60547             block: true
60548         });
60549     },
60550     
60551     slideInIf : function(e){
60552         if(!e.within(this.el)){
60553             this.slideIn();
60554         }
60555     },
60556
60557     animateCollapse : function(){
60558         this.beforeSlide();
60559         this.el.setStyle("z-index", 20000);
60560         var anchor = this.getSlideAnchor();
60561         this.el.slideOut(anchor, {
60562             callback : function(){
60563                 this.el.setStyle("z-index", "");
60564                 this.collapsedEl.slideIn(anchor, {duration:.3});
60565                 this.afterSlide();
60566                 this.el.setLocation(-10000,-10000);
60567                 this.el.hide();
60568                 this.fireEvent("collapsed", this);
60569             },
60570             scope: this,
60571             block: true
60572         });
60573     },
60574
60575     animateExpand : function(){
60576         this.beforeSlide();
60577         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
60578         this.el.setStyle("z-index", 20000);
60579         this.collapsedEl.hide({
60580             duration:.1
60581         });
60582         this.el.slideIn(this.getSlideAnchor(), {
60583             callback : function(){
60584                 this.el.setStyle("z-index", "");
60585                 this.afterSlide();
60586                 if(this.split){
60587                     this.split.el.show();
60588                 }
60589                 this.fireEvent("invalidated", this);
60590                 this.fireEvent("expanded", this);
60591             },
60592             scope: this,
60593             block: true
60594         });
60595     },
60596
60597     anchors : {
60598         "west" : "left",
60599         "east" : "right",
60600         "north" : "top",
60601         "south" : "bottom"
60602     },
60603
60604     sanchors : {
60605         "west" : "l",
60606         "east" : "r",
60607         "north" : "t",
60608         "south" : "b"
60609     },
60610
60611     canchors : {
60612         "west" : "tl-tr",
60613         "east" : "tr-tl",
60614         "north" : "tl-bl",
60615         "south" : "bl-tl"
60616     },
60617
60618     getAnchor : function(){
60619         return this.anchors[this.position];
60620     },
60621
60622     getCollapseAnchor : function(){
60623         return this.canchors[this.position];
60624     },
60625
60626     getSlideAnchor : function(){
60627         return this.sanchors[this.position];
60628     },
60629
60630     getAlignAdj : function(){
60631         var cm = this.cmargins;
60632         switch(this.position){
60633             case "west":
60634                 return [0, 0];
60635             break;
60636             case "east":
60637                 return [0, 0];
60638             break;
60639             case "north":
60640                 return [0, 0];
60641             break;
60642             case "south":
60643                 return [0, 0];
60644             break;
60645         }
60646     },
60647
60648     getExpandAdj : function(){
60649         var c = this.collapsedEl, cm = this.cmargins;
60650         switch(this.position){
60651             case "west":
60652                 return [-(cm.right+c.getWidth()+cm.left), 0];
60653             break;
60654             case "east":
60655                 return [cm.right+c.getWidth()+cm.left, 0];
60656             break;
60657             case "north":
60658                 return [0, -(cm.top+cm.bottom+c.getHeight())];
60659             break;
60660             case "south":
60661                 return [0, cm.top+cm.bottom+c.getHeight()];
60662             break;
60663         }
60664     }
60665 });/*
60666  * Based on:
60667  * Ext JS Library 1.1.1
60668  * Copyright(c) 2006-2007, Ext JS, LLC.
60669  *
60670  * Originally Released Under LGPL - original licence link has changed is not relivant.
60671  *
60672  * Fork - LGPL
60673  * <script type="text/javascript">
60674  */
60675 /*
60676  * These classes are private internal classes
60677  */
60678 Roo.CenterLayoutRegion = function(mgr, config){
60679     Roo.LayoutRegion.call(this, mgr, config, "center");
60680     this.visible = true;
60681     this.minWidth = config.minWidth || 20;
60682     this.minHeight = config.minHeight || 20;
60683 };
60684
60685 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
60686     hide : function(){
60687         // center panel can't be hidden
60688     },
60689     
60690     show : function(){
60691         // center panel can't be hidden
60692     },
60693     
60694     getMinWidth: function(){
60695         return this.minWidth;
60696     },
60697     
60698     getMinHeight: function(){
60699         return this.minHeight;
60700     }
60701 });
60702
60703
60704 Roo.NorthLayoutRegion = function(mgr, config){
60705     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
60706     if(this.split){
60707         this.split.placement = Roo.SplitBar.TOP;
60708         this.split.orientation = Roo.SplitBar.VERTICAL;
60709         this.split.el.addClass("x-layout-split-v");
60710     }
60711     var size = config.initialSize || config.height;
60712     if(typeof size != "undefined"){
60713         this.el.setHeight(size);
60714     }
60715 };
60716 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
60717     orientation: Roo.SplitBar.VERTICAL,
60718     getBox : function(){
60719         if(this.collapsed){
60720             return this.collapsedEl.getBox();
60721         }
60722         var box = this.el.getBox();
60723         if(this.split){
60724             box.height += this.split.el.getHeight();
60725         }
60726         return box;
60727     },
60728     
60729     updateBox : function(box){
60730         if(this.split && !this.collapsed){
60731             box.height -= this.split.el.getHeight();
60732             this.split.el.setLeft(box.x);
60733             this.split.el.setTop(box.y+box.height);
60734             this.split.el.setWidth(box.width);
60735         }
60736         if(this.collapsed){
60737             this.updateBody(box.width, null);
60738         }
60739         Roo.LayoutRegion.prototype.updateBox.call(this, box);
60740     }
60741 });
60742
60743 Roo.SouthLayoutRegion = function(mgr, config){
60744     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
60745     if(this.split){
60746         this.split.placement = Roo.SplitBar.BOTTOM;
60747         this.split.orientation = Roo.SplitBar.VERTICAL;
60748         this.split.el.addClass("x-layout-split-v");
60749     }
60750     var size = config.initialSize || config.height;
60751     if(typeof size != "undefined"){
60752         this.el.setHeight(size);
60753     }
60754 };
60755 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
60756     orientation: Roo.SplitBar.VERTICAL,
60757     getBox : function(){
60758         if(this.collapsed){
60759             return this.collapsedEl.getBox();
60760         }
60761         var box = this.el.getBox();
60762         if(this.split){
60763             var sh = this.split.el.getHeight();
60764             box.height += sh;
60765             box.y -= sh;
60766         }
60767         return box;
60768     },
60769     
60770     updateBox : function(box){
60771         if(this.split && !this.collapsed){
60772             var sh = this.split.el.getHeight();
60773             box.height -= sh;
60774             box.y += sh;
60775             this.split.el.setLeft(box.x);
60776             this.split.el.setTop(box.y-sh);
60777             this.split.el.setWidth(box.width);
60778         }
60779         if(this.collapsed){
60780             this.updateBody(box.width, null);
60781         }
60782         Roo.LayoutRegion.prototype.updateBox.call(this, box);
60783     }
60784 });
60785
60786 Roo.EastLayoutRegion = function(mgr, config){
60787     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
60788     if(this.split){
60789         this.split.placement = Roo.SplitBar.RIGHT;
60790         this.split.orientation = Roo.SplitBar.HORIZONTAL;
60791         this.split.el.addClass("x-layout-split-h");
60792     }
60793     var size = config.initialSize || config.width;
60794     if(typeof size != "undefined"){
60795         this.el.setWidth(size);
60796     }
60797 };
60798 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
60799     orientation: Roo.SplitBar.HORIZONTAL,
60800     getBox : function(){
60801         if(this.collapsed){
60802             return this.collapsedEl.getBox();
60803         }
60804         var box = this.el.getBox();
60805         if(this.split){
60806             var sw = this.split.el.getWidth();
60807             box.width += sw;
60808             box.x -= sw;
60809         }
60810         return box;
60811     },
60812
60813     updateBox : function(box){
60814         if(this.split && !this.collapsed){
60815             var sw = this.split.el.getWidth();
60816             box.width -= sw;
60817             this.split.el.setLeft(box.x);
60818             this.split.el.setTop(box.y);
60819             this.split.el.setHeight(box.height);
60820             box.x += sw;
60821         }
60822         if(this.collapsed){
60823             this.updateBody(null, box.height);
60824         }
60825         Roo.LayoutRegion.prototype.updateBox.call(this, box);
60826     }
60827 });
60828
60829 Roo.WestLayoutRegion = function(mgr, config){
60830     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
60831     if(this.split){
60832         this.split.placement = Roo.SplitBar.LEFT;
60833         this.split.orientation = Roo.SplitBar.HORIZONTAL;
60834         this.split.el.addClass("x-layout-split-h");
60835     }
60836     var size = config.initialSize || config.width;
60837     if(typeof size != "undefined"){
60838         this.el.setWidth(size);
60839     }
60840 };
60841 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
60842     orientation: Roo.SplitBar.HORIZONTAL,
60843     getBox : function(){
60844         if(this.collapsed){
60845             return this.collapsedEl.getBox();
60846         }
60847         var box = this.el.getBox();
60848         if(this.split){
60849             box.width += this.split.el.getWidth();
60850         }
60851         return box;
60852     },
60853     
60854     updateBox : function(box){
60855         if(this.split && !this.collapsed){
60856             var sw = this.split.el.getWidth();
60857             box.width -= sw;
60858             this.split.el.setLeft(box.x+box.width);
60859             this.split.el.setTop(box.y);
60860             this.split.el.setHeight(box.height);
60861         }
60862         if(this.collapsed){
60863             this.updateBody(null, box.height);
60864         }
60865         Roo.LayoutRegion.prototype.updateBox.call(this, box);
60866     }
60867 });
60868 /*
60869  * Based on:
60870  * Ext JS Library 1.1.1
60871  * Copyright(c) 2006-2007, Ext JS, LLC.
60872  *
60873  * Originally Released Under LGPL - original licence link has changed is not relivant.
60874  *
60875  * Fork - LGPL
60876  * <script type="text/javascript">
60877  */
60878  
60879  
60880 /*
60881  * Private internal class for reading and applying state
60882  */
60883 Roo.LayoutStateManager = function(layout){
60884      // default empty state
60885      this.state = {
60886         north: {},
60887         south: {},
60888         east: {},
60889         west: {}       
60890     };
60891 };
60892
60893 Roo.LayoutStateManager.prototype = {
60894     init : function(layout, provider){
60895         this.provider = provider;
60896         var state = provider.get(layout.id+"-layout-state");
60897         if(state){
60898             var wasUpdating = layout.isUpdating();
60899             if(!wasUpdating){
60900                 layout.beginUpdate();
60901             }
60902             for(var key in state){
60903                 if(typeof state[key] != "function"){
60904                     var rstate = state[key];
60905                     var r = layout.getRegion(key);
60906                     if(r && rstate){
60907                         if(rstate.size){
60908                             r.resizeTo(rstate.size);
60909                         }
60910                         if(rstate.collapsed == true){
60911                             r.collapse(true);
60912                         }else{
60913                             r.expand(null, true);
60914                         }
60915                     }
60916                 }
60917             }
60918             if(!wasUpdating){
60919                 layout.endUpdate();
60920             }
60921             this.state = state; 
60922         }
60923         this.layout = layout;
60924         layout.on("regionresized", this.onRegionResized, this);
60925         layout.on("regioncollapsed", this.onRegionCollapsed, this);
60926         layout.on("regionexpanded", this.onRegionExpanded, this);
60927     },
60928     
60929     storeState : function(){
60930         this.provider.set(this.layout.id+"-layout-state", this.state);
60931     },
60932     
60933     onRegionResized : function(region, newSize){
60934         this.state[region.getPosition()].size = newSize;
60935         this.storeState();
60936     },
60937     
60938     onRegionCollapsed : function(region){
60939         this.state[region.getPosition()].collapsed = true;
60940         this.storeState();
60941     },
60942     
60943     onRegionExpanded : function(region){
60944         this.state[region.getPosition()].collapsed = false;
60945         this.storeState();
60946     }
60947 };/*
60948  * Based on:
60949  * Ext JS Library 1.1.1
60950  * Copyright(c) 2006-2007, Ext JS, LLC.
60951  *
60952  * Originally Released Under LGPL - original licence link has changed is not relivant.
60953  *
60954  * Fork - LGPL
60955  * <script type="text/javascript">
60956  */
60957 /**
60958  * @class Roo.ContentPanel
60959  * @extends Roo.util.Observable
60960  * @children Roo.form.Form Roo.JsonView Roo.View
60961  * @parent Roo.BorderLayout Roo.LayoutDialog builder
60962  * A basic ContentPanel element.
60963  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
60964  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
60965  * @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
60966  * @cfg {Boolean}   closable      True if the panel can be closed/removed
60967  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
60968  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
60969  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
60970  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
60971  * @cfg {String} title          The title for this panel
60972  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
60973  * @cfg {String} url            Calls {@link #setUrl} with this value
60974  * @cfg {String} region (center|north|south|east|west) [required] which region to put this panel on (when used with xtype constructors)
60975  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
60976  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
60977  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
60978  * @cfg {String}    style  Extra style to add to the content panel
60979  * @cfg {Roo.menu.Menu} menu  popup menu
60980
60981  * @constructor
60982  * Create a new ContentPanel.
60983  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
60984  * @param {String/Object} config A string to set only the title or a config object
60985  * @param {String} content (optional) Set the HTML content for this panel
60986  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
60987  */
60988 Roo.ContentPanel = function(el, config, content){
60989     
60990     /*
60991     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
60992         config = el;
60993         el = Roo.id();
60994     }
60995     if (config && config.parentLayout) { 
60996         el = config.parentLayout.el.createChild(); 
60997     }
60998     */
60999     if(el.autoCreate){ // xtype is available if this is called from factory
61000         config = el;
61001         el = Roo.id();
61002     }
61003     this.el = Roo.get(el);
61004     if(!this.el && config && config.autoCreate){
61005         if(typeof config.autoCreate == "object"){
61006             if(!config.autoCreate.id){
61007                 config.autoCreate.id = config.id||el;
61008             }
61009             this.el = Roo.DomHelper.append(document.body,
61010                         config.autoCreate, true);
61011         }else{
61012             this.el = Roo.DomHelper.append(document.body,
61013                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
61014         }
61015     }
61016     
61017     
61018     this.closable = false;
61019     this.loaded = false;
61020     this.active = false;
61021     if(typeof config == "string"){
61022         this.title = config;
61023     }else{
61024         Roo.apply(this, config);
61025     }
61026     
61027     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
61028         this.wrapEl = this.el.wrap();
61029         this.toolbar.container = this.el.insertSibling(false, 'before');
61030         this.toolbar = new Roo.Toolbar(this.toolbar);
61031     }
61032     
61033     // xtype created footer. - not sure if will work as we normally have to render first..
61034     if (this.footer && !this.footer.el && this.footer.xtype) {
61035         if (!this.wrapEl) {
61036             this.wrapEl = this.el.wrap();
61037         }
61038     
61039         this.footer.container = this.wrapEl.createChild();
61040          
61041         this.footer = Roo.factory(this.footer, Roo);
61042         
61043     }
61044     
61045     if(this.resizeEl){
61046         this.resizeEl = Roo.get(this.resizeEl, true);
61047     }else{
61048         this.resizeEl = this.el;
61049     }
61050     // handle view.xtype
61051     
61052  
61053     
61054     
61055     this.addEvents({
61056         /**
61057          * @event activate
61058          * Fires when this panel is activated. 
61059          * @param {Roo.ContentPanel} this
61060          */
61061         "activate" : true,
61062         /**
61063          * @event deactivate
61064          * Fires when this panel is activated. 
61065          * @param {Roo.ContentPanel} this
61066          */
61067         "deactivate" : true,
61068
61069         /**
61070          * @event resize
61071          * Fires when this panel is resized if fitToFrame is true.
61072          * @param {Roo.ContentPanel} this
61073          * @param {Number} width The width after any component adjustments
61074          * @param {Number} height The height after any component adjustments
61075          */
61076         "resize" : true,
61077         
61078          /**
61079          * @event render
61080          * Fires when this tab is created
61081          * @param {Roo.ContentPanel} this
61082          */
61083         "render" : true
61084          
61085         
61086     });
61087     
61088
61089     
61090     
61091     if(this.autoScroll){
61092         this.resizeEl.setStyle("overflow", "auto");
61093     } else {
61094         // fix randome scrolling
61095         this.el.on('scroll', function() {
61096             Roo.log('fix random scolling');
61097             this.scrollTo('top',0); 
61098         });
61099     }
61100     content = content || this.content;
61101     if(content){
61102         this.setContent(content);
61103     }
61104     if(config && config.url){
61105         this.setUrl(this.url, this.params, this.loadOnce);
61106     }
61107     
61108     
61109     
61110     Roo.ContentPanel.superclass.constructor.call(this);
61111     
61112     if (this.view && typeof(this.view.xtype) != 'undefined') {
61113         this.view.el = this.el.appendChild(document.createElement("div"));
61114         this.view = Roo.factory(this.view); 
61115         this.view.render  &&  this.view.render(false, '');  
61116     }
61117     
61118     
61119     this.fireEvent('render', this);
61120 };
61121
61122 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
61123     tabTip:'',
61124     setRegion : function(region){
61125         this.region = region;
61126         if(region){
61127            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
61128         }else{
61129            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
61130         } 
61131     },
61132     
61133     /**
61134      * Returns the toolbar for this Panel if one was configured. 
61135      * @return {Roo.Toolbar} 
61136      */
61137     getToolbar : function(){
61138         return this.toolbar;
61139     },
61140     
61141     setActiveState : function(active){
61142         this.active = active;
61143         if(!active){
61144             this.fireEvent("deactivate", this);
61145         }else{
61146             this.fireEvent("activate", this);
61147         }
61148     },
61149     /**
61150      * Updates this panel's element
61151      * @param {String} content The new content
61152      * @param {Boolean} loadScripts (optional) true to look for and process scripts
61153     */
61154     setContent : function(content, loadScripts){
61155         this.el.update(content, loadScripts);
61156     },
61157
61158     ignoreResize : function(w, h){
61159         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
61160             return true;
61161         }else{
61162             this.lastSize = {width: w, height: h};
61163             return false;
61164         }
61165     },
61166     /**
61167      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
61168      * @return {Roo.UpdateManager} The UpdateManager
61169      */
61170     getUpdateManager : function(){
61171         return this.el.getUpdateManager();
61172     },
61173      /**
61174      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
61175      * @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:
61176 <pre><code>
61177 panel.load({
61178     url: "your-url.php",
61179     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
61180     callback: yourFunction,
61181     scope: yourObject, //(optional scope)
61182     discardUrl: false,
61183     nocache: false,
61184     text: "Loading...",
61185     timeout: 30,
61186     scripts: false
61187 });
61188 </code></pre>
61189      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
61190      * 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.
61191      * @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}
61192      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
61193      * @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.
61194      * @return {Roo.ContentPanel} this
61195      */
61196     load : function(){
61197         var um = this.el.getUpdateManager();
61198         um.update.apply(um, arguments);
61199         return this;
61200     },
61201
61202
61203     /**
61204      * 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.
61205      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
61206      * @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)
61207      * @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)
61208      * @return {Roo.UpdateManager} The UpdateManager
61209      */
61210     setUrl : function(url, params, loadOnce){
61211         if(this.refreshDelegate){
61212             this.removeListener("activate", this.refreshDelegate);
61213         }
61214         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
61215         this.on("activate", this.refreshDelegate);
61216         return this.el.getUpdateManager();
61217     },
61218     
61219     _handleRefresh : function(url, params, loadOnce){
61220         if(!loadOnce || !this.loaded){
61221             var updater = this.el.getUpdateManager();
61222             updater.update(url, params, this._setLoaded.createDelegate(this));
61223         }
61224     },
61225     
61226     _setLoaded : function(){
61227         this.loaded = true;
61228     }, 
61229     
61230     /**
61231      * Returns this panel's id
61232      * @return {String} 
61233      */
61234     getId : function(){
61235         return this.el.id;
61236     },
61237     
61238     /** 
61239      * Returns this panel's element - used by regiosn to add.
61240      * @return {Roo.Element} 
61241      */
61242     getEl : function(){
61243         return this.wrapEl || this.el;
61244     },
61245     
61246     adjustForComponents : function(width, height)
61247     {
61248         //Roo.log('adjustForComponents ');
61249         if(this.resizeEl != this.el){
61250             width -= this.el.getFrameWidth('lr');
61251             height -= this.el.getFrameWidth('tb');
61252         }
61253         if(this.toolbar){
61254             var te = this.toolbar.getEl();
61255             height -= te.getHeight();
61256             te.setWidth(width);
61257         }
61258         if(this.footer){
61259             var te = this.footer.getEl();
61260             //Roo.log("footer:" + te.getHeight());
61261             
61262             height -= te.getHeight();
61263             te.setWidth(width);
61264         }
61265         
61266         
61267         if(this.adjustments){
61268             width += this.adjustments[0];
61269             height += this.adjustments[1];
61270         }
61271         return {"width": width, "height": height};
61272     },
61273     
61274     setSize : function(width, height){
61275         if(this.fitToFrame && !this.ignoreResize(width, height)){
61276             if(this.fitContainer && this.resizeEl != this.el){
61277                 this.el.setSize(width, height);
61278             }
61279             var size = this.adjustForComponents(width, height);
61280             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
61281             this.fireEvent('resize', this, size.width, size.height);
61282         }
61283     },
61284     
61285     /**
61286      * Returns this panel's title
61287      * @return {String} 
61288      */
61289     getTitle : function(){
61290         return this.title;
61291     },
61292     
61293     /**
61294      * Set this panel's title
61295      * @param {String} title
61296      */
61297     setTitle : function(title){
61298         this.title = title;
61299         if(this.region){
61300             this.region.updatePanelTitle(this, title);
61301         }
61302     },
61303     
61304     /**
61305      * Returns true is this panel was configured to be closable
61306      * @return {Boolean} 
61307      */
61308     isClosable : function(){
61309         return this.closable;
61310     },
61311     
61312     beforeSlide : function(){
61313         this.el.clip();
61314         this.resizeEl.clip();
61315     },
61316     
61317     afterSlide : function(){
61318         this.el.unclip();
61319         this.resizeEl.unclip();
61320     },
61321     
61322     /**
61323      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
61324      *   Will fail silently if the {@link #setUrl} method has not been called.
61325      *   This does not activate the panel, just updates its content.
61326      */
61327     refresh : function(){
61328         if(this.refreshDelegate){
61329            this.loaded = false;
61330            this.refreshDelegate();
61331         }
61332     },
61333     
61334     /**
61335      * Destroys this panel
61336      */
61337     destroy : function(){
61338         this.el.removeAllListeners();
61339         var tempEl = document.createElement("span");
61340         tempEl.appendChild(this.el.dom);
61341         tempEl.innerHTML = "";
61342         this.el.remove();
61343         this.el = null;
61344     },
61345     
61346     /**
61347      * form - if the content panel contains a form - this is a reference to it.
61348      * @type {Roo.form.Form}
61349      */
61350     form : false,
61351     /**
61352      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
61353      *    This contains a reference to it.
61354      * @type {Roo.View}
61355      */
61356     view : false,
61357     
61358       /**
61359      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
61360      * <pre><code>
61361
61362 layout.addxtype({
61363        xtype : 'Form',
61364        items: [ .... ]
61365    }
61366 );
61367
61368 </code></pre>
61369      * @param {Object} cfg Xtype definition of item to add.
61370      */
61371     
61372     addxtype : function(cfg) {
61373         if(cfg.xtype.match(/^UploadCropbox$/)) {
61374
61375             this.cropbox = new Roo.factory(cfg);
61376
61377             this.cropbox.render(this.el);
61378
61379             return this.cropbox;
61380         }
61381         // add form..
61382         if (cfg.xtype.match(/^Form$/)) {
61383             
61384             var el;
61385             //if (this.footer) {
61386             //    el = this.footer.container.insertSibling(false, 'before');
61387             //} else {
61388                 el = this.el.createChild();
61389             //}
61390
61391             this.form = new  Roo.form.Form(cfg);
61392             
61393             
61394             if ( this.form.allItems.length) {
61395                 this.form.render(el.dom);
61396             }
61397             return this.form;
61398         }
61399         // should only have one of theses..
61400         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
61401             // views.. should not be just added - used named prop 'view''
61402             
61403             cfg.el = this.el.appendChild(document.createElement("div"));
61404             // factory?
61405             
61406             var ret = new Roo.factory(cfg);
61407              
61408              ret.render && ret.render(false, ''); // render blank..
61409             this.view = ret;
61410             return ret;
61411         }
61412         return false;
61413     }
61414 });
61415
61416
61417
61418
61419
61420
61421
61422
61423
61424
61425
61426
61427 /**
61428  * @class Roo.GridPanel
61429  * @extends Roo.ContentPanel
61430  * @parent Roo.BorderLayout Roo.LayoutDialog builder
61431  * @constructor
61432  * Create a new GridPanel.
61433  * @cfg {Roo.grid.Grid} grid The grid for this panel
61434  */
61435 Roo.GridPanel = function(grid, config){
61436     
61437     // universal ctor...
61438     if (typeof(grid.grid) != 'undefined') {
61439         config = grid;
61440         grid = config.grid;
61441     }
61442     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
61443         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
61444         
61445     this.wrapper.dom.appendChild(grid.getGridEl().dom);
61446     
61447     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
61448     
61449     if(this.toolbar){
61450         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
61451     }
61452     // xtype created footer. - not sure if will work as we normally have to render first..
61453     if (this.footer && !this.footer.el && this.footer.xtype) {
61454         
61455         this.footer.container = this.grid.getView().getFooterPanel(true);
61456         this.footer.dataSource = this.grid.dataSource;
61457         this.footer = Roo.factory(this.footer, Roo);
61458         
61459     }
61460     
61461     grid.monitorWindowResize = false; // turn off autosizing
61462     grid.autoHeight = false;
61463     grid.autoWidth = false;
61464     this.grid = grid;
61465     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
61466 };
61467
61468 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
61469     getId : function(){
61470         return this.grid.id;
61471     },
61472     
61473     /**
61474      * Returns the grid for this panel
61475      * @return {Roo.grid.Grid} 
61476      */
61477     getGrid : function(){
61478         return this.grid;    
61479     },
61480     
61481     setSize : function(width, height){
61482         if(!this.ignoreResize(width, height)){
61483             var grid = this.grid;
61484             var size = this.adjustForComponents(width, height);
61485             grid.getGridEl().setSize(size.width, size.height);
61486             grid.autoSize();
61487         }
61488     },
61489     
61490     beforeSlide : function(){
61491         this.grid.getView().scroller.clip();
61492     },
61493     
61494     afterSlide : function(){
61495         this.grid.getView().scroller.unclip();
61496     },
61497     
61498     destroy : function(){
61499         this.grid.destroy();
61500         delete this.grid;
61501         Roo.GridPanel.superclass.destroy.call(this); 
61502     }
61503 });
61504
61505
61506 /**
61507  * @class Roo.NestedLayoutPanel
61508  * @extends Roo.ContentPanel
61509  * @parent Roo.BorderLayout Roo.LayoutDialog builder
61510  * @cfg {Roo.BorderLayout} layout   [required] The layout for this panel
61511  *
61512  * 
61513  * @constructor
61514  * Create a new NestedLayoutPanel.
61515  * 
61516  * 
61517  * @param {Roo.BorderLayout} layout [required] The layout for this panel
61518  * @param {String/Object} config A string to set only the title or a config object
61519  */
61520 Roo.NestedLayoutPanel = function(layout, config)
61521 {
61522     // construct with only one argument..
61523     /* FIXME - implement nicer consturctors
61524     if (layout.layout) {
61525         config = layout;
61526         layout = config.layout;
61527         delete config.layout;
61528     }
61529     if (layout.xtype && !layout.getEl) {
61530         // then layout needs constructing..
61531         layout = Roo.factory(layout, Roo);
61532     }
61533     */
61534     
61535     
61536     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
61537     
61538     layout.monitorWindowResize = false; // turn off autosizing
61539     this.layout = layout;
61540     this.layout.getEl().addClass("x-layout-nested-layout");
61541     
61542     
61543     
61544     
61545 };
61546
61547 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
61548
61549     layout : false,
61550
61551     setSize : function(width, height){
61552         if(!this.ignoreResize(width, height)){
61553             var size = this.adjustForComponents(width, height);
61554             var el = this.layout.getEl();
61555             el.setSize(size.width, size.height);
61556             var touch = el.dom.offsetWidth;
61557             this.layout.layout();
61558             // ie requires a double layout on the first pass
61559             if(Roo.isIE && !this.initialized){
61560                 this.initialized = true;
61561                 this.layout.layout();
61562             }
61563         }
61564     },
61565     
61566     // activate all subpanels if not currently active..
61567     
61568     setActiveState : function(active){
61569         this.active = active;
61570         if(!active){
61571             this.fireEvent("deactivate", this);
61572             return;
61573         }
61574         
61575         this.fireEvent("activate", this);
61576         // not sure if this should happen before or after..
61577         if (!this.layout) {
61578             return; // should not happen..
61579         }
61580         var reg = false;
61581         for (var r in this.layout.regions) {
61582             reg = this.layout.getRegion(r);
61583             if (reg.getActivePanel()) {
61584                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
61585                 reg.setActivePanel(reg.getActivePanel());
61586                 continue;
61587             }
61588             if (!reg.panels.length) {
61589                 continue;
61590             }
61591             reg.showPanel(reg.getPanel(0));
61592         }
61593         
61594         
61595         
61596         
61597     },
61598     
61599     /**
61600      * Returns the nested BorderLayout for this panel
61601      * @return {Roo.BorderLayout}
61602      */
61603     getLayout : function(){
61604         return this.layout;
61605     },
61606     
61607      /**
61608      * Adds a xtype elements to the layout of the nested panel
61609      * <pre><code>
61610
61611 panel.addxtype({
61612        xtype : 'ContentPanel',
61613        region: 'west',
61614        items: [ .... ]
61615    }
61616 );
61617
61618 panel.addxtype({
61619         xtype : 'NestedLayoutPanel',
61620         region: 'west',
61621         layout: {
61622            center: { },
61623            west: { }   
61624         },
61625         items : [ ... list of content panels or nested layout panels.. ]
61626    }
61627 );
61628 </code></pre>
61629      * @param {Object} cfg Xtype definition of item to add.
61630      */
61631     addxtype : function(cfg) {
61632         return this.layout.addxtype(cfg);
61633     
61634     }
61635 });
61636
61637 Roo.ScrollPanel = function(el, config, content){
61638     config = config || {};
61639     config.fitToFrame = true;
61640     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
61641     
61642     this.el.dom.style.overflow = "hidden";
61643     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
61644     this.el.removeClass("x-layout-inactive-content");
61645     this.el.on("mousewheel", this.onWheel, this);
61646
61647     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
61648     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
61649     up.unselectable(); down.unselectable();
61650     up.on("click", this.scrollUp, this);
61651     down.on("click", this.scrollDown, this);
61652     up.addClassOnOver("x-scroller-btn-over");
61653     down.addClassOnOver("x-scroller-btn-over");
61654     up.addClassOnClick("x-scroller-btn-click");
61655     down.addClassOnClick("x-scroller-btn-click");
61656     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
61657
61658     this.resizeEl = this.el;
61659     this.el = wrap; this.up = up; this.down = down;
61660 };
61661
61662 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
61663     increment : 100,
61664     wheelIncrement : 5,
61665     scrollUp : function(){
61666         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
61667     },
61668
61669     scrollDown : function(){
61670         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
61671     },
61672
61673     afterScroll : function(){
61674         var el = this.resizeEl;
61675         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
61676         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
61677         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
61678     },
61679
61680     setSize : function(){
61681         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
61682         this.afterScroll();
61683     },
61684
61685     onWheel : function(e){
61686         var d = e.getWheelDelta();
61687         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
61688         this.afterScroll();
61689         e.stopEvent();
61690     },
61691
61692     setContent : function(content, loadScripts){
61693         this.resizeEl.update(content, loadScripts);
61694     }
61695
61696 });
61697
61698
61699
61700 /**
61701  * @class Roo.TreePanel
61702  * @extends Roo.ContentPanel
61703  * @parent Roo.BorderLayout Roo.LayoutDialog builder
61704  * Treepanel component
61705  * 
61706  * @constructor
61707  * Create a new TreePanel. - defaults to fit/scoll contents.
61708  * @param {String/Object} config A string to set only the panel's title, or a config object
61709  */
61710 Roo.TreePanel = function(config){
61711     var el = config.el;
61712     var tree = config.tree;
61713     delete config.tree; 
61714     delete config.el; // hopefull!
61715     
61716     // wrapper for IE7 strict & safari scroll issue
61717     
61718     var treeEl = el.createChild();
61719     config.resizeEl = treeEl;
61720     
61721     
61722     
61723     Roo.TreePanel.superclass.constructor.call(this, el, config);
61724  
61725  
61726     this.tree = new Roo.tree.TreePanel(treeEl , tree);
61727     //console.log(tree);
61728     this.on('activate', function()
61729     {
61730         if (this.tree.rendered) {
61731             return;
61732         }
61733         //console.log('render tree');
61734         this.tree.render();
61735     });
61736     // this should not be needed.. - it's actually the 'el' that resizes?
61737     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
61738     
61739     //this.on('resize',  function (cp, w, h) {
61740     //        this.tree.innerCt.setWidth(w);
61741     //        this.tree.innerCt.setHeight(h);
61742     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
61743     //});
61744
61745         
61746     
61747 };
61748
61749 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
61750     fitToFrame : true,
61751     autoScroll : true,
61752     /*
61753      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
61754      */
61755     tree : false
61756
61757 });
61758 /*
61759  * Based on:
61760  * Ext JS Library 1.1.1
61761  * Copyright(c) 2006-2007, Ext JS, LLC.
61762  *
61763  * Originally Released Under LGPL - original licence link has changed is not relivant.
61764  *
61765  * Fork - LGPL
61766  * <script type="text/javascript">
61767  */
61768  
61769
61770 /**
61771  * @class Roo.ReaderLayout
61772  * @extends Roo.BorderLayout
61773  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
61774  * center region containing two nested regions (a top one for a list view and one for item preview below),
61775  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
61776  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
61777  * expedites the setup of the overall layout and regions for this common application style.
61778  * Example:
61779  <pre><code>
61780 var reader = new Roo.ReaderLayout();
61781 var CP = Roo.ContentPanel;  // shortcut for adding
61782
61783 reader.beginUpdate();
61784 reader.add("north", new CP("north", "North"));
61785 reader.add("west", new CP("west", {title: "West"}));
61786 reader.add("east", new CP("east", {title: "East"}));
61787
61788 reader.regions.listView.add(new CP("listView", "List"));
61789 reader.regions.preview.add(new CP("preview", "Preview"));
61790 reader.endUpdate();
61791 </code></pre>
61792 * @constructor
61793 * Create a new ReaderLayout
61794 * @param {Object} config Configuration options
61795 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
61796 * document.body if omitted)
61797 */
61798 Roo.ReaderLayout = function(config, renderTo){
61799     var c = config || {size:{}};
61800     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
61801         north: c.north !== false ? Roo.apply({
61802             split:false,
61803             initialSize: 32,
61804             titlebar: false
61805         }, c.north) : false,
61806         west: c.west !== false ? Roo.apply({
61807             split:true,
61808             initialSize: 200,
61809             minSize: 175,
61810             maxSize: 400,
61811             titlebar: true,
61812             collapsible: true,
61813             animate: true,
61814             margins:{left:5,right:0,bottom:5,top:5},
61815             cmargins:{left:5,right:5,bottom:5,top:5}
61816         }, c.west) : false,
61817         east: c.east !== false ? Roo.apply({
61818             split:true,
61819             initialSize: 200,
61820             minSize: 175,
61821             maxSize: 400,
61822             titlebar: true,
61823             collapsible: true,
61824             animate: true,
61825             margins:{left:0,right:5,bottom:5,top:5},
61826             cmargins:{left:5,right:5,bottom:5,top:5}
61827         }, c.east) : false,
61828         center: Roo.apply({
61829             tabPosition: 'top',
61830             autoScroll:false,
61831             closeOnTab: true,
61832             titlebar:false,
61833             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
61834         }, c.center)
61835     });
61836
61837     this.el.addClass('x-reader');
61838
61839     this.beginUpdate();
61840
61841     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
61842         south: c.preview !== false ? Roo.apply({
61843             split:true,
61844             initialSize: 200,
61845             minSize: 100,
61846             autoScroll:true,
61847             collapsible:true,
61848             titlebar: true,
61849             cmargins:{top:5,left:0, right:0, bottom:0}
61850         }, c.preview) : false,
61851         center: Roo.apply({
61852             autoScroll:false,
61853             titlebar:false,
61854             minHeight:200
61855         }, c.listView)
61856     });
61857     this.add('center', new Roo.NestedLayoutPanel(inner,
61858             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
61859
61860     this.endUpdate();
61861
61862     this.regions.preview = inner.getRegion('south');
61863     this.regions.listView = inner.getRegion('center');
61864 };
61865
61866 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
61867  * Based on:
61868  * Ext JS Library 1.1.1
61869  * Copyright(c) 2006-2007, Ext JS, LLC.
61870  *
61871  * Originally Released Under LGPL - original licence link has changed is not relivant.
61872  *
61873  * Fork - LGPL
61874  * <script type="text/javascript">
61875  */
61876  
61877 /**
61878  * @class Roo.grid.Grid
61879  * @extends Roo.util.Observable
61880  * This class represents the primary interface of a component based grid control.
61881  * <br><br>Usage:<pre><code>
61882  var grid = new Roo.grid.Grid("my-container-id", {
61883      ds: myDataStore,
61884      cm: myColModel,
61885      selModel: mySelectionModel,
61886      autoSizeColumns: true,
61887      monitorWindowResize: false,
61888      trackMouseOver: true
61889  });
61890  // set any options
61891  grid.render();
61892  * </code></pre>
61893  * <b>Common Problems:</b><br/>
61894  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
61895  * element will correct this<br/>
61896  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
61897  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
61898  * are unpredictable.<br/>
61899  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
61900  * grid to calculate dimensions/offsets.<br/>
61901   * @constructor
61902  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
61903  * The container MUST have some type of size defined for the grid to fill. The container will be
61904  * automatically set to position relative if it isn't already.
61905  * @param {Object} config A config object that sets properties on this grid.
61906  */
61907 Roo.grid.Grid = function(container, config){
61908         // initialize the container
61909         this.container = Roo.get(container);
61910         this.container.update("");
61911         this.container.setStyle("overflow", "hidden");
61912     this.container.addClass('x-grid-container');
61913
61914     this.id = this.container.id;
61915
61916     Roo.apply(this, config);
61917     // check and correct shorthanded configs
61918     if(this.ds){
61919         this.dataSource = this.ds;
61920         delete this.ds;
61921     }
61922     if(this.cm){
61923         this.colModel = this.cm;
61924         delete this.cm;
61925     }
61926     if(this.sm){
61927         this.selModel = this.sm;
61928         delete this.sm;
61929     }
61930
61931     if (this.selModel) {
61932         this.selModel = Roo.factory(this.selModel, Roo.grid);
61933         this.sm = this.selModel;
61934         this.sm.xmodule = this.xmodule || false;
61935     }
61936     if (typeof(this.colModel.config) == 'undefined') {
61937         this.colModel = new Roo.grid.ColumnModel(this.colModel);
61938         this.cm = this.colModel;
61939         this.cm.xmodule = this.xmodule || false;
61940     }
61941     if (this.dataSource) {
61942         this.dataSource= Roo.factory(this.dataSource, Roo.data);
61943         this.ds = this.dataSource;
61944         this.ds.xmodule = this.xmodule || false;
61945          
61946     }
61947     
61948     
61949     
61950     if(this.width){
61951         this.container.setWidth(this.width);
61952     }
61953
61954     if(this.height){
61955         this.container.setHeight(this.height);
61956     }
61957     /** @private */
61958         this.addEvents({
61959         // raw events
61960         /**
61961          * @event click
61962          * The raw click event for the entire grid.
61963          * @param {Roo.EventObject} e
61964          */
61965         "click" : true,
61966         /**
61967          * @event dblclick
61968          * The raw dblclick event for the entire grid.
61969          * @param {Roo.EventObject} e
61970          */
61971         "dblclick" : true,
61972         /**
61973          * @event contextmenu
61974          * The raw contextmenu event for the entire grid.
61975          * @param {Roo.EventObject} e
61976          */
61977         "contextmenu" : true,
61978         /**
61979          * @event mousedown
61980          * The raw mousedown event for the entire grid.
61981          * @param {Roo.EventObject} e
61982          */
61983         "mousedown" : true,
61984         /**
61985          * @event mouseup
61986          * The raw mouseup event for the entire grid.
61987          * @param {Roo.EventObject} e
61988          */
61989         "mouseup" : true,
61990         /**
61991          * @event mouseover
61992          * The raw mouseover event for the entire grid.
61993          * @param {Roo.EventObject} e
61994          */
61995         "mouseover" : true,
61996         /**
61997          * @event mouseout
61998          * The raw mouseout event for the entire grid.
61999          * @param {Roo.EventObject} e
62000          */
62001         "mouseout" : true,
62002         /**
62003          * @event keypress
62004          * The raw keypress event for the entire grid.
62005          * @param {Roo.EventObject} e
62006          */
62007         "keypress" : true,
62008         /**
62009          * @event keydown
62010          * The raw keydown event for the entire grid.
62011          * @param {Roo.EventObject} e
62012          */
62013         "keydown" : true,
62014
62015         // custom events
62016
62017         /**
62018          * @event cellclick
62019          * Fires when a cell is clicked
62020          * @param {Grid} this
62021          * @param {Number} rowIndex
62022          * @param {Number} columnIndex
62023          * @param {Roo.EventObject} e
62024          */
62025         "cellclick" : true,
62026         /**
62027          * @event celldblclick
62028          * Fires when a cell is double clicked
62029          * @param {Grid} this
62030          * @param {Number} rowIndex
62031          * @param {Number} columnIndex
62032          * @param {Roo.EventObject} e
62033          */
62034         "celldblclick" : true,
62035         /**
62036          * @event rowclick
62037          * Fires when a row is clicked
62038          * @param {Grid} this
62039          * @param {Number} rowIndex
62040          * @param {Roo.EventObject} e
62041          */
62042         "rowclick" : true,
62043         /**
62044          * @event rowdblclick
62045          * Fires when a row is double clicked
62046          * @param {Grid} this
62047          * @param {Number} rowIndex
62048          * @param {Roo.EventObject} e
62049          */
62050         "rowdblclick" : true,
62051         /**
62052          * @event headerclick
62053          * Fires when a header is clicked
62054          * @param {Grid} this
62055          * @param {Number} columnIndex
62056          * @param {Roo.EventObject} e
62057          */
62058         "headerclick" : true,
62059         /**
62060          * @event headerdblclick
62061          * Fires when a header cell is double clicked
62062          * @param {Grid} this
62063          * @param {Number} columnIndex
62064          * @param {Roo.EventObject} e
62065          */
62066         "headerdblclick" : true,
62067         /**
62068          * @event rowcontextmenu
62069          * Fires when a row is right clicked
62070          * @param {Grid} this
62071          * @param {Number} rowIndex
62072          * @param {Roo.EventObject} e
62073          */
62074         "rowcontextmenu" : true,
62075         /**
62076          * @event cellcontextmenu
62077          * Fires when a cell is right clicked
62078          * @param {Grid} this
62079          * @param {Number} rowIndex
62080          * @param {Number} cellIndex
62081          * @param {Roo.EventObject} e
62082          */
62083          "cellcontextmenu" : true,
62084         /**
62085          * @event headercontextmenu
62086          * Fires when a header is right clicked
62087          * @param {Grid} this
62088          * @param {Number} columnIndex
62089          * @param {Roo.EventObject} e
62090          */
62091         "headercontextmenu" : true,
62092         /**
62093          * @event bodyscroll
62094          * Fires when the body element is scrolled
62095          * @param {Number} scrollLeft
62096          * @param {Number} scrollTop
62097          */
62098         "bodyscroll" : true,
62099         /**
62100          * @event columnresize
62101          * Fires when the user resizes a column
62102          * @param {Number} columnIndex
62103          * @param {Number} newSize
62104          */
62105         "columnresize" : true,
62106         /**
62107          * @event columnmove
62108          * Fires when the user moves a column
62109          * @param {Number} oldIndex
62110          * @param {Number} newIndex
62111          */
62112         "columnmove" : true,
62113         /**
62114          * @event startdrag
62115          * Fires when row(s) start being dragged
62116          * @param {Grid} this
62117          * @param {Roo.GridDD} dd The drag drop object
62118          * @param {event} e The raw browser event
62119          */
62120         "startdrag" : true,
62121         /**
62122          * @event enddrag
62123          * Fires when a drag operation is complete
62124          * @param {Grid} this
62125          * @param {Roo.GridDD} dd The drag drop object
62126          * @param {event} e The raw browser event
62127          */
62128         "enddrag" : true,
62129         /**
62130          * @event dragdrop
62131          * Fires when dragged row(s) are dropped on a valid DD target
62132          * @param {Grid} this
62133          * @param {Roo.GridDD} dd The drag drop object
62134          * @param {String} targetId The target drag drop object
62135          * @param {event} e The raw browser event
62136          */
62137         "dragdrop" : true,
62138         /**
62139          * @event dragover
62140          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
62141          * @param {Grid} this
62142          * @param {Roo.GridDD} dd The drag drop object
62143          * @param {String} targetId The target drag drop object
62144          * @param {event} e The raw browser event
62145          */
62146         "dragover" : true,
62147         /**
62148          * @event dragenter
62149          *  Fires when the dragged row(s) first cross another DD target while being dragged
62150          * @param {Grid} this
62151          * @param {Roo.GridDD} dd The drag drop object
62152          * @param {String} targetId The target drag drop object
62153          * @param {event} e The raw browser event
62154          */
62155         "dragenter" : true,
62156         /**
62157          * @event dragout
62158          * Fires when the dragged row(s) leave another DD target while being dragged
62159          * @param {Grid} this
62160          * @param {Roo.GridDD} dd The drag drop object
62161          * @param {String} targetId The target drag drop object
62162          * @param {event} e The raw browser event
62163          */
62164         "dragout" : true,
62165         /**
62166          * @event rowclass
62167          * Fires when a row is rendered, so you can change add a style to it.
62168          * @param {GridView} gridview   The grid view
62169          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
62170          */
62171         'rowclass' : true,
62172
62173         /**
62174          * @event render
62175          * Fires when the grid is rendered
62176          * @param {Grid} grid
62177          */
62178         'render' : true
62179     });
62180
62181     Roo.grid.Grid.superclass.constructor.call(this);
62182 };
62183 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
62184     
62185     /**
62186          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
62187          */
62188         /**
62189          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
62190          */
62191         /**
62192          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
62193          */
62194         /**
62195          * @cfg {Roo.data.Store} ds The data store for the grid
62196          */
62197         /**
62198          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
62199          */
62200          
62201          /**
62202          * @cfg {Roo.PagingToolbar} footer the paging toolbar
62203          */
62204         
62205         /**
62206      * @cfg {String} ddGroup - drag drop group.
62207      */
62208       /**
62209      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
62210      */
62211
62212     /**
62213      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
62214      */
62215     minColumnWidth : 25,
62216
62217     /**
62218      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
62219      * <b>on initial render.</b> It is more efficient to explicitly size the columns
62220      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
62221      */
62222     autoSizeColumns : false,
62223
62224     /**
62225      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
62226      */
62227     autoSizeHeaders : true,
62228
62229     /**
62230      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
62231      */
62232     monitorWindowResize : true,
62233
62234     /**
62235      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
62236      * rows measured to get a columns size. Default is 0 (all rows).
62237      */
62238     maxRowsToMeasure : 0,
62239
62240     /**
62241      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
62242      */
62243     trackMouseOver : true,
62244
62245     /**
62246     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
62247     */
62248       /**
62249     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
62250     */
62251     
62252     /**
62253     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
62254     */
62255     enableDragDrop : false,
62256     
62257     /**
62258     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
62259     */
62260     enableColumnMove : true,
62261     
62262     /**
62263     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
62264     */
62265     enableColumnHide : true,
62266     
62267     /**
62268     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
62269     */
62270     enableRowHeightSync : false,
62271     
62272     /**
62273     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
62274     */
62275     stripeRows : true,
62276     
62277     /**
62278     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
62279     */
62280     autoHeight : false,
62281
62282     /**
62283      * @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.
62284      */
62285     autoExpandColumn : false,
62286
62287     /**
62288     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
62289     * Default is 50.
62290     */
62291     autoExpandMin : 50,
62292
62293     /**
62294     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
62295     */
62296     autoExpandMax : 1000,
62297
62298     /**
62299     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
62300     */
62301     view : null,
62302
62303     /**
62304     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
62305     */
62306     loadMask : false,
62307     /**
62308     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
62309     */
62310     dropTarget: false,
62311      /**
62312     * @cfg {boolean} sortColMenu Sort the column order menu when it shows (usefull for long lists..) default false
62313     */ 
62314     sortColMenu : false,
62315     
62316     // private
62317     rendered : false,
62318
62319     /**
62320     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
62321     * of a fixed width. Default is false.
62322     */
62323     /**
62324     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
62325     */
62326     
62327     
62328     /**
62329     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
62330     * %0 is replaced with the number of selected rows.
62331     */
62332     ddText : "{0} selected row{1}",
62333     
62334     
62335     /**
62336      * Called once after all setup has been completed and the grid is ready to be rendered.
62337      * @return {Roo.grid.Grid} this
62338      */
62339     render : function()
62340     {
62341         var c = this.container;
62342         // try to detect autoHeight/width mode
62343         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
62344             this.autoHeight = true;
62345         }
62346         var view = this.getView();
62347         view.init(this);
62348
62349         c.on("click", this.onClick, this);
62350         c.on("dblclick", this.onDblClick, this);
62351         c.on("contextmenu", this.onContextMenu, this);
62352         c.on("keydown", this.onKeyDown, this);
62353         if (Roo.isTouch) {
62354             c.on("touchstart", this.onTouchStart, this);
62355         }
62356
62357         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
62358
62359         this.getSelectionModel().init(this);
62360
62361         view.render();
62362
62363         if(this.loadMask){
62364             this.loadMask = new Roo.LoadMask(this.container,
62365                     Roo.apply({store:this.dataSource}, this.loadMask));
62366         }
62367         
62368         
62369         if (this.toolbar && this.toolbar.xtype) {
62370             this.toolbar.container = this.getView().getHeaderPanel(true);
62371             this.toolbar = new Roo.Toolbar(this.toolbar);
62372         }
62373         if (this.footer && this.footer.xtype) {
62374             this.footer.dataSource = this.getDataSource();
62375             this.footer.container = this.getView().getFooterPanel(true);
62376             this.footer = Roo.factory(this.footer, Roo);
62377         }
62378         if (this.dropTarget && this.dropTarget.xtype) {
62379             delete this.dropTarget.xtype;
62380             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
62381         }
62382         
62383         
62384         this.rendered = true;
62385         this.fireEvent('render', this);
62386         return this;
62387     },
62388
62389     /**
62390      * Reconfigures the grid to use a different Store and Column Model.
62391      * The View will be bound to the new objects and refreshed.
62392      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
62393      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
62394      */
62395     reconfigure : function(dataSource, colModel){
62396         if(this.loadMask){
62397             this.loadMask.destroy();
62398             this.loadMask = new Roo.LoadMask(this.container,
62399                     Roo.apply({store:dataSource}, this.loadMask));
62400         }
62401         this.view.bind(dataSource, colModel);
62402         this.dataSource = dataSource;
62403         this.colModel = colModel;
62404         this.view.refresh(true);
62405     },
62406     /**
62407      * addColumns
62408      * Add's a column, default at the end..
62409      
62410      * @param {int} position to add (default end)
62411      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
62412      */
62413     addColumns : function(pos, ar)
62414     {
62415         
62416         for (var i =0;i< ar.length;i++) {
62417             var cfg = ar[i];
62418             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
62419             this.cm.lookup[cfg.id] = cfg;
62420         }
62421         
62422         
62423         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
62424             pos = this.cm.config.length; //this.cm.config.push(cfg);
62425         } 
62426         pos = Math.max(0,pos);
62427         ar.unshift(0);
62428         ar.unshift(pos);
62429         this.cm.config.splice.apply(this.cm.config, ar);
62430         
62431         
62432         
62433         this.view.generateRules(this.cm);
62434         this.view.refresh(true);
62435         
62436     },
62437     
62438     
62439     
62440     
62441     // private
62442     onKeyDown : function(e){
62443         this.fireEvent("keydown", e);
62444     },
62445
62446     /**
62447      * Destroy this grid.
62448      * @param {Boolean} removeEl True to remove the element
62449      */
62450     destroy : function(removeEl, keepListeners){
62451         if(this.loadMask){
62452             this.loadMask.destroy();
62453         }
62454         var c = this.container;
62455         c.removeAllListeners();
62456         this.view.destroy();
62457         this.colModel.purgeListeners();
62458         if(!keepListeners){
62459             this.purgeListeners();
62460         }
62461         c.update("");
62462         if(removeEl === true){
62463             c.remove();
62464         }
62465     },
62466
62467     // private
62468     processEvent : function(name, e){
62469         // does this fire select???
62470         //Roo.log('grid:processEvent '  + name);
62471         
62472         if (name != 'touchstart' ) {
62473             this.fireEvent(name, e);    
62474         }
62475         
62476         var t = e.getTarget();
62477         var v = this.view;
62478         var header = v.findHeaderIndex(t);
62479         if(header !== false){
62480             var ename = name == 'touchstart' ? 'click' : name;
62481              
62482             this.fireEvent("header" + ename, this, header, e);
62483         }else{
62484             var row = v.findRowIndex(t);
62485             var cell = v.findCellIndex(t);
62486             if (name == 'touchstart') {
62487                 // first touch is always a click.
62488                 // hopefull this happens after selection is updated.?
62489                 name = false;
62490                 
62491                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
62492                     var cs = this.selModel.getSelectedCell();
62493                     if (row == cs[0] && cell == cs[1]){
62494                         name = 'dblclick';
62495                     }
62496                 }
62497                 if (typeof(this.selModel.getSelections) != 'undefined') {
62498                     var cs = this.selModel.getSelections();
62499                     var ds = this.dataSource;
62500                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
62501                         name = 'dblclick';
62502                     }
62503                 }
62504                 if (!name) {
62505                     return;
62506                 }
62507             }
62508             
62509             
62510             if(row !== false){
62511                 this.fireEvent("row" + name, this, row, e);
62512                 if(cell !== false){
62513                     this.fireEvent("cell" + name, this, row, cell, e);
62514                 }
62515             }
62516         }
62517     },
62518
62519     // private
62520     onClick : function(e){
62521         this.processEvent("click", e);
62522     },
62523    // private
62524     onTouchStart : function(e){
62525         this.processEvent("touchstart", e);
62526     },
62527
62528     // private
62529     onContextMenu : function(e, t){
62530         this.processEvent("contextmenu", e);
62531     },
62532
62533     // private
62534     onDblClick : function(e){
62535         this.processEvent("dblclick", e);
62536     },
62537
62538     // private
62539     walkCells : function(row, col, step, fn, scope){
62540         var cm = this.colModel, clen = cm.getColumnCount();
62541         var ds = this.dataSource, rlen = ds.getCount(), first = true;
62542         if(step < 0){
62543             if(col < 0){
62544                 row--;
62545                 first = false;
62546             }
62547             while(row >= 0){
62548                 if(!first){
62549                     col = clen-1;
62550                 }
62551                 first = false;
62552                 while(col >= 0){
62553                     if(fn.call(scope || this, row, col, cm) === true){
62554                         return [row, col];
62555                     }
62556                     col--;
62557                 }
62558                 row--;
62559             }
62560         } else {
62561             if(col >= clen){
62562                 row++;
62563                 first = false;
62564             }
62565             while(row < rlen){
62566                 if(!first){
62567                     col = 0;
62568                 }
62569                 first = false;
62570                 while(col < clen){
62571                     if(fn.call(scope || this, row, col, cm) === true){
62572                         return [row, col];
62573                     }
62574                     col++;
62575                 }
62576                 row++;
62577             }
62578         }
62579         return null;
62580     },
62581
62582     // private
62583     getSelections : function(){
62584         return this.selModel.getSelections();
62585     },
62586
62587     /**
62588      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
62589      * but if manual update is required this method will initiate it.
62590      */
62591     autoSize : function(){
62592         if(this.rendered){
62593             this.view.layout();
62594             if(this.view.adjustForScroll){
62595                 this.view.adjustForScroll();
62596             }
62597         }
62598     },
62599
62600     /**
62601      * Returns the grid's underlying element.
62602      * @return {Element} The element
62603      */
62604     getGridEl : function(){
62605         return this.container;
62606     },
62607
62608     // private for compatibility, overridden by editor grid
62609     stopEditing : function(){},
62610
62611     /**
62612      * Returns the grid's SelectionModel.
62613      * @return {SelectionModel}
62614      */
62615     getSelectionModel : function(){
62616         if(!this.selModel){
62617             this.selModel = new Roo.grid.RowSelectionModel();
62618         }
62619         return this.selModel;
62620     },
62621
62622     /**
62623      * Returns the grid's DataSource.
62624      * @return {DataSource}
62625      */
62626     getDataSource : function(){
62627         return this.dataSource;
62628     },
62629
62630     /**
62631      * Returns the grid's ColumnModel.
62632      * @return {ColumnModel}
62633      */
62634     getColumnModel : function(){
62635         return this.colModel;
62636     },
62637
62638     /**
62639      * Returns the grid's GridView object.
62640      * @return {GridView}
62641      */
62642     getView : function(){
62643         if(!this.view){
62644             this.view = new Roo.grid.GridView(this.viewConfig);
62645             this.relayEvents(this.view, [
62646                 "beforerowremoved", "beforerowsinserted",
62647                 "beforerefresh", "rowremoved",
62648                 "rowsinserted", "rowupdated" ,"refresh"
62649             ]);
62650         }
62651         return this.view;
62652     },
62653     /**
62654      * Called to get grid's drag proxy text, by default returns this.ddText.
62655      * Override this to put something different in the dragged text.
62656      * @return {String}
62657      */
62658     getDragDropText : function(){
62659         var count = this.selModel.getCount();
62660         return String.format(this.ddText, count, count == 1 ? '' : 's');
62661     }
62662 });
62663 /*
62664  * Based on:
62665  * Ext JS Library 1.1.1
62666  * Copyright(c) 2006-2007, Ext JS, LLC.
62667  *
62668  * Originally Released Under LGPL - original licence link has changed is not relivant.
62669  *
62670  * Fork - LGPL
62671  * <script type="text/javascript">
62672  */
62673  /**
62674  * @class Roo.grid.AbstractGridView
62675  * @extends Roo.util.Observable
62676  * @abstract
62677  * Abstract base class for grid Views
62678  * @constructor
62679  */
62680 Roo.grid.AbstractGridView = function(){
62681         this.grid = null;
62682         
62683         this.events = {
62684             "beforerowremoved" : true,
62685             "beforerowsinserted" : true,
62686             "beforerefresh" : true,
62687             "rowremoved" : true,
62688             "rowsinserted" : true,
62689             "rowupdated" : true,
62690             "refresh" : true
62691         };
62692     Roo.grid.AbstractGridView.superclass.constructor.call(this);
62693 };
62694
62695 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
62696     rowClass : "x-grid-row",
62697     cellClass : "x-grid-cell",
62698     tdClass : "x-grid-td",
62699     hdClass : "x-grid-hd",
62700     splitClass : "x-grid-hd-split",
62701     
62702     init: function(grid){
62703         this.grid = grid;
62704                 var cid = this.grid.getGridEl().id;
62705         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
62706         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
62707         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
62708         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
62709         },
62710         
62711     getColumnRenderers : function(){
62712         var renderers = [];
62713         var cm = this.grid.colModel;
62714         var colCount = cm.getColumnCount();
62715         for(var i = 0; i < colCount; i++){
62716             renderers[i] = cm.getRenderer(i);
62717         }
62718         return renderers;
62719     },
62720     
62721     getColumnIds : function(){
62722         var ids = [];
62723         var cm = this.grid.colModel;
62724         var colCount = cm.getColumnCount();
62725         for(var i = 0; i < colCount; i++){
62726             ids[i] = cm.getColumnId(i);
62727         }
62728         return ids;
62729     },
62730     
62731     getDataIndexes : function(){
62732         if(!this.indexMap){
62733             this.indexMap = this.buildIndexMap();
62734         }
62735         return this.indexMap.colToData;
62736     },
62737     
62738     getColumnIndexByDataIndex : function(dataIndex){
62739         if(!this.indexMap){
62740             this.indexMap = this.buildIndexMap();
62741         }
62742         return this.indexMap.dataToCol[dataIndex];
62743     },
62744     
62745     /**
62746      * Set a css style for a column dynamically. 
62747      * @param {Number} colIndex The index of the column
62748      * @param {String} name The css property name
62749      * @param {String} value The css value
62750      */
62751     setCSSStyle : function(colIndex, name, value){
62752         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
62753         Roo.util.CSS.updateRule(selector, name, value);
62754     },
62755     
62756     generateRules : function(cm){
62757         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
62758         Roo.util.CSS.removeStyleSheet(rulesId);
62759         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
62760             var cid = cm.getColumnId(i);
62761             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
62762                          this.tdSelector, cid, " {\n}\n",
62763                          this.hdSelector, cid, " {\n}\n",
62764                          this.splitSelector, cid, " {\n}\n");
62765         }
62766         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
62767     }
62768 });/*
62769  * Based on:
62770  * Ext JS Library 1.1.1
62771  * Copyright(c) 2006-2007, Ext JS, LLC.
62772  *
62773  * Originally Released Under LGPL - original licence link has changed is not relivant.
62774  *
62775  * Fork - LGPL
62776  * <script type="text/javascript">
62777  */
62778
62779 // private
62780 // This is a support class used internally by the Grid components
62781 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
62782     this.grid = grid;
62783     this.view = grid.getView();
62784     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
62785     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
62786     if(hd2){
62787         this.setHandleElId(Roo.id(hd));
62788         this.setOuterHandleElId(Roo.id(hd2));
62789     }
62790     this.scroll = false;
62791 };
62792 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
62793     maxDragWidth: 120,
62794     getDragData : function(e){
62795         var t = Roo.lib.Event.getTarget(e);
62796         var h = this.view.findHeaderCell(t);
62797         if(h){
62798             return {ddel: h.firstChild, header:h};
62799         }
62800         return false;
62801     },
62802
62803     onInitDrag : function(e){
62804         this.view.headersDisabled = true;
62805         var clone = this.dragData.ddel.cloneNode(true);
62806         clone.id = Roo.id();
62807         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
62808         this.proxy.update(clone);
62809         return true;
62810     },
62811
62812     afterValidDrop : function(){
62813         var v = this.view;
62814         setTimeout(function(){
62815             v.headersDisabled = false;
62816         }, 50);
62817     },
62818
62819     afterInvalidDrop : function(){
62820         var v = this.view;
62821         setTimeout(function(){
62822             v.headersDisabled = false;
62823         }, 50);
62824     }
62825 });
62826 /*
62827  * Based on:
62828  * Ext JS Library 1.1.1
62829  * Copyright(c) 2006-2007, Ext JS, LLC.
62830  *
62831  * Originally Released Under LGPL - original licence link has changed is not relivant.
62832  *
62833  * Fork - LGPL
62834  * <script type="text/javascript">
62835  */
62836 // private
62837 // This is a support class used internally by the Grid components
62838 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
62839     this.grid = grid;
62840     this.view = grid.getView();
62841     // split the proxies so they don't interfere with mouse events
62842     this.proxyTop = Roo.DomHelper.append(document.body, {
62843         cls:"col-move-top", html:"&#160;"
62844     }, true);
62845     this.proxyBottom = Roo.DomHelper.append(document.body, {
62846         cls:"col-move-bottom", html:"&#160;"
62847     }, true);
62848     this.proxyTop.hide = this.proxyBottom.hide = function(){
62849         this.setLeftTop(-100,-100);
62850         this.setStyle("visibility", "hidden");
62851     };
62852     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
62853     // temporarily disabled
62854     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
62855     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
62856 };
62857 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
62858     proxyOffsets : [-4, -9],
62859     fly: Roo.Element.fly,
62860
62861     getTargetFromEvent : function(e){
62862         var t = Roo.lib.Event.getTarget(e);
62863         var cindex = this.view.findCellIndex(t);
62864         if(cindex !== false){
62865             return this.view.getHeaderCell(cindex);
62866         }
62867         return null;
62868     },
62869
62870     nextVisible : function(h){
62871         var v = this.view, cm = this.grid.colModel;
62872         h = h.nextSibling;
62873         while(h){
62874             if(!cm.isHidden(v.getCellIndex(h))){
62875                 return h;
62876             }
62877             h = h.nextSibling;
62878         }
62879         return null;
62880     },
62881
62882     prevVisible : function(h){
62883         var v = this.view, cm = this.grid.colModel;
62884         h = h.prevSibling;
62885         while(h){
62886             if(!cm.isHidden(v.getCellIndex(h))){
62887                 return h;
62888             }
62889             h = h.prevSibling;
62890         }
62891         return null;
62892     },
62893
62894     positionIndicator : function(h, n, e){
62895         var x = Roo.lib.Event.getPageX(e);
62896         var r = Roo.lib.Dom.getRegion(n.firstChild);
62897         var px, pt, py = r.top + this.proxyOffsets[1];
62898         if((r.right - x) <= (r.right-r.left)/2){
62899             px = r.right+this.view.borderWidth;
62900             pt = "after";
62901         }else{
62902             px = r.left;
62903             pt = "before";
62904         }
62905         var oldIndex = this.view.getCellIndex(h);
62906         var newIndex = this.view.getCellIndex(n);
62907
62908         if(this.grid.colModel.isFixed(newIndex)){
62909             return false;
62910         }
62911
62912         var locked = this.grid.colModel.isLocked(newIndex);
62913
62914         if(pt == "after"){
62915             newIndex++;
62916         }
62917         if(oldIndex < newIndex){
62918             newIndex--;
62919         }
62920         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
62921             return false;
62922         }
62923         px +=  this.proxyOffsets[0];
62924         this.proxyTop.setLeftTop(px, py);
62925         this.proxyTop.show();
62926         if(!this.bottomOffset){
62927             this.bottomOffset = this.view.mainHd.getHeight();
62928         }
62929         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
62930         this.proxyBottom.show();
62931         return pt;
62932     },
62933
62934     onNodeEnter : function(n, dd, e, data){
62935         if(data.header != n){
62936             this.positionIndicator(data.header, n, e);
62937         }
62938     },
62939
62940     onNodeOver : function(n, dd, e, data){
62941         var result = false;
62942         if(data.header != n){
62943             result = this.positionIndicator(data.header, n, e);
62944         }
62945         if(!result){
62946             this.proxyTop.hide();
62947             this.proxyBottom.hide();
62948         }
62949         return result ? this.dropAllowed : this.dropNotAllowed;
62950     },
62951
62952     onNodeOut : function(n, dd, e, data){
62953         this.proxyTop.hide();
62954         this.proxyBottom.hide();
62955     },
62956
62957     onNodeDrop : function(n, dd, e, data){
62958         var h = data.header;
62959         if(h != n){
62960             var cm = this.grid.colModel;
62961             var x = Roo.lib.Event.getPageX(e);
62962             var r = Roo.lib.Dom.getRegion(n.firstChild);
62963             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
62964             var oldIndex = this.view.getCellIndex(h);
62965             var newIndex = this.view.getCellIndex(n);
62966             var locked = cm.isLocked(newIndex);
62967             if(pt == "after"){
62968                 newIndex++;
62969             }
62970             if(oldIndex < newIndex){
62971                 newIndex--;
62972             }
62973             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
62974                 return false;
62975             }
62976             cm.setLocked(oldIndex, locked, true);
62977             cm.moveColumn(oldIndex, newIndex);
62978             this.grid.fireEvent("columnmove", oldIndex, newIndex);
62979             return true;
62980         }
62981         return false;
62982     }
62983 });
62984 /*
62985  * Based on:
62986  * Ext JS Library 1.1.1
62987  * Copyright(c) 2006-2007, Ext JS, LLC.
62988  *
62989  * Originally Released Under LGPL - original licence link has changed is not relivant.
62990  *
62991  * Fork - LGPL
62992  * <script type="text/javascript">
62993  */
62994   
62995 /**
62996  * @class Roo.grid.GridView
62997  * @extends Roo.util.Observable
62998  *
62999  * @constructor
63000  * @param {Object} config
63001  */
63002 Roo.grid.GridView = function(config){
63003     Roo.grid.GridView.superclass.constructor.call(this);
63004     this.el = null;
63005
63006     Roo.apply(this, config);
63007 };
63008
63009 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
63010
63011     unselectable :  'unselectable="on"',
63012     unselectableCls :  'x-unselectable',
63013     
63014     
63015     rowClass : "x-grid-row",
63016
63017     cellClass : "x-grid-col",
63018
63019     tdClass : "x-grid-td",
63020
63021     hdClass : "x-grid-hd",
63022
63023     splitClass : "x-grid-split",
63024
63025     sortClasses : ["sort-asc", "sort-desc"],
63026
63027     enableMoveAnim : false,
63028
63029     hlColor: "C3DAF9",
63030
63031     dh : Roo.DomHelper,
63032
63033     fly : Roo.Element.fly,
63034
63035     css : Roo.util.CSS,
63036
63037     borderWidth: 1,
63038
63039     splitOffset: 3,
63040
63041     scrollIncrement : 22,
63042
63043     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
63044
63045     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
63046
63047     bind : function(ds, cm){
63048         if(this.ds){
63049             this.ds.un("load", this.onLoad, this);
63050             this.ds.un("datachanged", this.onDataChange, this);
63051             this.ds.un("add", this.onAdd, this);
63052             this.ds.un("remove", this.onRemove, this);
63053             this.ds.un("update", this.onUpdate, this);
63054             this.ds.un("clear", this.onClear, this);
63055         }
63056         if(ds){
63057             ds.on("load", this.onLoad, this);
63058             ds.on("datachanged", this.onDataChange, this);
63059             ds.on("add", this.onAdd, this);
63060             ds.on("remove", this.onRemove, this);
63061             ds.on("update", this.onUpdate, this);
63062             ds.on("clear", this.onClear, this);
63063         }
63064         this.ds = ds;
63065
63066         if(this.cm){
63067             this.cm.un("widthchange", this.onColWidthChange, this);
63068             this.cm.un("headerchange", this.onHeaderChange, this);
63069             this.cm.un("hiddenchange", this.onHiddenChange, this);
63070             this.cm.un("columnmoved", this.onColumnMove, this);
63071             this.cm.un("columnlockchange", this.onColumnLock, this);
63072         }
63073         if(cm){
63074             this.generateRules(cm);
63075             cm.on("widthchange", this.onColWidthChange, this);
63076             cm.on("headerchange", this.onHeaderChange, this);
63077             cm.on("hiddenchange", this.onHiddenChange, this);
63078             cm.on("columnmoved", this.onColumnMove, this);
63079             cm.on("columnlockchange", this.onColumnLock, this);
63080         }
63081         this.cm = cm;
63082     },
63083
63084     init: function(grid){
63085         Roo.grid.GridView.superclass.init.call(this, grid);
63086
63087         this.bind(grid.dataSource, grid.colModel);
63088
63089         grid.on("headerclick", this.handleHeaderClick, this);
63090
63091         if(grid.trackMouseOver){
63092             grid.on("mouseover", this.onRowOver, this);
63093             grid.on("mouseout", this.onRowOut, this);
63094         }
63095         grid.cancelTextSelection = function(){};
63096         this.gridId = grid.id;
63097
63098         var tpls = this.templates || {};
63099
63100         if(!tpls.master){
63101             tpls.master = new Roo.Template(
63102                '<div class="x-grid" hidefocus="true">',
63103                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
63104                   '<div class="x-grid-topbar"></div>',
63105                   '<div class="x-grid-scroller"><div></div></div>',
63106                   '<div class="x-grid-locked">',
63107                       '<div class="x-grid-header">{lockedHeader}</div>',
63108                       '<div class="x-grid-body">{lockedBody}</div>',
63109                   "</div>",
63110                   '<div class="x-grid-viewport">',
63111                       '<div class="x-grid-header">{header}</div>',
63112                       '<div class="x-grid-body">{body}</div>',
63113                   "</div>",
63114                   '<div class="x-grid-bottombar"></div>',
63115                  
63116                   '<div class="x-grid-resize-proxy">&#160;</div>',
63117                "</div>"
63118             );
63119             tpls.master.disableformats = true;
63120         }
63121
63122         if(!tpls.header){
63123             tpls.header = new Roo.Template(
63124                '<table border="0" cellspacing="0" cellpadding="0">',
63125                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
63126                "</table>{splits}"
63127             );
63128             tpls.header.disableformats = true;
63129         }
63130         tpls.header.compile();
63131
63132         if(!tpls.hcell){
63133             tpls.hcell = new Roo.Template(
63134                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
63135                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
63136                 "</div></td>"
63137              );
63138              tpls.hcell.disableFormats = true;
63139         }
63140         tpls.hcell.compile();
63141
63142         if(!tpls.hsplit){
63143             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
63144                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
63145             tpls.hsplit.disableFormats = true;
63146         }
63147         tpls.hsplit.compile();
63148
63149         if(!tpls.body){
63150             tpls.body = new Roo.Template(
63151                '<table border="0" cellspacing="0" cellpadding="0">',
63152                "<tbody>{rows}</tbody>",
63153                "</table>"
63154             );
63155             tpls.body.disableFormats = true;
63156         }
63157         tpls.body.compile();
63158
63159         if(!tpls.row){
63160             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
63161             tpls.row.disableFormats = true;
63162         }
63163         tpls.row.compile();
63164
63165         if(!tpls.cell){
63166             tpls.cell = new Roo.Template(
63167                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
63168                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
63169                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
63170                 "</td>"
63171             );
63172             tpls.cell.disableFormats = true;
63173         }
63174         tpls.cell.compile();
63175
63176         this.templates = tpls;
63177     },
63178
63179     // remap these for backwards compat
63180     onColWidthChange : function(){
63181         this.updateColumns.apply(this, arguments);
63182     },
63183     onHeaderChange : function(){
63184         this.updateHeaders.apply(this, arguments);
63185     }, 
63186     onHiddenChange : function(){
63187         this.handleHiddenChange.apply(this, arguments);
63188     },
63189     onColumnMove : function(){
63190         this.handleColumnMove.apply(this, arguments);
63191     },
63192     onColumnLock : function(){
63193         this.handleLockChange.apply(this, arguments);
63194     },
63195
63196     onDataChange : function(){
63197         this.refresh();
63198         this.updateHeaderSortState();
63199     },
63200
63201     onClear : function(){
63202         this.refresh();
63203     },
63204
63205     onUpdate : function(ds, record){
63206         this.refreshRow(record);
63207     },
63208
63209     refreshRow : function(record){
63210         var ds = this.ds, index;
63211         if(typeof record == 'number'){
63212             index = record;
63213             record = ds.getAt(index);
63214         }else{
63215             index = ds.indexOf(record);
63216         }
63217         this.insertRows(ds, index, index, true);
63218         this.onRemove(ds, record, index+1, true);
63219         this.syncRowHeights(index, index);
63220         this.layout();
63221         this.fireEvent("rowupdated", this, index, record);
63222     },
63223
63224     onAdd : function(ds, records, index){
63225         this.insertRows(ds, index, index + (records.length-1));
63226     },
63227
63228     onRemove : function(ds, record, index, isUpdate){
63229         if(isUpdate !== true){
63230             this.fireEvent("beforerowremoved", this, index, record);
63231         }
63232         var bt = this.getBodyTable(), lt = this.getLockedTable();
63233         if(bt.rows[index]){
63234             bt.firstChild.removeChild(bt.rows[index]);
63235         }
63236         if(lt.rows[index]){
63237             lt.firstChild.removeChild(lt.rows[index]);
63238         }
63239         if(isUpdate !== true){
63240             this.stripeRows(index);
63241             this.syncRowHeights(index, index);
63242             this.layout();
63243             this.fireEvent("rowremoved", this, index, record);
63244         }
63245     },
63246
63247     onLoad : function(){
63248         this.scrollToTop();
63249     },
63250
63251     /**
63252      * Scrolls the grid to the top
63253      */
63254     scrollToTop : function(){
63255         if(this.scroller){
63256             this.scroller.dom.scrollTop = 0;
63257             this.syncScroll();
63258         }
63259     },
63260
63261     /**
63262      * Gets a panel in the header of the grid that can be used for toolbars etc.
63263      * After modifying the contents of this panel a call to grid.autoSize() may be
63264      * required to register any changes in size.
63265      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
63266      * @return Roo.Element
63267      */
63268     getHeaderPanel : function(doShow){
63269         if(doShow){
63270             this.headerPanel.show();
63271         }
63272         return this.headerPanel;
63273     },
63274
63275     /**
63276      * Gets a panel in the footer of the grid that can be used for toolbars etc.
63277      * After modifying the contents of this panel a call to grid.autoSize() may be
63278      * required to register any changes in size.
63279      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
63280      * @return Roo.Element
63281      */
63282     getFooterPanel : function(doShow){
63283         if(doShow){
63284             this.footerPanel.show();
63285         }
63286         return this.footerPanel;
63287     },
63288
63289     initElements : function(){
63290         var E = Roo.Element;
63291         var el = this.grid.getGridEl().dom.firstChild;
63292         var cs = el.childNodes;
63293
63294         this.el = new E(el);
63295         
63296          this.focusEl = new E(el.firstChild);
63297         this.focusEl.swallowEvent("click", true);
63298         
63299         this.headerPanel = new E(cs[1]);
63300         this.headerPanel.enableDisplayMode("block");
63301
63302         this.scroller = new E(cs[2]);
63303         this.scrollSizer = new E(this.scroller.dom.firstChild);
63304
63305         this.lockedWrap = new E(cs[3]);
63306         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
63307         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
63308
63309         this.mainWrap = new E(cs[4]);
63310         this.mainHd = new E(this.mainWrap.dom.firstChild);
63311         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
63312
63313         this.footerPanel = new E(cs[5]);
63314         this.footerPanel.enableDisplayMode("block");
63315
63316         this.resizeProxy = new E(cs[6]);
63317
63318         this.headerSelector = String.format(
63319            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
63320            this.lockedHd.id, this.mainHd.id
63321         );
63322
63323         this.splitterSelector = String.format(
63324            '#{0} div.x-grid-split, #{1} div.x-grid-split',
63325            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
63326         );
63327     },
63328     idToCssName : function(s)
63329     {
63330         return s.replace(/[^a-z0-9]+/ig, '-');
63331     },
63332
63333     getHeaderCell : function(index){
63334         return Roo.DomQuery.select(this.headerSelector)[index];
63335     },
63336
63337     getHeaderCellMeasure : function(index){
63338         return this.getHeaderCell(index).firstChild;
63339     },
63340
63341     getHeaderCellText : function(index){
63342         return this.getHeaderCell(index).firstChild.firstChild;
63343     },
63344
63345     getLockedTable : function(){
63346         return this.lockedBody.dom.firstChild;
63347     },
63348
63349     getBodyTable : function(){
63350         return this.mainBody.dom.firstChild;
63351     },
63352
63353     getLockedRow : function(index){
63354         return this.getLockedTable().rows[index];
63355     },
63356
63357     getRow : function(index){
63358         return this.getBodyTable().rows[index];
63359     },
63360
63361     getRowComposite : function(index){
63362         if(!this.rowEl){
63363             this.rowEl = new Roo.CompositeElementLite();
63364         }
63365         var els = [], lrow, mrow;
63366         if(lrow = this.getLockedRow(index)){
63367             els.push(lrow);
63368         }
63369         if(mrow = this.getRow(index)){
63370             els.push(mrow);
63371         }
63372         this.rowEl.elements = els;
63373         return this.rowEl;
63374     },
63375     /**
63376      * Gets the 'td' of the cell
63377      * 
63378      * @param {Integer} rowIndex row to select
63379      * @param {Integer} colIndex column to select
63380      * 
63381      * @return {Object} 
63382      */
63383     getCell : function(rowIndex, colIndex){
63384         var locked = this.cm.getLockedCount();
63385         var source;
63386         if(colIndex < locked){
63387             source = this.lockedBody.dom.firstChild;
63388         }else{
63389             source = this.mainBody.dom.firstChild;
63390             colIndex -= locked;
63391         }
63392         return source.rows[rowIndex].childNodes[colIndex];
63393     },
63394
63395     getCellText : function(rowIndex, colIndex){
63396         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
63397     },
63398
63399     getCellBox : function(cell){
63400         var b = this.fly(cell).getBox();
63401         if(Roo.isOpera){ // opera fails to report the Y
63402             b.y = cell.offsetTop + this.mainBody.getY();
63403         }
63404         return b;
63405     },
63406
63407     getCellIndex : function(cell){
63408         var id = String(cell.className).match(this.cellRE);
63409         if(id){
63410             return parseInt(id[1], 10);
63411         }
63412         return 0;
63413     },
63414
63415     findHeaderIndex : function(n){
63416         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
63417         return r ? this.getCellIndex(r) : false;
63418     },
63419
63420     findHeaderCell : function(n){
63421         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
63422         return r ? r : false;
63423     },
63424
63425     findRowIndex : function(n){
63426         if(!n){
63427             return false;
63428         }
63429         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
63430         return r ? r.rowIndex : false;
63431     },
63432
63433     findCellIndex : function(node){
63434         var stop = this.el.dom;
63435         while(node && node != stop){
63436             if(this.findRE.test(node.className)){
63437                 return this.getCellIndex(node);
63438             }
63439             node = node.parentNode;
63440         }
63441         return false;
63442     },
63443
63444     getColumnId : function(index){
63445         return this.cm.getColumnId(index);
63446     },
63447
63448     getSplitters : function()
63449     {
63450         if(this.splitterSelector){
63451            return Roo.DomQuery.select(this.splitterSelector);
63452         }else{
63453             return null;
63454       }
63455     },
63456
63457     getSplitter : function(index){
63458         return this.getSplitters()[index];
63459     },
63460
63461     onRowOver : function(e, t){
63462         var row;
63463         if((row = this.findRowIndex(t)) !== false){
63464             this.getRowComposite(row).addClass("x-grid-row-over");
63465         }
63466     },
63467
63468     onRowOut : function(e, t){
63469         var row;
63470         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
63471             this.getRowComposite(row).removeClass("x-grid-row-over");
63472         }
63473     },
63474
63475     renderHeaders : function(){
63476         var cm = this.cm;
63477         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
63478         var cb = [], lb = [], sb = [], lsb = [], p = {};
63479         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
63480             p.cellId = "x-grid-hd-0-" + i;
63481             p.splitId = "x-grid-csplit-0-" + i;
63482             p.id = cm.getColumnId(i);
63483             p.value = cm.getColumnHeader(i) || "";
63484             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
63485             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
63486             if(!cm.isLocked(i)){
63487                 cb[cb.length] = ct.apply(p);
63488                 sb[sb.length] = st.apply(p);
63489             }else{
63490                 lb[lb.length] = ct.apply(p);
63491                 lsb[lsb.length] = st.apply(p);
63492             }
63493         }
63494         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
63495                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
63496     },
63497
63498     updateHeaders : function(){
63499         var html = this.renderHeaders();
63500         this.lockedHd.update(html[0]);
63501         this.mainHd.update(html[1]);
63502     },
63503
63504     /**
63505      * Focuses the specified row.
63506      * @param {Number} row The row index
63507      */
63508     focusRow : function(row)
63509     {
63510         //Roo.log('GridView.focusRow');
63511         var x = this.scroller.dom.scrollLeft;
63512         this.focusCell(row, 0, false);
63513         this.scroller.dom.scrollLeft = x;
63514     },
63515
63516     /**
63517      * Focuses the specified cell.
63518      * @param {Number} row The row index
63519      * @param {Number} col The column index
63520      * @param {Boolean} hscroll false to disable horizontal scrolling
63521      */
63522     focusCell : function(row, col, hscroll)
63523     {
63524         //Roo.log('GridView.focusCell');
63525         var el = this.ensureVisible(row, col, hscroll);
63526         this.focusEl.alignTo(el, "tl-tl");
63527         if(Roo.isGecko){
63528             this.focusEl.focus();
63529         }else{
63530             this.focusEl.focus.defer(1, this.focusEl);
63531         }
63532     },
63533
63534     /**
63535      * Scrolls the specified cell into view
63536      * @param {Number} row The row index
63537      * @param {Number} col The column index
63538      * @param {Boolean} hscroll false to disable horizontal scrolling
63539      */
63540     ensureVisible : function(row, col, hscroll)
63541     {
63542         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
63543         //return null; //disable for testing.
63544         if(typeof row != "number"){
63545             row = row.rowIndex;
63546         }
63547         if(row < 0 && row >= this.ds.getCount()){
63548             return  null;
63549         }
63550         col = (col !== undefined ? col : 0);
63551         var cm = this.grid.colModel;
63552         while(cm.isHidden(col)){
63553             col++;
63554         }
63555
63556         var el = this.getCell(row, col);
63557         if(!el){
63558             return null;
63559         }
63560         var c = this.scroller.dom;
63561
63562         var ctop = parseInt(el.offsetTop, 10);
63563         var cleft = parseInt(el.offsetLeft, 10);
63564         var cbot = ctop + el.offsetHeight;
63565         var cright = cleft + el.offsetWidth;
63566         
63567         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
63568         var stop = parseInt(c.scrollTop, 10);
63569         var sleft = parseInt(c.scrollLeft, 10);
63570         var sbot = stop + ch;
63571         var sright = sleft + c.clientWidth;
63572         /*
63573         Roo.log('GridView.ensureVisible:' +
63574                 ' ctop:' + ctop +
63575                 ' c.clientHeight:' + c.clientHeight +
63576                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
63577                 ' stop:' + stop +
63578                 ' cbot:' + cbot +
63579                 ' sbot:' + sbot +
63580                 ' ch:' + ch  
63581                 );
63582         */
63583         if(ctop < stop){
63584             c.scrollTop = ctop;
63585             //Roo.log("set scrolltop to ctop DISABLE?");
63586         }else if(cbot > sbot){
63587             //Roo.log("set scrolltop to cbot-ch");
63588             c.scrollTop = cbot-ch;
63589         }
63590         
63591         if(hscroll !== false){
63592             if(cleft < sleft){
63593                 c.scrollLeft = cleft;
63594             }else if(cright > sright){
63595                 c.scrollLeft = cright-c.clientWidth;
63596             }
63597         }
63598          
63599         return el;
63600     },
63601
63602     updateColumns : function(){
63603         this.grid.stopEditing();
63604         var cm = this.grid.colModel, colIds = this.getColumnIds();
63605         //var totalWidth = cm.getTotalWidth();
63606         var pos = 0;
63607         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
63608             //if(cm.isHidden(i)) continue;
63609             var w = cm.getColumnWidth(i);
63610             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
63611             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
63612         }
63613         this.updateSplitters();
63614     },
63615
63616     generateRules : function(cm){
63617         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
63618         Roo.util.CSS.removeStyleSheet(rulesId);
63619         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
63620             var cid = cm.getColumnId(i);
63621             var align = '';
63622             if(cm.config[i].align){
63623                 align = 'text-align:'+cm.config[i].align+';';
63624             }
63625             var hidden = '';
63626             if(cm.isHidden(i)){
63627                 hidden = 'display:none;';
63628             }
63629             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
63630             ruleBuf.push(
63631                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
63632                     this.hdSelector, cid, " {\n", align, width, "}\n",
63633                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
63634                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
63635         }
63636         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
63637     },
63638
63639     updateSplitters : function(){
63640         var cm = this.cm, s = this.getSplitters();
63641         if(s){ // splitters not created yet
63642             var pos = 0, locked = true;
63643             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
63644                 if(cm.isHidden(i)) {
63645                     continue;
63646                 }
63647                 var w = cm.getColumnWidth(i); // make sure it's a number
63648                 if(!cm.isLocked(i) && locked){
63649                     pos = 0;
63650                     locked = false;
63651                 }
63652                 pos += w;
63653                 s[i].style.left = (pos-this.splitOffset) + "px";
63654             }
63655         }
63656     },
63657
63658     handleHiddenChange : function(colModel, colIndex, hidden){
63659         if(hidden){
63660             this.hideColumn(colIndex);
63661         }else{
63662             this.unhideColumn(colIndex);
63663         }
63664     },
63665
63666     hideColumn : function(colIndex){
63667         var cid = this.getColumnId(colIndex);
63668         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
63669         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
63670         if(Roo.isSafari){
63671             this.updateHeaders();
63672         }
63673         this.updateSplitters();
63674         this.layout();
63675     },
63676
63677     unhideColumn : function(colIndex){
63678         var cid = this.getColumnId(colIndex);
63679         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
63680         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
63681
63682         if(Roo.isSafari){
63683             this.updateHeaders();
63684         }
63685         this.updateSplitters();
63686         this.layout();
63687     },
63688
63689     insertRows : function(dm, firstRow, lastRow, isUpdate){
63690         if(firstRow == 0 && lastRow == dm.getCount()-1){
63691             this.refresh();
63692         }else{
63693             if(!isUpdate){
63694                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
63695             }
63696             var s = this.getScrollState();
63697             var markup = this.renderRows(firstRow, lastRow);
63698             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
63699             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
63700             this.restoreScroll(s);
63701             if(!isUpdate){
63702                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
63703                 this.syncRowHeights(firstRow, lastRow);
63704                 this.stripeRows(firstRow);
63705                 this.layout();
63706             }
63707         }
63708     },
63709
63710     bufferRows : function(markup, target, index){
63711         var before = null, trows = target.rows, tbody = target.tBodies[0];
63712         if(index < trows.length){
63713             before = trows[index];
63714         }
63715         var b = document.createElement("div");
63716         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
63717         var rows = b.firstChild.rows;
63718         for(var i = 0, len = rows.length; i < len; i++){
63719             if(before){
63720                 tbody.insertBefore(rows[0], before);
63721             }else{
63722                 tbody.appendChild(rows[0]);
63723             }
63724         }
63725         b.innerHTML = "";
63726         b = null;
63727     },
63728
63729     deleteRows : function(dm, firstRow, lastRow){
63730         if(dm.getRowCount()<1){
63731             this.fireEvent("beforerefresh", this);
63732             this.mainBody.update("");
63733             this.lockedBody.update("");
63734             this.fireEvent("refresh", this);
63735         }else{
63736             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
63737             var bt = this.getBodyTable();
63738             var tbody = bt.firstChild;
63739             var rows = bt.rows;
63740             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
63741                 tbody.removeChild(rows[firstRow]);
63742             }
63743             this.stripeRows(firstRow);
63744             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
63745         }
63746     },
63747
63748     updateRows : function(dataSource, firstRow, lastRow){
63749         var s = this.getScrollState();
63750         this.refresh();
63751         this.restoreScroll(s);
63752     },
63753
63754     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
63755         if(!noRefresh){
63756            this.refresh();
63757         }
63758         this.updateHeaderSortState();
63759     },
63760
63761     getScrollState : function(){
63762         
63763         var sb = this.scroller.dom;
63764         return {left: sb.scrollLeft, top: sb.scrollTop};
63765     },
63766
63767     stripeRows : function(startRow){
63768         if(!this.grid.stripeRows || this.ds.getCount() < 1){
63769             return;
63770         }
63771         startRow = startRow || 0;
63772         var rows = this.getBodyTable().rows;
63773         var lrows = this.getLockedTable().rows;
63774         var cls = ' x-grid-row-alt ';
63775         for(var i = startRow, len = rows.length; i < len; i++){
63776             var row = rows[i], lrow = lrows[i];
63777             var isAlt = ((i+1) % 2 == 0);
63778             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
63779             if(isAlt == hasAlt){
63780                 continue;
63781             }
63782             if(isAlt){
63783                 row.className += " x-grid-row-alt";
63784             }else{
63785                 row.className = row.className.replace("x-grid-row-alt", "");
63786             }
63787             if(lrow){
63788                 lrow.className = row.className;
63789             }
63790         }
63791     },
63792
63793     restoreScroll : function(state){
63794         //Roo.log('GridView.restoreScroll');
63795         var sb = this.scroller.dom;
63796         sb.scrollLeft = state.left;
63797         sb.scrollTop = state.top;
63798         this.syncScroll();
63799     },
63800
63801     syncScroll : function(){
63802         //Roo.log('GridView.syncScroll');
63803         var sb = this.scroller.dom;
63804         var sh = this.mainHd.dom;
63805         var bs = this.mainBody.dom;
63806         var lv = this.lockedBody.dom;
63807         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
63808         lv.scrollTop = bs.scrollTop = sb.scrollTop;
63809     },
63810
63811     handleScroll : function(e){
63812         this.syncScroll();
63813         var sb = this.scroller.dom;
63814         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
63815         e.stopEvent();
63816     },
63817
63818     handleWheel : function(e){
63819         var d = e.getWheelDelta();
63820         this.scroller.dom.scrollTop -= d*22;
63821         // set this here to prevent jumpy scrolling on large tables
63822         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
63823         e.stopEvent();
63824     },
63825
63826     renderRows : function(startRow, endRow){
63827         // pull in all the crap needed to render rows
63828         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
63829         var colCount = cm.getColumnCount();
63830
63831         if(ds.getCount() < 1){
63832             return ["", ""];
63833         }
63834
63835         // build a map for all the columns
63836         var cs = [];
63837         for(var i = 0; i < colCount; i++){
63838             var name = cm.getDataIndex(i);
63839             cs[i] = {
63840                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
63841                 renderer : cm.getRenderer(i),
63842                 id : cm.getColumnId(i),
63843                 locked : cm.isLocked(i),
63844                 has_editor : cm.isCellEditable(i)
63845             };
63846         }
63847
63848         startRow = startRow || 0;
63849         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
63850
63851         // records to render
63852         var rs = ds.getRange(startRow, endRow);
63853
63854         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
63855     },
63856
63857     // As much as I hate to duplicate code, this was branched because FireFox really hates
63858     // [].join("") on strings. The performance difference was substantial enough to
63859     // branch this function
63860     doRender : Roo.isGecko ?
63861             function(cs, rs, ds, startRow, colCount, stripe){
63862                 var ts = this.templates, ct = ts.cell, rt = ts.row;
63863                 // buffers
63864                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
63865                 
63866                 var hasListener = this.grid.hasListener('rowclass');
63867                 var rowcfg = {};
63868                 for(var j = 0, len = rs.length; j < len; j++){
63869                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
63870                     for(var i = 0; i < colCount; i++){
63871                         c = cs[i];
63872                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
63873                         p.id = c.id;
63874                         p.css = p.attr = "";
63875                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
63876                         if(p.value == undefined || p.value === "") {
63877                             p.value = "&#160;";
63878                         }
63879                         if(c.has_editor){
63880                             p.css += ' x-grid-editable-cell';
63881                         }
63882                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
63883                             p.css +=  ' x-grid-dirty-cell';
63884                         }
63885                         var markup = ct.apply(p);
63886                         if(!c.locked){
63887                             cb+= markup;
63888                         }else{
63889                             lcb+= markup;
63890                         }
63891                     }
63892                     var alt = [];
63893                     if(stripe && ((rowIndex+1) % 2 == 0)){
63894                         alt.push("x-grid-row-alt")
63895                     }
63896                     if(r.dirty){
63897                         alt.push(  " x-grid-dirty-row");
63898                     }
63899                     rp.cells = lcb;
63900                     if(this.getRowClass){
63901                         alt.push(this.getRowClass(r, rowIndex));
63902                     }
63903                     if (hasListener) {
63904                         rowcfg = {
63905                              
63906                             record: r,
63907                             rowIndex : rowIndex,
63908                             rowClass : ''
63909                         };
63910                         this.grid.fireEvent('rowclass', this, rowcfg);
63911                         alt.push(rowcfg.rowClass);
63912                     }
63913                     rp.alt = alt.join(" ");
63914                     lbuf+= rt.apply(rp);
63915                     rp.cells = cb;
63916                     buf+=  rt.apply(rp);
63917                 }
63918                 return [lbuf, buf];
63919             } :
63920             function(cs, rs, ds, startRow, colCount, stripe){
63921                 var ts = this.templates, ct = ts.cell, rt = ts.row;
63922                 // buffers
63923                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
63924                 var hasListener = this.grid.hasListener('rowclass');
63925  
63926                 var rowcfg = {};
63927                 for(var j = 0, len = rs.length; j < len; j++){
63928                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
63929                     for(var i = 0; i < colCount; i++){
63930                         c = cs[i];
63931                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
63932                         p.id = c.id;
63933                         p.css = p.attr = "";
63934                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
63935                         if(p.value == undefined || p.value === "") {
63936                             p.value = "&#160;";
63937                         }
63938                         //Roo.log(c);
63939                          if(c.has_editor){
63940                             p.css += ' x-grid-editable-cell';
63941                         }
63942                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
63943                             p.css += ' x-grid-dirty-cell' 
63944                         }
63945                         
63946                         var markup = ct.apply(p);
63947                         if(!c.locked){
63948                             cb[cb.length] = markup;
63949                         }else{
63950                             lcb[lcb.length] = markup;
63951                         }
63952                     }
63953                     var alt = [];
63954                     if(stripe && ((rowIndex+1) % 2 == 0)){
63955                         alt.push( "x-grid-row-alt");
63956                     }
63957                     if(r.dirty){
63958                         alt.push(" x-grid-dirty-row");
63959                     }
63960                     rp.cells = lcb;
63961                     if(this.getRowClass){
63962                         alt.push( this.getRowClass(r, rowIndex));
63963                     }
63964                     if (hasListener) {
63965                         rowcfg = {
63966                              
63967                             record: r,
63968                             rowIndex : rowIndex,
63969                             rowClass : ''
63970                         };
63971                         this.grid.fireEvent('rowclass', this, rowcfg);
63972                         alt.push(rowcfg.rowClass);
63973                     }
63974                     
63975                     rp.alt = alt.join(" ");
63976                     rp.cells = lcb.join("");
63977                     lbuf[lbuf.length] = rt.apply(rp);
63978                     rp.cells = cb.join("");
63979                     buf[buf.length] =  rt.apply(rp);
63980                 }
63981                 return [lbuf.join(""), buf.join("")];
63982             },
63983
63984     renderBody : function(){
63985         var markup = this.renderRows();
63986         var bt = this.templates.body;
63987         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
63988     },
63989
63990     /**
63991      * Refreshes the grid
63992      * @param {Boolean} headersToo
63993      */
63994     refresh : function(headersToo){
63995         this.fireEvent("beforerefresh", this);
63996         this.grid.stopEditing();
63997         var result = this.renderBody();
63998         this.lockedBody.update(result[0]);
63999         this.mainBody.update(result[1]);
64000         if(headersToo === true){
64001             this.updateHeaders();
64002             this.updateColumns();
64003             this.updateSplitters();
64004             this.updateHeaderSortState();
64005         }
64006         this.syncRowHeights();
64007         this.layout();
64008         this.fireEvent("refresh", this);
64009     },
64010
64011     handleColumnMove : function(cm, oldIndex, newIndex){
64012         this.indexMap = null;
64013         var s = this.getScrollState();
64014         this.refresh(true);
64015         this.restoreScroll(s);
64016         this.afterMove(newIndex);
64017     },
64018
64019     afterMove : function(colIndex){
64020         if(this.enableMoveAnim && Roo.enableFx){
64021             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
64022         }
64023         // if multisort - fix sortOrder, and reload..
64024         if (this.grid.dataSource.multiSort) {
64025             // the we can call sort again..
64026             var dm = this.grid.dataSource;
64027             var cm = this.grid.colModel;
64028             var so = [];
64029             for(var i = 0; i < cm.config.length; i++ ) {
64030                 
64031                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
64032                     continue; // dont' bother, it's not in sort list or being set.
64033                 }
64034                 
64035                 so.push(cm.config[i].dataIndex);
64036             };
64037             dm.sortOrder = so;
64038             dm.load(dm.lastOptions);
64039             
64040             
64041         }
64042         
64043     },
64044
64045     updateCell : function(dm, rowIndex, dataIndex){
64046         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
64047         if(typeof colIndex == "undefined"){ // not present in grid
64048             return;
64049         }
64050         var cm = this.grid.colModel;
64051         var cell = this.getCell(rowIndex, colIndex);
64052         var cellText = this.getCellText(rowIndex, colIndex);
64053
64054         var p = {
64055             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
64056             id : cm.getColumnId(colIndex),
64057             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
64058         };
64059         var renderer = cm.getRenderer(colIndex);
64060         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
64061         if(typeof val == "undefined" || val === "") {
64062             val = "&#160;";
64063         }
64064         cellText.innerHTML = val;
64065         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
64066         this.syncRowHeights(rowIndex, rowIndex);
64067     },
64068
64069     calcColumnWidth : function(colIndex, maxRowsToMeasure){
64070         var maxWidth = 0;
64071         if(this.grid.autoSizeHeaders){
64072             var h = this.getHeaderCellMeasure(colIndex);
64073             maxWidth = Math.max(maxWidth, h.scrollWidth);
64074         }
64075         var tb, index;
64076         if(this.cm.isLocked(colIndex)){
64077             tb = this.getLockedTable();
64078             index = colIndex;
64079         }else{
64080             tb = this.getBodyTable();
64081             index = colIndex - this.cm.getLockedCount();
64082         }
64083         if(tb && tb.rows){
64084             var rows = tb.rows;
64085             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
64086             for(var i = 0; i < stopIndex; i++){
64087                 var cell = rows[i].childNodes[index].firstChild;
64088                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
64089             }
64090         }
64091         return maxWidth + /*margin for error in IE*/ 5;
64092     },
64093     /**
64094      * Autofit a column to its content.
64095      * @param {Number} colIndex
64096      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
64097      */
64098      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
64099          if(this.cm.isHidden(colIndex)){
64100              return; // can't calc a hidden column
64101          }
64102         if(forceMinSize){
64103             var cid = this.cm.getColumnId(colIndex);
64104             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
64105            if(this.grid.autoSizeHeaders){
64106                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
64107            }
64108         }
64109         var newWidth = this.calcColumnWidth(colIndex);
64110         this.cm.setColumnWidth(colIndex,
64111             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
64112         if(!suppressEvent){
64113             this.grid.fireEvent("columnresize", colIndex, newWidth);
64114         }
64115     },
64116
64117     /**
64118      * Autofits all columns to their content and then expands to fit any extra space in the grid
64119      */
64120      autoSizeColumns : function(){
64121         var cm = this.grid.colModel;
64122         var colCount = cm.getColumnCount();
64123         for(var i = 0; i < colCount; i++){
64124             this.autoSizeColumn(i, true, true);
64125         }
64126         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
64127             this.fitColumns();
64128         }else{
64129             this.updateColumns();
64130             this.layout();
64131         }
64132     },
64133
64134     /**
64135      * Autofits all columns to the grid's width proportionate with their current size
64136      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
64137      */
64138     fitColumns : function(reserveScrollSpace){
64139         var cm = this.grid.colModel;
64140         var colCount = cm.getColumnCount();
64141         var cols = [];
64142         var width = 0;
64143         var i, w;
64144         for (i = 0; i < colCount; i++){
64145             if(!cm.isHidden(i) && !cm.isFixed(i)){
64146                 w = cm.getColumnWidth(i);
64147                 cols.push(i);
64148                 cols.push(w);
64149                 width += w;
64150             }
64151         }
64152         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
64153         if(reserveScrollSpace){
64154             avail -= 17;
64155         }
64156         var frac = (avail - cm.getTotalWidth())/width;
64157         while (cols.length){
64158             w = cols.pop();
64159             i = cols.pop();
64160             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
64161         }
64162         this.updateColumns();
64163         this.layout();
64164     },
64165
64166     onRowSelect : function(rowIndex){
64167         var row = this.getRowComposite(rowIndex);
64168         row.addClass("x-grid-row-selected");
64169     },
64170
64171     onRowDeselect : function(rowIndex){
64172         var row = this.getRowComposite(rowIndex);
64173         row.removeClass("x-grid-row-selected");
64174     },
64175
64176     onCellSelect : function(row, col){
64177         var cell = this.getCell(row, col);
64178         if(cell){
64179             Roo.fly(cell).addClass("x-grid-cell-selected");
64180         }
64181     },
64182
64183     onCellDeselect : function(row, col){
64184         var cell = this.getCell(row, col);
64185         if(cell){
64186             Roo.fly(cell).removeClass("x-grid-cell-selected");
64187         }
64188     },
64189
64190     updateHeaderSortState : function(){
64191         
64192         // sort state can be single { field: xxx, direction : yyy}
64193         // or   { xxx=>ASC , yyy : DESC ..... }
64194         
64195         var mstate = {};
64196         if (!this.ds.multiSort) { 
64197             var state = this.ds.getSortState();
64198             if(!state){
64199                 return;
64200             }
64201             mstate[state.field] = state.direction;
64202             // FIXME... - this is not used here.. but might be elsewhere..
64203             this.sortState = state;
64204             
64205         } else {
64206             mstate = this.ds.sortToggle;
64207         }
64208         //remove existing sort classes..
64209         
64210         var sc = this.sortClasses;
64211         var hds = this.el.select(this.headerSelector).removeClass(sc);
64212         
64213         for(var f in mstate) {
64214         
64215             var sortColumn = this.cm.findColumnIndex(f);
64216             
64217             if(sortColumn != -1){
64218                 var sortDir = mstate[f];        
64219                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
64220             }
64221         }
64222         
64223          
64224         
64225     },
64226
64227
64228     handleHeaderClick : function(g, index,e){
64229         
64230         Roo.log("header click");
64231         
64232         if (Roo.isTouch) {
64233             // touch events on header are handled by context
64234             this.handleHdCtx(g,index,e);
64235             return;
64236         }
64237         
64238         
64239         if(this.headersDisabled){
64240             return;
64241         }
64242         var dm = g.dataSource, cm = g.colModel;
64243         if(!cm.isSortable(index)){
64244             return;
64245         }
64246         g.stopEditing();
64247         
64248         if (dm.multiSort) {
64249             // update the sortOrder
64250             var so = [];
64251             for(var i = 0; i < cm.config.length; i++ ) {
64252                 
64253                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
64254                     continue; // dont' bother, it's not in sort list or being set.
64255                 }
64256                 
64257                 so.push(cm.config[i].dataIndex);
64258             };
64259             dm.sortOrder = so;
64260         }
64261         
64262         
64263         dm.sort(cm.getDataIndex(index));
64264     },
64265
64266
64267     destroy : function(){
64268         if(this.colMenu){
64269             this.colMenu.removeAll();
64270             Roo.menu.MenuMgr.unregister(this.colMenu);
64271             this.colMenu.getEl().remove();
64272             delete this.colMenu;
64273         }
64274         if(this.hmenu){
64275             this.hmenu.removeAll();
64276             Roo.menu.MenuMgr.unregister(this.hmenu);
64277             this.hmenu.getEl().remove();
64278             delete this.hmenu;
64279         }
64280         if(this.grid.enableColumnMove){
64281             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
64282             if(dds){
64283                 for(var dd in dds){
64284                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
64285                         var elid = dds[dd].dragElId;
64286                         dds[dd].unreg();
64287                         Roo.get(elid).remove();
64288                     } else if(dds[dd].config.isTarget){
64289                         dds[dd].proxyTop.remove();
64290                         dds[dd].proxyBottom.remove();
64291                         dds[dd].unreg();
64292                     }
64293                     if(Roo.dd.DDM.locationCache[dd]){
64294                         delete Roo.dd.DDM.locationCache[dd];
64295                     }
64296                 }
64297                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
64298             }
64299         }
64300         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
64301         this.bind(null, null);
64302         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
64303     },
64304
64305     handleLockChange : function(){
64306         this.refresh(true);
64307     },
64308
64309     onDenyColumnLock : function(){
64310
64311     },
64312
64313     onDenyColumnHide : function(){
64314
64315     },
64316
64317     handleHdMenuClick : function(item){
64318         var index = this.hdCtxIndex;
64319         var cm = this.cm, ds = this.ds;
64320         switch(item.id){
64321             case "asc":
64322                 ds.sort(cm.getDataIndex(index), "ASC");
64323                 break;
64324             case "desc":
64325                 ds.sort(cm.getDataIndex(index), "DESC");
64326                 break;
64327             case "lock":
64328                 var lc = cm.getLockedCount();
64329                 if(cm.getColumnCount(true) <= lc+1){
64330                     this.onDenyColumnLock();
64331                     return;
64332                 }
64333                 if(lc != index){
64334                     cm.setLocked(index, true, true);
64335                     cm.moveColumn(index, lc);
64336                     this.grid.fireEvent("columnmove", index, lc);
64337                 }else{
64338                     cm.setLocked(index, true);
64339                 }
64340             break;
64341             case "unlock":
64342                 var lc = cm.getLockedCount();
64343                 if((lc-1) != index){
64344                     cm.setLocked(index, false, true);
64345                     cm.moveColumn(index, lc-1);
64346                     this.grid.fireEvent("columnmove", index, lc-1);
64347                 }else{
64348                     cm.setLocked(index, false);
64349                 }
64350             break;
64351             case 'wider': // used to expand cols on touch..
64352             case 'narrow':
64353                 var cw = cm.getColumnWidth(index);
64354                 cw += (item.id == 'wider' ? 1 : -1) * 50;
64355                 cw = Math.max(0, cw);
64356                 cw = Math.min(cw,4000);
64357                 cm.setColumnWidth(index, cw);
64358                 break;
64359                 
64360             default:
64361                 index = cm.getIndexById(item.id.substr(4));
64362                 if(index != -1){
64363                     if(item.checked && cm.getColumnCount(true) <= 1){
64364                         this.onDenyColumnHide();
64365                         return false;
64366                     }
64367                     cm.setHidden(index, item.checked);
64368                 }
64369         }
64370         return true;
64371     },
64372
64373     beforeColMenuShow : function(){
64374         var cm = this.cm,  colCount = cm.getColumnCount();
64375         this.colMenu.removeAll();
64376         
64377         var items = [];
64378         for(var i = 0; i < colCount; i++){
64379             items.push({
64380                 id: "col-"+cm.getColumnId(i),
64381                 text: cm.getColumnHeader(i),
64382                 checked: !cm.isHidden(i),
64383                 hideOnClick:false
64384             });
64385         }
64386         
64387         if (this.grid.sortColMenu) {
64388             items.sort(function(a,b) {
64389                 if (a.text == b.text) {
64390                     return 0;
64391                 }
64392                 return a.text.toUpperCase() > b.text.toUpperCase() ? 1 : -1;
64393             });
64394         }
64395         
64396         for(var i = 0; i < colCount; i++){
64397             this.colMenu.add(new Roo.menu.CheckItem(items[i]));
64398         }
64399     },
64400
64401     handleHdCtx : function(g, index, e){
64402         e.stopEvent();
64403         var hd = this.getHeaderCell(index);
64404         this.hdCtxIndex = index;
64405         var ms = this.hmenu.items, cm = this.cm;
64406         ms.get("asc").setDisabled(!cm.isSortable(index));
64407         ms.get("desc").setDisabled(!cm.isSortable(index));
64408         if(this.grid.enableColLock !== false){
64409             ms.get("lock").setDisabled(cm.isLocked(index));
64410             ms.get("unlock").setDisabled(!cm.isLocked(index));
64411         }
64412         this.hmenu.show(hd, "tl-bl");
64413     },
64414
64415     handleHdOver : function(e){
64416         var hd = this.findHeaderCell(e.getTarget());
64417         if(hd && !this.headersDisabled){
64418             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
64419                this.fly(hd).addClass("x-grid-hd-over");
64420             }
64421         }
64422     },
64423
64424     handleHdOut : function(e){
64425         var hd = this.findHeaderCell(e.getTarget());
64426         if(hd){
64427             this.fly(hd).removeClass("x-grid-hd-over");
64428         }
64429     },
64430
64431     handleSplitDblClick : function(e, t){
64432         var i = this.getCellIndex(t);
64433         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
64434             this.autoSizeColumn(i, true);
64435             this.layout();
64436         }
64437     },
64438
64439     render : function(){
64440
64441         var cm = this.cm;
64442         var colCount = cm.getColumnCount();
64443
64444         if(this.grid.monitorWindowResize === true){
64445             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
64446         }
64447         var header = this.renderHeaders();
64448         var body = this.templates.body.apply({rows:""});
64449         var html = this.templates.master.apply({
64450             lockedBody: body,
64451             body: body,
64452             lockedHeader: header[0],
64453             header: header[1]
64454         });
64455
64456         //this.updateColumns();
64457
64458         this.grid.getGridEl().dom.innerHTML = html;
64459
64460         this.initElements();
64461         
64462         // a kludge to fix the random scolling effect in webkit
64463         this.el.on("scroll", function() {
64464             this.el.dom.scrollTop=0; // hopefully not recursive..
64465         },this);
64466
64467         this.scroller.on("scroll", this.handleScroll, this);
64468         this.lockedBody.on("mousewheel", this.handleWheel, this);
64469         this.mainBody.on("mousewheel", this.handleWheel, this);
64470
64471         this.mainHd.on("mouseover", this.handleHdOver, this);
64472         this.mainHd.on("mouseout", this.handleHdOut, this);
64473         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
64474                 {delegate: "."+this.splitClass});
64475
64476         this.lockedHd.on("mouseover", this.handleHdOver, this);
64477         this.lockedHd.on("mouseout", this.handleHdOut, this);
64478         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
64479                 {delegate: "."+this.splitClass});
64480
64481         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
64482             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
64483         }
64484
64485         this.updateSplitters();
64486
64487         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
64488             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
64489             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
64490         }
64491
64492         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
64493             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
64494             this.hmenu.add(
64495                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
64496                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
64497             );
64498             if(this.grid.enableColLock !== false){
64499                 this.hmenu.add('-',
64500                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
64501                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
64502                 );
64503             }
64504             if (Roo.isTouch) {
64505                  this.hmenu.add('-',
64506                     {id:"wider", text: this.columnsWiderText},
64507                     {id:"narrow", text: this.columnsNarrowText }
64508                 );
64509                 
64510                  
64511             }
64512             
64513             if(this.grid.enableColumnHide !== false){
64514
64515                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
64516                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
64517                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
64518
64519                 this.hmenu.add('-',
64520                     {id:"columns", text: this.columnsText, menu: this.colMenu}
64521                 );
64522             }
64523             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
64524
64525             this.grid.on("headercontextmenu", this.handleHdCtx, this);
64526         }
64527
64528         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
64529             this.dd = new Roo.grid.GridDragZone(this.grid, {
64530                 ddGroup : this.grid.ddGroup || 'GridDD'
64531             });
64532             
64533         }
64534
64535         /*
64536         for(var i = 0; i < colCount; i++){
64537             if(cm.isHidden(i)){
64538                 this.hideColumn(i);
64539             }
64540             if(cm.config[i].align){
64541                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
64542                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
64543             }
64544         }*/
64545         
64546         this.updateHeaderSortState();
64547
64548         this.beforeInitialResize();
64549         this.layout(true);
64550
64551         // two part rendering gives faster view to the user
64552         this.renderPhase2.defer(1, this);
64553     },
64554
64555     renderPhase2 : function(){
64556         // render the rows now
64557         this.refresh();
64558         if(this.grid.autoSizeColumns){
64559             this.autoSizeColumns();
64560         }
64561     },
64562
64563     beforeInitialResize : function(){
64564
64565     },
64566
64567     onColumnSplitterMoved : function(i, w){
64568         this.userResized = true;
64569         var cm = this.grid.colModel;
64570         cm.setColumnWidth(i, w, true);
64571         var cid = cm.getColumnId(i);
64572         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
64573         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
64574         this.updateSplitters();
64575         this.layout();
64576         this.grid.fireEvent("columnresize", i, w);
64577     },
64578
64579     syncRowHeights : function(startIndex, endIndex){
64580         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
64581             startIndex = startIndex || 0;
64582             var mrows = this.getBodyTable().rows;
64583             var lrows = this.getLockedTable().rows;
64584             var len = mrows.length-1;
64585             endIndex = Math.min(endIndex || len, len);
64586             for(var i = startIndex; i <= endIndex; i++){
64587                 var m = mrows[i], l = lrows[i];
64588                 var h = Math.max(m.offsetHeight, l.offsetHeight);
64589                 m.style.height = l.style.height = h + "px";
64590             }
64591         }
64592     },
64593
64594     layout : function(initialRender, is2ndPass)
64595     {
64596         var g = this.grid;
64597         var auto = g.autoHeight;
64598         var scrollOffset = 16;
64599         var c = g.getGridEl(), cm = this.cm,
64600                 expandCol = g.autoExpandColumn,
64601                 gv = this;
64602         //c.beginMeasure();
64603
64604         if(!c.dom.offsetWidth){ // display:none?
64605             if(initialRender){
64606                 this.lockedWrap.show();
64607                 this.mainWrap.show();
64608             }
64609             return;
64610         }
64611
64612         var hasLock = this.cm.isLocked(0);
64613
64614         var tbh = this.headerPanel.getHeight();
64615         var bbh = this.footerPanel.getHeight();
64616
64617         if(auto){
64618             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
64619             var newHeight = ch + c.getBorderWidth("tb");
64620             if(g.maxHeight){
64621                 newHeight = Math.min(g.maxHeight, newHeight);
64622             }
64623             c.setHeight(newHeight);
64624         }
64625
64626         if(g.autoWidth){
64627             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
64628         }
64629
64630         var s = this.scroller;
64631
64632         var csize = c.getSize(true);
64633
64634         this.el.setSize(csize.width, csize.height);
64635
64636         this.headerPanel.setWidth(csize.width);
64637         this.footerPanel.setWidth(csize.width);
64638
64639         var hdHeight = this.mainHd.getHeight();
64640         var vw = csize.width;
64641         var vh = csize.height - (tbh + bbh);
64642
64643         s.setSize(vw, vh);
64644
64645         var bt = this.getBodyTable();
64646         
64647         if(cm.getLockedCount() == cm.config.length){
64648             bt = this.getLockedTable();
64649         }
64650         
64651         var ltWidth = hasLock ?
64652                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
64653
64654         var scrollHeight = bt.offsetHeight;
64655         var scrollWidth = ltWidth + bt.offsetWidth;
64656         var vscroll = false, hscroll = false;
64657
64658         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
64659
64660         var lw = this.lockedWrap, mw = this.mainWrap;
64661         var lb = this.lockedBody, mb = this.mainBody;
64662
64663         setTimeout(function(){
64664             var t = s.dom.offsetTop;
64665             var w = s.dom.clientWidth,
64666                 h = s.dom.clientHeight;
64667
64668             lw.setTop(t);
64669             lw.setSize(ltWidth, h);
64670
64671             mw.setLeftTop(ltWidth, t);
64672             mw.setSize(w-ltWidth, h);
64673
64674             lb.setHeight(h-hdHeight);
64675             mb.setHeight(h-hdHeight);
64676
64677             if(is2ndPass !== true && !gv.userResized && expandCol){
64678                 // high speed resize without full column calculation
64679                 
64680                 var ci = cm.getIndexById(expandCol);
64681                 if (ci < 0) {
64682                     ci = cm.findColumnIndex(expandCol);
64683                 }
64684                 ci = Math.max(0, ci); // make sure it's got at least the first col.
64685                 var expandId = cm.getColumnId(ci);
64686                 var  tw = cm.getTotalWidth(false);
64687                 var currentWidth = cm.getColumnWidth(ci);
64688                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
64689                 if(currentWidth != cw){
64690                     cm.setColumnWidth(ci, cw, true);
64691                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
64692                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
64693                     gv.updateSplitters();
64694                     gv.layout(false, true);
64695                 }
64696             }
64697
64698             if(initialRender){
64699                 lw.show();
64700                 mw.show();
64701             }
64702             //c.endMeasure();
64703         }, 10);
64704     },
64705
64706     onWindowResize : function(){
64707         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
64708             return;
64709         }
64710         this.layout();
64711     },
64712
64713     appendFooter : function(parentEl){
64714         return null;
64715     },
64716
64717     sortAscText : "Sort Ascending",
64718     sortDescText : "Sort Descending",
64719     lockText : "Lock Column",
64720     unlockText : "Unlock Column",
64721     columnsText : "Columns",
64722  
64723     columnsWiderText : "Wider",
64724     columnsNarrowText : "Thinner"
64725 });
64726
64727
64728 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
64729     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
64730     this.proxy.el.addClass('x-grid3-col-dd');
64731 };
64732
64733 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
64734     handleMouseDown : function(e){
64735
64736     },
64737
64738     callHandleMouseDown : function(e){
64739         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
64740     }
64741 });
64742 /*
64743  * Based on:
64744  * Ext JS Library 1.1.1
64745  * Copyright(c) 2006-2007, Ext JS, LLC.
64746  *
64747  * Originally Released Under LGPL - original licence link has changed is not relivant.
64748  *
64749  * Fork - LGPL
64750  * <script type="text/javascript">
64751  */
64752  /**
64753  * @extends Roo.dd.DDProxy
64754  * @class Roo.grid.SplitDragZone
64755  * Support for Column Header resizing
64756  * @constructor
64757  * @param {Object} config
64758  */
64759 // private
64760 // This is a support class used internally by the Grid components
64761 Roo.grid.SplitDragZone = function(grid, hd, hd2){
64762     this.grid = grid;
64763     this.view = grid.getView();
64764     this.proxy = this.view.resizeProxy;
64765     Roo.grid.SplitDragZone.superclass.constructor.call(
64766         this,
64767         hd, // ID
64768         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
64769         {  // CONFIG
64770             dragElId : Roo.id(this.proxy.dom),
64771             resizeFrame:false
64772         }
64773     );
64774     
64775     this.setHandleElId(Roo.id(hd));
64776     if (hd2 !== false) {
64777         this.setOuterHandleElId(Roo.id(hd2));
64778     }
64779     
64780     this.scroll = false;
64781 };
64782 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
64783     fly: Roo.Element.fly,
64784
64785     b4StartDrag : function(x, y){
64786         this.view.headersDisabled = true;
64787         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
64788                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
64789         );
64790         this.proxy.setHeight(h);
64791         
64792         // for old system colWidth really stored the actual width?
64793         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
64794         // which in reality did not work.. - it worked only for fixed sizes
64795         // for resizable we need to use actual sizes.
64796         var w = this.cm.getColumnWidth(this.cellIndex);
64797         if (!this.view.mainWrap) {
64798             // bootstrap.
64799             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
64800         }
64801         
64802         
64803         
64804         // this was w-this.grid.minColumnWidth;
64805         // doesnt really make sense? - w = thie curren width or the rendered one?
64806         var minw = Math.max(w-this.grid.minColumnWidth, 0);
64807         this.resetConstraints();
64808         this.setXConstraint(minw, 1000);
64809         this.setYConstraint(0, 0);
64810         this.minX = x - minw;
64811         this.maxX = x + 1000;
64812         this.startPos = x;
64813         if (!this.view.mainWrap) { // this is Bootstrap code..
64814             this.getDragEl().style.display='block';
64815         }
64816         
64817         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
64818     },
64819
64820
64821     handleMouseDown : function(e){
64822         ev = Roo.EventObject.setEvent(e);
64823         var t = this.fly(ev.getTarget());
64824         if(t.hasClass("x-grid-split")){
64825             this.cellIndex = this.view.getCellIndex(t.dom);
64826             this.split = t.dom;
64827             this.cm = this.grid.colModel;
64828             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
64829                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
64830             }
64831         }
64832     },
64833
64834     endDrag : function(e){
64835         this.view.headersDisabled = false;
64836         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
64837         var diff = endX - this.startPos;
64838         // 
64839         var w = this.cm.getColumnWidth(this.cellIndex);
64840         if (!this.view.mainWrap) {
64841             w = 0;
64842         }
64843         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
64844     },
64845
64846     autoOffset : function(){
64847         this.setDelta(0,0);
64848     }
64849 });/*
64850  * Based on:
64851  * Ext JS Library 1.1.1
64852  * Copyright(c) 2006-2007, Ext JS, LLC.
64853  *
64854  * Originally Released Under LGPL - original licence link has changed is not relivant.
64855  *
64856  * Fork - LGPL
64857  * <script type="text/javascript">
64858  */
64859  
64860 // private
64861 // This is a support class used internally by the Grid components
64862 Roo.grid.GridDragZone = function(grid, config){
64863     this.view = grid.getView();
64864     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
64865     if(this.view.lockedBody){
64866         this.setHandleElId(Roo.id(this.view.mainBody.dom));
64867         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
64868     }
64869     this.scroll = false;
64870     this.grid = grid;
64871     this.ddel = document.createElement('div');
64872     this.ddel.className = 'x-grid-dd-wrap';
64873 };
64874
64875 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
64876     ddGroup : "GridDD",
64877
64878     getDragData : function(e){
64879         var t = Roo.lib.Event.getTarget(e);
64880         var rowIndex = this.view.findRowIndex(t);
64881         var sm = this.grid.selModel;
64882             
64883         //Roo.log(rowIndex);
64884         
64885         if (sm.getSelectedCell) {
64886             // cell selection..
64887             if (!sm.getSelectedCell()) {
64888                 return false;
64889             }
64890             if (rowIndex != sm.getSelectedCell()[0]) {
64891                 return false;
64892             }
64893         
64894         }
64895         if (sm.getSelections && sm.getSelections().length < 1) {
64896             return false;
64897         }
64898         
64899         
64900         // before it used to all dragging of unseleted... - now we dont do that.
64901         if(rowIndex !== false){
64902             
64903             // if editorgrid.. 
64904             
64905             
64906             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
64907                
64908             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
64909               //  
64910             //}
64911             if (e.hasModifier()){
64912                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
64913             }
64914             
64915             Roo.log("getDragData");
64916             
64917             return {
64918                 grid: this.grid,
64919                 ddel: this.ddel,
64920                 rowIndex: rowIndex,
64921                 selections: sm.getSelections ? sm.getSelections() : (
64922                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
64923             };
64924         }
64925         return false;
64926     },
64927     
64928     
64929     onInitDrag : function(e){
64930         var data = this.dragData;
64931         this.ddel.innerHTML = this.grid.getDragDropText();
64932         this.proxy.update(this.ddel);
64933         // fire start drag?
64934     },
64935
64936     afterRepair : function(){
64937         this.dragging = false;
64938     },
64939
64940     getRepairXY : function(e, data){
64941         return false;
64942     },
64943
64944     onEndDrag : function(data, e){
64945         // fire end drag?
64946     },
64947
64948     onValidDrop : function(dd, e, id){
64949         // fire drag drop?
64950         this.hideProxy();
64951     },
64952
64953     beforeInvalidDrop : function(e, id){
64954
64955     }
64956 });/*
64957  * Based on:
64958  * Ext JS Library 1.1.1
64959  * Copyright(c) 2006-2007, Ext JS, LLC.
64960  *
64961  * Originally Released Under LGPL - original licence link has changed is not relivant.
64962  *
64963  * Fork - LGPL
64964  * <script type="text/javascript">
64965  */
64966  
64967
64968 /**
64969  * @class Roo.grid.ColumnModel
64970  * @extends Roo.util.Observable
64971  * This is the default implementation of a ColumnModel used by the Grid. It defines
64972  * the columns in the grid.
64973  * <br>Usage:<br>
64974  <pre><code>
64975  var colModel = new Roo.grid.ColumnModel([
64976         {header: "Ticker", width: 60, sortable: true, locked: true},
64977         {header: "Company Name", width: 150, sortable: true},
64978         {header: "Market Cap.", width: 100, sortable: true},
64979         {header: "$ Sales", width: 100, sortable: true, renderer: money},
64980         {header: "Employees", width: 100, sortable: true, resizable: false}
64981  ]);
64982  </code></pre>
64983  * <p>
64984  
64985  * The config options listed for this class are options which may appear in each
64986  * individual column definition.
64987  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
64988  * @constructor
64989  * @param {Object} config An Array of column config objects. See this class's
64990  * config objects for details.
64991 */
64992 Roo.grid.ColumnModel = function(config){
64993         /**
64994      * The config passed into the constructor
64995      */
64996     this.config = []; //config;
64997     this.lookup = {};
64998
64999     // if no id, create one
65000     // if the column does not have a dataIndex mapping,
65001     // map it to the order it is in the config
65002     for(var i = 0, len = config.length; i < len; i++){
65003         this.addColumn(config[i]);
65004         
65005     }
65006
65007     /**
65008      * The width of columns which have no width specified (defaults to 100)
65009      * @type Number
65010      */
65011     this.defaultWidth = 100;
65012
65013     /**
65014      * Default sortable of columns which have no sortable specified (defaults to false)
65015      * @type Boolean
65016      */
65017     this.defaultSortable = false;
65018
65019     this.addEvents({
65020         /**
65021              * @event widthchange
65022              * Fires when the width of a column changes.
65023              * @param {ColumnModel} this
65024              * @param {Number} columnIndex The column index
65025              * @param {Number} newWidth The new width
65026              */
65027             "widthchange": true,
65028         /**
65029              * @event headerchange
65030              * Fires when the text of a header changes.
65031              * @param {ColumnModel} this
65032              * @param {Number} columnIndex The column index
65033              * @param {Number} newText The new header text
65034              */
65035             "headerchange": true,
65036         /**
65037              * @event hiddenchange
65038              * Fires when a column is hidden or "unhidden".
65039              * @param {ColumnModel} this
65040              * @param {Number} columnIndex The column index
65041              * @param {Boolean} hidden true if hidden, false otherwise
65042              */
65043             "hiddenchange": true,
65044             /**
65045          * @event columnmoved
65046          * Fires when a column is moved.
65047          * @param {ColumnModel} this
65048          * @param {Number} oldIndex
65049          * @param {Number} newIndex
65050          */
65051         "columnmoved" : true,
65052         /**
65053          * @event columlockchange
65054          * Fires when a column's locked state is changed
65055          * @param {ColumnModel} this
65056          * @param {Number} colIndex
65057          * @param {Boolean} locked true if locked
65058          */
65059         "columnlockchange" : true
65060     });
65061     Roo.grid.ColumnModel.superclass.constructor.call(this);
65062 };
65063 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
65064     /**
65065      * @cfg {String} header [required] The header text to display in the Grid view.
65066      */
65067         /**
65068      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
65069      */
65070         /**
65071      * @cfg {String} smHeader Header at Bootsrap Small width
65072      */
65073         /**
65074      * @cfg {String} mdHeader Header at Bootsrap Medium width
65075      */
65076         /**
65077      * @cfg {String} lgHeader Header at Bootsrap Large width
65078      */
65079         /**
65080      * @cfg {String} xlHeader Header at Bootsrap extra Large width
65081      */
65082     /**
65083      * @cfg {String} dataIndex  The name of the field in the grid's {@link Roo.data.Store}'s
65084      * {@link Roo.data.Record} definition from which to draw the column's value. If not
65085      * specified, the column's index is used as an index into the Record's data Array.
65086      */
65087     /**
65088      * @cfg {Number} width  The initial width in pixels of the column. Using this
65089      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
65090      */
65091     /**
65092      * @cfg {Boolean} sortable True if sorting is to be allowed on this column.
65093      * Defaults to the value of the {@link #defaultSortable} property.
65094      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
65095      */
65096     /**
65097      * @cfg {Boolean} locked  True to lock the column in place while scrolling the Grid.  Defaults to false.
65098      */
65099     /**
65100      * @cfg {Boolean} fixed  True if the column width cannot be changed.  Defaults to false.
65101      */
65102     /**
65103      * @cfg {Boolean} resizable  False to disable column resizing. Defaults to true.
65104      */
65105     /**
65106      * @cfg {Boolean} hidden  True to hide the column. Defaults to false.
65107      */
65108     /**
65109      * @cfg {Function} renderer A function used to generate HTML markup for a cell
65110      * given the cell's data value. See {@link #setRenderer}. If not specified, the
65111      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
65112      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
65113      */
65114        /**
65115      * @cfg {Roo.grid.GridEditor} editor  For grid editors - returns the grid editor 
65116      */
65117     /**
65118      * @cfg {String} align (left|right) Set the CSS text-align property of the column.  Defaults to undefined (left).
65119      */
65120     /**
65121      * @cfg {String} valign (top|bottom|middle) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined (middle)
65122      */
65123     /**
65124      * @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)
65125      */
65126     /**
65127      * @cfg {String} tooltip mouse over tooltip text
65128      */
65129     /**
65130      * @cfg {Number} xs  can be '0' for hidden at this size (number less than 12)
65131      */
65132     /**
65133      * @cfg {Number} sm can be '0' for hidden at this size (number less than 12)
65134      */
65135     /**
65136      * @cfg {Number} md can be '0' for hidden at this size (number less than 12)
65137      */
65138     /**
65139      * @cfg {Number} lg   can be '0' for hidden at this size (number less than 12)
65140      */
65141         /**
65142      * @cfg {Number} xl   can be '0' for hidden at this size (number less than 12)
65143      */
65144     /**
65145      * Returns the id of the column at the specified index.
65146      * @param {Number} index The column index
65147      * @return {String} the id
65148      */
65149     getColumnId : function(index){
65150         return this.config[index].id;
65151     },
65152
65153     /**
65154      * Returns the column for a specified id.
65155      * @param {String} id The column id
65156      * @return {Object} the column
65157      */
65158     getColumnById : function(id){
65159         return this.lookup[id];
65160     },
65161
65162     
65163     /**
65164      * Returns the column Object for a specified dataIndex.
65165      * @param {String} dataIndex The column dataIndex
65166      * @return {Object|Boolean} the column or false if not found
65167      */
65168     getColumnByDataIndex: function(dataIndex){
65169         var index = this.findColumnIndex(dataIndex);
65170         return index > -1 ? this.config[index] : false;
65171     },
65172     
65173     /**
65174      * Returns the index for a specified column id.
65175      * @param {String} id The column id
65176      * @return {Number} the index, or -1 if not found
65177      */
65178     getIndexById : function(id){
65179         for(var i = 0, len = this.config.length; i < len; i++){
65180             if(this.config[i].id == id){
65181                 return i;
65182             }
65183         }
65184         return -1;
65185     },
65186     
65187     /**
65188      * Returns the index for a specified column dataIndex.
65189      * @param {String} dataIndex The column dataIndex
65190      * @return {Number} the index, or -1 if not found
65191      */
65192     
65193     findColumnIndex : function(dataIndex){
65194         for(var i = 0, len = this.config.length; i < len; i++){
65195             if(this.config[i].dataIndex == dataIndex){
65196                 return i;
65197             }
65198         }
65199         return -1;
65200     },
65201     
65202     
65203     moveColumn : function(oldIndex, newIndex){
65204         var c = this.config[oldIndex];
65205         this.config.splice(oldIndex, 1);
65206         this.config.splice(newIndex, 0, c);
65207         this.dataMap = null;
65208         this.fireEvent("columnmoved", this, oldIndex, newIndex);
65209     },
65210
65211     isLocked : function(colIndex){
65212         return this.config[colIndex].locked === true;
65213     },
65214
65215     setLocked : function(colIndex, value, suppressEvent){
65216         if(this.isLocked(colIndex) == value){
65217             return;
65218         }
65219         this.config[colIndex].locked = value;
65220         if(!suppressEvent){
65221             this.fireEvent("columnlockchange", this, colIndex, value);
65222         }
65223     },
65224
65225     getTotalLockedWidth : function(){
65226         var totalWidth = 0;
65227         for(var i = 0; i < this.config.length; i++){
65228             if(this.isLocked(i) && !this.isHidden(i)){
65229                 this.totalWidth += this.getColumnWidth(i);
65230             }
65231         }
65232         return totalWidth;
65233     },
65234
65235     getLockedCount : function(){
65236         for(var i = 0, len = this.config.length; i < len; i++){
65237             if(!this.isLocked(i)){
65238                 return i;
65239             }
65240         }
65241         
65242         return this.config.length;
65243     },
65244
65245     /**
65246      * Returns the number of columns.
65247      * @return {Number}
65248      */
65249     getColumnCount : function(visibleOnly){
65250         if(visibleOnly === true){
65251             var c = 0;
65252             for(var i = 0, len = this.config.length; i < len; i++){
65253                 if(!this.isHidden(i)){
65254                     c++;
65255                 }
65256             }
65257             return c;
65258         }
65259         return this.config.length;
65260     },
65261
65262     /**
65263      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
65264      * @param {Function} fn
65265      * @param {Object} scope (optional)
65266      * @return {Array} result
65267      */
65268     getColumnsBy : function(fn, scope){
65269         var r = [];
65270         for(var i = 0, len = this.config.length; i < len; i++){
65271             var c = this.config[i];
65272             if(fn.call(scope||this, c, i) === true){
65273                 r[r.length] = c;
65274             }
65275         }
65276         return r;
65277     },
65278
65279     /**
65280      * Returns true if the specified column is sortable.
65281      * @param {Number} col The column index
65282      * @return {Boolean}
65283      */
65284     isSortable : function(col){
65285         if(typeof this.config[col].sortable == "undefined"){
65286             return this.defaultSortable;
65287         }
65288         return this.config[col].sortable;
65289     },
65290
65291     /**
65292      * Returns the rendering (formatting) function defined for the column.
65293      * @param {Number} col The column index.
65294      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
65295      */
65296     getRenderer : function(col){
65297         if(!this.config[col].renderer){
65298             return Roo.grid.ColumnModel.defaultRenderer;
65299         }
65300         return this.config[col].renderer;
65301     },
65302
65303     /**
65304      * Sets the rendering (formatting) function for a column.
65305      * @param {Number} col The column index
65306      * @param {Function} fn The function to use to process the cell's raw data
65307      * to return HTML markup for the grid view. The render function is called with
65308      * the following parameters:<ul>
65309      * <li>Data value.</li>
65310      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
65311      * <li>css A CSS style string to apply to the table cell.</li>
65312      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
65313      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
65314      * <li>Row index</li>
65315      * <li>Column index</li>
65316      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
65317      */
65318     setRenderer : function(col, fn){
65319         this.config[col].renderer = fn;
65320     },
65321
65322     /**
65323      * Returns the width for the specified column.
65324      * @param {Number} col The column index
65325      * @param (optional) {String} gridSize bootstrap width size.
65326      * @return {Number}
65327      */
65328     getColumnWidth : function(col, gridSize)
65329         {
65330                 var cfg = this.config[col];
65331                 
65332                 if (typeof(gridSize) == 'undefined') {
65333                         return cfg.width * 1 || this.defaultWidth;
65334                 }
65335                 if (gridSize === false) { // if we set it..
65336                         return cfg.width || false;
65337                 }
65338                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
65339                 
65340                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
65341                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
65342                                 continue;
65343                         }
65344                         return cfg[ sizes[i] ];
65345                 }
65346                 return 1;
65347                 
65348     },
65349
65350     /**
65351      * Sets the width for a column.
65352      * @param {Number} col The column index
65353      * @param {Number} width The new width
65354      */
65355     setColumnWidth : function(col, width, suppressEvent){
65356         this.config[col].width = width;
65357         this.totalWidth = null;
65358         if(!suppressEvent){
65359              this.fireEvent("widthchange", this, col, width);
65360         }
65361     },
65362
65363     /**
65364      * Returns the total width of all columns.
65365      * @param {Boolean} includeHidden True to include hidden column widths
65366      * @return {Number}
65367      */
65368     getTotalWidth : function(includeHidden){
65369         if(!this.totalWidth){
65370             this.totalWidth = 0;
65371             for(var i = 0, len = this.config.length; i < len; i++){
65372                 if(includeHidden || !this.isHidden(i)){
65373                     this.totalWidth += this.getColumnWidth(i);
65374                 }
65375             }
65376         }
65377         return this.totalWidth;
65378     },
65379
65380     /**
65381      * Returns the header for the specified column.
65382      * @param {Number} col The column index
65383      * @return {String}
65384      */
65385     getColumnHeader : function(col){
65386         return this.config[col].header;
65387     },
65388
65389     /**
65390      * Sets the header for a column.
65391      * @param {Number} col The column index
65392      * @param {String} header The new header
65393      */
65394     setColumnHeader : function(col, header){
65395         this.config[col].header = header;
65396         this.fireEvent("headerchange", this, col, header);
65397     },
65398
65399     /**
65400      * Returns the tooltip for the specified column.
65401      * @param {Number} col The column index
65402      * @return {String}
65403      */
65404     getColumnTooltip : function(col){
65405             return this.config[col].tooltip;
65406     },
65407     /**
65408      * Sets the tooltip for a column.
65409      * @param {Number} col The column index
65410      * @param {String} tooltip The new tooltip
65411      */
65412     setColumnTooltip : function(col, tooltip){
65413             this.config[col].tooltip = tooltip;
65414     },
65415
65416     /**
65417      * Returns the dataIndex for the specified column.
65418      * @param {Number} col The column index
65419      * @return {Number}
65420      */
65421     getDataIndex : function(col){
65422         return this.config[col].dataIndex;
65423     },
65424
65425     /**
65426      * Sets the dataIndex for a column.
65427      * @param {Number} col The column index
65428      * @param {Number} dataIndex The new dataIndex
65429      */
65430     setDataIndex : function(col, dataIndex){
65431         this.config[col].dataIndex = dataIndex;
65432     },
65433
65434     
65435     
65436     /**
65437      * Returns true if the cell is editable.
65438      * @param {Number} colIndex The column index
65439      * @param {Number} rowIndex The row index - this is nto actually used..?
65440      * @return {Boolean}
65441      */
65442     isCellEditable : function(colIndex, rowIndex){
65443         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
65444     },
65445
65446     /**
65447      * Returns the editor defined for the cell/column.
65448      * return false or null to disable editing.
65449      * @param {Number} colIndex The column index
65450      * @param {Number} rowIndex The row index
65451      * @return {Object}
65452      */
65453     getCellEditor : function(colIndex, rowIndex){
65454         return this.config[colIndex].editor;
65455     },
65456
65457     /**
65458      * Sets if a column is editable.
65459      * @param {Number} col The column index
65460      * @param {Boolean} editable True if the column is editable
65461      */
65462     setEditable : function(col, editable){
65463         this.config[col].editable = editable;
65464     },
65465
65466
65467     /**
65468      * Returns true if the column is hidden.
65469      * @param {Number} colIndex The column index
65470      * @return {Boolean}
65471      */
65472     isHidden : function(colIndex){
65473         return this.config[colIndex].hidden;
65474     },
65475
65476
65477     /**
65478      * Returns true if the column width cannot be changed
65479      */
65480     isFixed : function(colIndex){
65481         return this.config[colIndex].fixed;
65482     },
65483
65484     /**
65485      * Returns true if the column can be resized
65486      * @return {Boolean}
65487      */
65488     isResizable : function(colIndex){
65489         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
65490     },
65491     /**
65492      * Sets if a column is hidden.
65493      * @param {Number} colIndex The column index
65494      * @param {Boolean} hidden True if the column is hidden
65495      */
65496     setHidden : function(colIndex, hidden){
65497         this.config[colIndex].hidden = hidden;
65498         this.totalWidth = null;
65499         this.fireEvent("hiddenchange", this, colIndex, hidden);
65500     },
65501
65502     /**
65503      * Sets the editor for a column.
65504      * @param {Number} col The column index
65505      * @param {Object} editor The editor object
65506      */
65507     setEditor : function(col, editor){
65508         this.config[col].editor = editor;
65509     },
65510     /**
65511      * Add a column (experimental...) - defaults to adding to the end..
65512      * @param {Object} config 
65513     */
65514     addColumn : function(c)
65515     {
65516     
65517         var i = this.config.length;
65518         this.config[i] = c;
65519         
65520         if(typeof c.dataIndex == "undefined"){
65521             c.dataIndex = i;
65522         }
65523         if(typeof c.renderer == "string"){
65524             c.renderer = Roo.util.Format[c.renderer];
65525         }
65526         if(typeof c.id == "undefined"){
65527             c.id = Roo.id();
65528         }
65529         if(c.editor && c.editor.xtype){
65530             c.editor  = Roo.factory(c.editor, Roo.grid);
65531         }
65532         if(c.editor && c.editor.isFormField){
65533             c.editor = new Roo.grid.GridEditor(c.editor);
65534         }
65535         this.lookup[c.id] = c;
65536     }
65537     
65538 });
65539
65540 Roo.grid.ColumnModel.defaultRenderer = function(value)
65541 {
65542     if(typeof value == "object") {
65543         return value;
65544     }
65545         if(typeof value == "string" && value.length < 1){
65546             return "&#160;";
65547         }
65548     
65549         return String.format("{0}", value);
65550 };
65551
65552 // Alias for backwards compatibility
65553 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
65554 /*
65555  * Based on:
65556  * Ext JS Library 1.1.1
65557  * Copyright(c) 2006-2007, Ext JS, LLC.
65558  *
65559  * Originally Released Under LGPL - original licence link has changed is not relivant.
65560  *
65561  * Fork - LGPL
65562  * <script type="text/javascript">
65563  */
65564
65565 /**
65566  * @class Roo.grid.AbstractSelectionModel
65567  * @extends Roo.util.Observable
65568  * @abstract
65569  * Abstract base class for grid SelectionModels.  It provides the interface that should be
65570  * implemented by descendant classes.  This class should not be directly instantiated.
65571  * @constructor
65572  */
65573 Roo.grid.AbstractSelectionModel = function(){
65574     this.locked = false;
65575     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
65576 };
65577
65578 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
65579     /** @ignore Called by the grid automatically. Do not call directly. */
65580     init : function(grid){
65581         this.grid = grid;
65582         this.initEvents();
65583     },
65584
65585     /**
65586      * Locks the selections.
65587      */
65588     lock : function(){
65589         this.locked = true;
65590     },
65591
65592     /**
65593      * Unlocks the selections.
65594      */
65595     unlock : function(){
65596         this.locked = false;
65597     },
65598
65599     /**
65600      * Returns true if the selections are locked.
65601      * @return {Boolean}
65602      */
65603     isLocked : function(){
65604         return this.locked;
65605     }
65606 });/*
65607  * Based on:
65608  * Ext JS Library 1.1.1
65609  * Copyright(c) 2006-2007, Ext JS, LLC.
65610  *
65611  * Originally Released Under LGPL - original licence link has changed is not relivant.
65612  *
65613  * Fork - LGPL
65614  * <script type="text/javascript">
65615  */
65616 /**
65617  * @extends Roo.grid.AbstractSelectionModel
65618  * @class Roo.grid.RowSelectionModel
65619  * The default SelectionModel used by {@link Roo.grid.Grid}.
65620  * It supports multiple selections and keyboard selection/navigation. 
65621  * @constructor
65622  * @param {Object} config
65623  */
65624 Roo.grid.RowSelectionModel = function(config){
65625     Roo.apply(this, config);
65626     this.selections = new Roo.util.MixedCollection(false, function(o){
65627         return o.id;
65628     });
65629
65630     this.last = false;
65631     this.lastActive = false;
65632
65633     this.addEvents({
65634         /**
65635         * @event selectionchange
65636         * Fires when the selection changes
65637         * @param {SelectionModel} this
65638         */
65639        "selectionchange" : true,
65640        /**
65641         * @event afterselectionchange
65642         * Fires after the selection changes (eg. by key press or clicking)
65643         * @param {SelectionModel} this
65644         */
65645        "afterselectionchange" : true,
65646        /**
65647         * @event beforerowselect
65648         * Fires when a row is selected being selected, return false to cancel.
65649         * @param {SelectionModel} this
65650         * @param {Number} rowIndex The selected index
65651         * @param {Boolean} keepExisting False if other selections will be cleared
65652         */
65653        "beforerowselect" : true,
65654        /**
65655         * @event rowselect
65656         * Fires when a row is selected.
65657         * @param {SelectionModel} this
65658         * @param {Number} rowIndex The selected index
65659         * @param {Roo.data.Record} r The record
65660         */
65661        "rowselect" : true,
65662        /**
65663         * @event rowdeselect
65664         * Fires when a row is deselected.
65665         * @param {SelectionModel} this
65666         * @param {Number} rowIndex The selected index
65667         */
65668         "rowdeselect" : true
65669     });
65670     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
65671     this.locked = false;
65672 };
65673
65674 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
65675     /**
65676      * @cfg {Boolean} singleSelect
65677      * True to allow selection of only one row at a time (defaults to false)
65678      */
65679     singleSelect : false,
65680
65681     // private
65682     initEvents : function(){
65683
65684         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
65685             this.grid.on("mousedown", this.handleMouseDown, this);
65686         }else{ // allow click to work like normal
65687             this.grid.on("rowclick", this.handleDragableRowClick, this);
65688         }
65689         // bootstrap does not have a view..
65690         var view = this.grid.view ? this.grid.view : this.grid;
65691         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
65692             "up" : function(e){
65693                 if(!e.shiftKey){
65694                     this.selectPrevious(e.shiftKey);
65695                 }else if(this.last !== false && this.lastActive !== false){
65696                     var last = this.last;
65697                     this.selectRange(this.last,  this.lastActive-1);
65698                     view.focusRow(this.lastActive);
65699                     if(last !== false){
65700                         this.last = last;
65701                     }
65702                 }else{
65703                     this.selectFirstRow();
65704                 }
65705                 this.fireEvent("afterselectionchange", this);
65706             },
65707             "down" : function(e){
65708                 if(!e.shiftKey){
65709                     this.selectNext(e.shiftKey);
65710                 }else if(this.last !== false && this.lastActive !== false){
65711                     var last = this.last;
65712                     this.selectRange(this.last,  this.lastActive+1);
65713                     view.focusRow(this.lastActive);
65714                     if(last !== false){
65715                         this.last = last;
65716                     }
65717                 }else{
65718                     this.selectFirstRow();
65719                 }
65720                 this.fireEvent("afterselectionchange", this);
65721             },
65722             scope: this
65723         });
65724
65725          
65726         view.on("refresh", this.onRefresh, this);
65727         view.on("rowupdated", this.onRowUpdated, this);
65728         view.on("rowremoved", this.onRemove, this);
65729     },
65730
65731     // private
65732     onRefresh : function(){
65733         var ds = this.grid.ds, i, v = this.grid.view;
65734         var s = this.selections;
65735         s.each(function(r){
65736             if((i = ds.indexOfId(r.id)) != -1){
65737                 v.onRowSelect(i);
65738                 s.add(ds.getAt(i)); // updating the selection relate data
65739             }else{
65740                 s.remove(r);
65741             }
65742         });
65743     },
65744
65745     // private
65746     onRemove : function(v, index, r){
65747         this.selections.remove(r);
65748     },
65749
65750     // private
65751     onRowUpdated : function(v, index, r){
65752         if(this.isSelected(r)){
65753             v.onRowSelect(index);
65754         }
65755     },
65756
65757     /**
65758      * Select records.
65759      * @param {Array} records The records to select
65760      * @param {Boolean} keepExisting (optional) True to keep existing selections
65761      */
65762     selectRecords : function(records, keepExisting){
65763         if(!keepExisting){
65764             this.clearSelections();
65765         }
65766         var ds = this.grid.ds;
65767         for(var i = 0, len = records.length; i < len; i++){
65768             this.selectRow(ds.indexOf(records[i]), true);
65769         }
65770     },
65771
65772     /**
65773      * Gets the number of selected rows.
65774      * @return {Number}
65775      */
65776     getCount : function(){
65777         return this.selections.length;
65778     },
65779
65780     /**
65781      * Selects the first row in the grid.
65782      */
65783     selectFirstRow : function(){
65784         this.selectRow(0);
65785     },
65786
65787     /**
65788      * Select the last row.
65789      * @param {Boolean} keepExisting (optional) True to keep existing selections
65790      */
65791     selectLastRow : function(keepExisting){
65792         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
65793     },
65794
65795     /**
65796      * Selects the row immediately following the last selected row.
65797      * @param {Boolean} keepExisting (optional) True to keep existing selections
65798      */
65799     selectNext : function(keepExisting){
65800         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
65801             this.selectRow(this.last+1, keepExisting);
65802             var view = this.grid.view ? this.grid.view : this.grid;
65803             view.focusRow(this.last);
65804         }
65805     },
65806
65807     /**
65808      * Selects the row that precedes the last selected row.
65809      * @param {Boolean} keepExisting (optional) True to keep existing selections
65810      */
65811     selectPrevious : function(keepExisting){
65812         if(this.last){
65813             this.selectRow(this.last-1, keepExisting);
65814             var view = this.grid.view ? this.grid.view : this.grid;
65815             view.focusRow(this.last);
65816         }
65817     },
65818
65819     /**
65820      * Returns the selected records
65821      * @return {Array} Array of selected records
65822      */
65823     getSelections : function(){
65824         return [].concat(this.selections.items);
65825     },
65826
65827     /**
65828      * Returns the first selected record.
65829      * @return {Record}
65830      */
65831     getSelected : function(){
65832         return this.selections.itemAt(0);
65833     },
65834
65835
65836     /**
65837      * Clears all selections.
65838      */
65839     clearSelections : function(fast){
65840         if(this.locked) {
65841             return;
65842         }
65843         if(fast !== true){
65844             var ds = this.grid.ds;
65845             var s = this.selections;
65846             s.each(function(r){
65847                 this.deselectRow(ds.indexOfId(r.id));
65848             }, this);
65849             s.clear();
65850         }else{
65851             this.selections.clear();
65852         }
65853         this.last = false;
65854     },
65855
65856
65857     /**
65858      * Selects all rows.
65859      */
65860     selectAll : function(){
65861         if(this.locked) {
65862             return;
65863         }
65864         this.selections.clear();
65865         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
65866             this.selectRow(i, true);
65867         }
65868     },
65869
65870     /**
65871      * Returns True if there is a selection.
65872      * @return {Boolean}
65873      */
65874     hasSelection : function(){
65875         return this.selections.length > 0;
65876     },
65877
65878     /**
65879      * Returns True if the specified row is selected.
65880      * @param {Number/Record} record The record or index of the record to check
65881      * @return {Boolean}
65882      */
65883     isSelected : function(index){
65884         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
65885         return (r && this.selections.key(r.id) ? true : false);
65886     },
65887
65888     /**
65889      * Returns True if the specified record id is selected.
65890      * @param {String} id The id of record to check
65891      * @return {Boolean}
65892      */
65893     isIdSelected : function(id){
65894         return (this.selections.key(id) ? true : false);
65895     },
65896
65897     // private
65898     handleMouseDown : function(e, t)
65899     {
65900         var view = this.grid.view ? this.grid.view : this.grid;
65901         var rowIndex;
65902         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
65903             return;
65904         };
65905         if(e.shiftKey && this.last !== false){
65906             var last = this.last;
65907             this.selectRange(last, rowIndex, e.ctrlKey);
65908             this.last = last; // reset the last
65909             view.focusRow(rowIndex);
65910         }else{
65911             var isSelected = this.isSelected(rowIndex);
65912             if(e.button !== 0 && isSelected){
65913                 view.focusRow(rowIndex);
65914             }else if(e.ctrlKey && isSelected){
65915                 this.deselectRow(rowIndex);
65916             }else if(!isSelected){
65917                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
65918                 view.focusRow(rowIndex);
65919             }
65920         }
65921         this.fireEvent("afterselectionchange", this);
65922     },
65923     // private
65924     handleDragableRowClick :  function(grid, rowIndex, e) 
65925     {
65926         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
65927             this.selectRow(rowIndex, false);
65928             var view = this.grid.view ? this.grid.view : this.grid;
65929             view.focusRow(rowIndex);
65930              this.fireEvent("afterselectionchange", this);
65931         }
65932     },
65933     
65934     /**
65935      * Selects multiple rows.
65936      * @param {Array} rows Array of the indexes of the row to select
65937      * @param {Boolean} keepExisting (optional) True to keep existing selections
65938      */
65939     selectRows : function(rows, keepExisting){
65940         if(!keepExisting){
65941             this.clearSelections();
65942         }
65943         for(var i = 0, len = rows.length; i < len; i++){
65944             this.selectRow(rows[i], true);
65945         }
65946     },
65947
65948     /**
65949      * Selects a range of rows. All rows in between startRow and endRow are also selected.
65950      * @param {Number} startRow The index of the first row in the range
65951      * @param {Number} endRow The index of the last row in the range
65952      * @param {Boolean} keepExisting (optional) True to retain existing selections
65953      */
65954     selectRange : function(startRow, endRow, keepExisting){
65955         if(this.locked) {
65956             return;
65957         }
65958         if(!keepExisting){
65959             this.clearSelections();
65960         }
65961         if(startRow <= endRow){
65962             for(var i = startRow; i <= endRow; i++){
65963                 this.selectRow(i, true);
65964             }
65965         }else{
65966             for(var i = startRow; i >= endRow; i--){
65967                 this.selectRow(i, true);
65968             }
65969         }
65970     },
65971
65972     /**
65973      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
65974      * @param {Number} startRow The index of the first row in the range
65975      * @param {Number} endRow The index of the last row in the range
65976      */
65977     deselectRange : function(startRow, endRow, preventViewNotify){
65978         if(this.locked) {
65979             return;
65980         }
65981         for(var i = startRow; i <= endRow; i++){
65982             this.deselectRow(i, preventViewNotify);
65983         }
65984     },
65985
65986     /**
65987      * Selects a row.
65988      * @param {Number} row The index of the row to select
65989      * @param {Boolean} keepExisting (optional) True to keep existing selections
65990      */
65991     selectRow : function(index, keepExisting, preventViewNotify){
65992         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
65993             return;
65994         }
65995         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
65996             if(!keepExisting || this.singleSelect){
65997                 this.clearSelections();
65998             }
65999             var r = this.grid.ds.getAt(index);
66000             this.selections.add(r);
66001             this.last = this.lastActive = index;
66002             if(!preventViewNotify){
66003                 var view = this.grid.view ? this.grid.view : this.grid;
66004                 view.onRowSelect(index);
66005             }
66006             this.fireEvent("rowselect", this, index, r);
66007             this.fireEvent("selectionchange", this);
66008         }
66009     },
66010
66011     /**
66012      * Deselects a row.
66013      * @param {Number} row The index of the row to deselect
66014      */
66015     deselectRow : function(index, preventViewNotify){
66016         if(this.locked) {
66017             return;
66018         }
66019         if(this.last == index){
66020             this.last = false;
66021         }
66022         if(this.lastActive == index){
66023             this.lastActive = false;
66024         }
66025         var r = this.grid.ds.getAt(index);
66026         this.selections.remove(r);
66027         if(!preventViewNotify){
66028             var view = this.grid.view ? this.grid.view : this.grid;
66029             view.onRowDeselect(index);
66030         }
66031         this.fireEvent("rowdeselect", this, index);
66032         this.fireEvent("selectionchange", this);
66033     },
66034
66035     // private
66036     restoreLast : function(){
66037         if(this._last){
66038             this.last = this._last;
66039         }
66040     },
66041
66042     // private
66043     acceptsNav : function(row, col, cm){
66044         return !cm.isHidden(col) && cm.isCellEditable(col, row);
66045     },
66046
66047     // private
66048     onEditorKey : function(field, e){
66049         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
66050         if(k == e.TAB){
66051             e.stopEvent();
66052             ed.completeEdit();
66053             if(e.shiftKey){
66054                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
66055             }else{
66056                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
66057             }
66058         }else if(k == e.ENTER && !e.ctrlKey){
66059             e.stopEvent();
66060             ed.completeEdit();
66061             if(e.shiftKey){
66062                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
66063             }else{
66064                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
66065             }
66066         }else if(k == e.ESC){
66067             ed.cancelEdit();
66068         }
66069         if(newCell){
66070             g.startEditing(newCell[0], newCell[1]);
66071         }
66072     }
66073 });/*
66074  * Based on:
66075  * Ext JS Library 1.1.1
66076  * Copyright(c) 2006-2007, Ext JS, LLC.
66077  *
66078  * Originally Released Under LGPL - original licence link has changed is not relivant.
66079  *
66080  * Fork - LGPL
66081  * <script type="text/javascript">
66082  */
66083 /**
66084  * @class Roo.grid.CellSelectionModel
66085  * @extends Roo.grid.AbstractSelectionModel
66086  * This class provides the basic implementation for cell selection in a grid.
66087  * @constructor
66088  * @param {Object} config The object containing the configuration of this model.
66089  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
66090  */
66091 Roo.grid.CellSelectionModel = function(config){
66092     Roo.apply(this, config);
66093
66094     this.selection = null;
66095
66096     this.addEvents({
66097         /**
66098              * @event beforerowselect
66099              * Fires before a cell is selected.
66100              * @param {SelectionModel} this
66101              * @param {Number} rowIndex The selected row index
66102              * @param {Number} colIndex The selected cell index
66103              */
66104             "beforecellselect" : true,
66105         /**
66106              * @event cellselect
66107              * Fires when a cell is selected.
66108              * @param {SelectionModel} this
66109              * @param {Number} rowIndex The selected row index
66110              * @param {Number} colIndex The selected cell index
66111              */
66112             "cellselect" : true,
66113         /**
66114              * @event selectionchange
66115              * Fires when the active selection changes.
66116              * @param {SelectionModel} this
66117              * @param {Object} selection null for no selection or an object (o) with two properties
66118                 <ul>
66119                 <li>o.record: the record object for the row the selection is in</li>
66120                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
66121                 </ul>
66122              */
66123             "selectionchange" : true,
66124         /**
66125              * @event tabend
66126              * Fires when the tab (or enter) was pressed on the last editable cell
66127              * You can use this to trigger add new row.
66128              * @param {SelectionModel} this
66129              */
66130             "tabend" : true,
66131          /**
66132              * @event beforeeditnext
66133              * Fires before the next editable sell is made active
66134              * You can use this to skip to another cell or fire the tabend
66135              *    if you set cell to false
66136              * @param {Object} eventdata object : { cell : [ row, col ] } 
66137              */
66138             "beforeeditnext" : true
66139     });
66140     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
66141 };
66142
66143 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
66144     
66145     enter_is_tab: false,
66146
66147     /** @ignore */
66148     initEvents : function(){
66149         this.grid.on("mousedown", this.handleMouseDown, this);
66150         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
66151         var view = this.grid.view;
66152         view.on("refresh", this.onViewChange, this);
66153         view.on("rowupdated", this.onRowUpdated, this);
66154         view.on("beforerowremoved", this.clearSelections, this);
66155         view.on("beforerowsinserted", this.clearSelections, this);
66156         if(this.grid.isEditor){
66157             this.grid.on("beforeedit", this.beforeEdit,  this);
66158         }
66159     },
66160
66161         //private
66162     beforeEdit : function(e){
66163         this.select(e.row, e.column, false, true, e.record);
66164     },
66165
66166         //private
66167     onRowUpdated : function(v, index, r){
66168         if(this.selection && this.selection.record == r){
66169             v.onCellSelect(index, this.selection.cell[1]);
66170         }
66171     },
66172
66173         //private
66174     onViewChange : function(){
66175         this.clearSelections(true);
66176     },
66177
66178         /**
66179          * Returns the currently selected cell,.
66180          * @return {Array} The selected cell (row, column) or null if none selected.
66181          */
66182     getSelectedCell : function(){
66183         return this.selection ? this.selection.cell : null;
66184     },
66185
66186     /**
66187      * Clears all selections.
66188      * @param {Boolean} true to prevent the gridview from being notified about the change.
66189      */
66190     clearSelections : function(preventNotify){
66191         var s = this.selection;
66192         if(s){
66193             if(preventNotify !== true){
66194                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
66195             }
66196             this.selection = null;
66197             this.fireEvent("selectionchange", this, null);
66198         }
66199     },
66200
66201     /**
66202      * Returns true if there is a selection.
66203      * @return {Boolean}
66204      */
66205     hasSelection : function(){
66206         return this.selection ? true : false;
66207     },
66208
66209     /** @ignore */
66210     handleMouseDown : function(e, t){
66211         var v = this.grid.getView();
66212         if(this.isLocked()){
66213             return;
66214         };
66215         var row = v.findRowIndex(t);
66216         var cell = v.findCellIndex(t);
66217         if(row !== false && cell !== false){
66218             this.select(row, cell);
66219         }
66220     },
66221
66222     /**
66223      * Selects a cell.
66224      * @param {Number} rowIndex
66225      * @param {Number} collIndex
66226      */
66227     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
66228         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
66229             this.clearSelections();
66230             r = r || this.grid.dataSource.getAt(rowIndex);
66231             this.selection = {
66232                 record : r,
66233                 cell : [rowIndex, colIndex]
66234             };
66235             if(!preventViewNotify){
66236                 var v = this.grid.getView();
66237                 v.onCellSelect(rowIndex, colIndex);
66238                 if(preventFocus !== true){
66239                     v.focusCell(rowIndex, colIndex);
66240                 }
66241             }
66242             this.fireEvent("cellselect", this, rowIndex, colIndex);
66243             this.fireEvent("selectionchange", this, this.selection);
66244         }
66245     },
66246
66247         //private
66248     isSelectable : function(rowIndex, colIndex, cm){
66249         return !cm.isHidden(colIndex);
66250     },
66251
66252     /** @ignore */
66253     handleKeyDown : function(e){
66254         //Roo.log('Cell Sel Model handleKeyDown');
66255         if(!e.isNavKeyPress()){
66256             return;
66257         }
66258         var g = this.grid, s = this.selection;
66259         if(!s){
66260             e.stopEvent();
66261             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
66262             if(cell){
66263                 this.select(cell[0], cell[1]);
66264             }
66265             return;
66266         }
66267         var sm = this;
66268         var walk = function(row, col, step){
66269             return g.walkCells(row, col, step, sm.isSelectable,  sm);
66270         };
66271         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
66272         var newCell;
66273
66274       
66275
66276         switch(k){
66277             case e.TAB:
66278                 // handled by onEditorKey
66279                 if (g.isEditor && g.editing) {
66280                     return;
66281                 }
66282                 if(e.shiftKey) {
66283                     newCell = walk(r, c-1, -1);
66284                 } else {
66285                     newCell = walk(r, c+1, 1);
66286                 }
66287                 break;
66288             
66289             case e.DOWN:
66290                newCell = walk(r+1, c, 1);
66291                 break;
66292             
66293             case e.UP:
66294                 newCell = walk(r-1, c, -1);
66295                 break;
66296             
66297             case e.RIGHT:
66298                 newCell = walk(r, c+1, 1);
66299                 break;
66300             
66301             case e.LEFT:
66302                 newCell = walk(r, c-1, -1);
66303                 break;
66304             
66305             case e.ENTER:
66306                 
66307                 if(g.isEditor && !g.editing){
66308                    g.startEditing(r, c);
66309                    e.stopEvent();
66310                    return;
66311                 }
66312                 
66313                 
66314              break;
66315         };
66316         if(newCell){
66317             this.select(newCell[0], newCell[1]);
66318             e.stopEvent();
66319             
66320         }
66321     },
66322
66323     acceptsNav : function(row, col, cm){
66324         return !cm.isHidden(col) && cm.isCellEditable(col, row);
66325     },
66326     /**
66327      * Selects a cell.
66328      * @param {Number} field (not used) - as it's normally used as a listener
66329      * @param {Number} e - event - fake it by using
66330      *
66331      * var e = Roo.EventObjectImpl.prototype;
66332      * e.keyCode = e.TAB
66333      *
66334      * 
66335      */
66336     onEditorKey : function(field, e){
66337         
66338         var k = e.getKey(),
66339             newCell,
66340             g = this.grid,
66341             ed = g.activeEditor,
66342             forward = false;
66343         ///Roo.log('onEditorKey' + k);
66344         
66345         
66346         if (this.enter_is_tab && k == e.ENTER) {
66347             k = e.TAB;
66348         }
66349         
66350         if(k == e.TAB){
66351             if(e.shiftKey){
66352                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
66353             }else{
66354                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
66355                 forward = true;
66356             }
66357             
66358             e.stopEvent();
66359             
66360         } else if(k == e.ENTER &&  !e.ctrlKey){
66361             ed.completeEdit();
66362             e.stopEvent();
66363             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
66364         
66365                 } else if(k == e.ESC){
66366             ed.cancelEdit();
66367         }
66368                 
66369         if (newCell) {
66370             var ecall = { cell : newCell, forward : forward };
66371             this.fireEvent('beforeeditnext', ecall );
66372             newCell = ecall.cell;
66373                         forward = ecall.forward;
66374         }
66375                 
66376         if(newCell){
66377             //Roo.log('next cell after edit');
66378             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
66379         } else if (forward) {
66380             // tabbed past last
66381             this.fireEvent.defer(100, this, ['tabend',this]);
66382         }
66383     }
66384 });/*
66385  * Based on:
66386  * Ext JS Library 1.1.1
66387  * Copyright(c) 2006-2007, Ext JS, LLC.
66388  *
66389  * Originally Released Under LGPL - original licence link has changed is not relivant.
66390  *
66391  * Fork - LGPL
66392  * <script type="text/javascript">
66393  */
66394  
66395 /**
66396  * @class Roo.grid.EditorGrid
66397  * @extends Roo.grid.Grid
66398  * Class for creating and editable grid.
66399  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
66400  * The container MUST have some type of size defined for the grid to fill. The container will be 
66401  * automatically set to position relative if it isn't already.
66402  * @param {Object} dataSource The data model to bind to
66403  * @param {Object} colModel The column model with info about this grid's columns
66404  */
66405 Roo.grid.EditorGrid = function(container, config){
66406     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
66407     this.getGridEl().addClass("xedit-grid");
66408
66409     if(!this.selModel){
66410         this.selModel = new Roo.grid.CellSelectionModel();
66411     }
66412
66413     this.activeEditor = null;
66414
66415         this.addEvents({
66416             /**
66417              * @event beforeedit
66418              * Fires before cell editing is triggered. The edit event object has the following properties <br />
66419              * <ul style="padding:5px;padding-left:16px;">
66420              * <li>grid - This grid</li>
66421              * <li>record - The record being edited</li>
66422              * <li>field - The field name being edited</li>
66423              * <li>value - The value for the field being edited.</li>
66424              * <li>row - The grid row index</li>
66425              * <li>column - The grid column index</li>
66426              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
66427              * </ul>
66428              * @param {Object} e An edit event (see above for description)
66429              */
66430             "beforeedit" : true,
66431             /**
66432              * @event afteredit
66433              * Fires after a cell is edited. <br />
66434              * <ul style="padding:5px;padding-left:16px;">
66435              * <li>grid - This grid</li>
66436              * <li>record - The record being edited</li>
66437              * <li>field - The field name being edited</li>
66438              * <li>value - The value being set</li>
66439              * <li>originalValue - The original value for the field, before the edit.</li>
66440              * <li>row - The grid row index</li>
66441              * <li>column - The grid column index</li>
66442              * </ul>
66443              * @param {Object} e An edit event (see above for description)
66444              */
66445             "afteredit" : true,
66446             /**
66447              * @event validateedit
66448              * Fires after a cell is edited, but before the value is set in the record. 
66449          * You can use this to modify the value being set in the field, Return false
66450              * to cancel the change. The edit event object has the following properties <br />
66451              * <ul style="padding:5px;padding-left:16px;">
66452          * <li>editor - This editor</li>
66453              * <li>grid - This grid</li>
66454              * <li>record - The record being edited</li>
66455              * <li>field - The field name being edited</li>
66456              * <li>value - The value being set</li>
66457              * <li>originalValue - The original value for the field, before the edit.</li>
66458              * <li>row - The grid row index</li>
66459              * <li>column - The grid column index</li>
66460              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
66461              * </ul>
66462              * @param {Object} e An edit event (see above for description)
66463              */
66464             "validateedit" : true
66465         });
66466     this.on("bodyscroll", this.stopEditing,  this);
66467     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
66468 };
66469
66470 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
66471     /**
66472      * @cfg {Number} clicksToEdit
66473      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
66474      */
66475     clicksToEdit: 2,
66476
66477     // private
66478     isEditor : true,
66479     // private
66480     trackMouseOver: false, // causes very odd FF errors
66481
66482     onCellDblClick : function(g, row, col){
66483         this.startEditing(row, col);
66484     },
66485
66486     onEditComplete : function(ed, value, startValue){
66487         this.editing = false;
66488         this.activeEditor = null;
66489         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
66490         var r = ed.record;
66491         var field = this.colModel.getDataIndex(ed.col);
66492         var e = {
66493             grid: this,
66494             record: r,
66495             field: field,
66496             originalValue: startValue,
66497             value: value,
66498             row: ed.row,
66499             column: ed.col,
66500             cancel:false,
66501             editor: ed
66502         };
66503         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
66504         cell.show();
66505           
66506         if(String(value) !== String(startValue)){
66507             
66508             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
66509                 r.set(field, e.value);
66510                 // if we are dealing with a combo box..
66511                 // then we also set the 'name' colum to be the displayField
66512                 if (ed.field.displayField && ed.field.name) {
66513                     r.set(ed.field.name, ed.field.el.dom.value);
66514                 }
66515                 
66516                 delete e.cancel; //?? why!!!
66517                 this.fireEvent("afteredit", e);
66518             }
66519         } else {
66520             this.fireEvent("afteredit", e); // always fire it!
66521         }
66522         this.view.focusCell(ed.row, ed.col);
66523     },
66524
66525     /**
66526      * Starts editing the specified for the specified row/column
66527      * @param {Number} rowIndex
66528      * @param {Number} colIndex
66529      */
66530     startEditing : function(row, col){
66531         this.stopEditing();
66532         if(this.colModel.isCellEditable(col, row)){
66533             this.view.ensureVisible(row, col, true);
66534           
66535             var r = this.dataSource.getAt(row);
66536             var field = this.colModel.getDataIndex(col);
66537             var cell = Roo.get(this.view.getCell(row,col));
66538             var e = {
66539                 grid: this,
66540                 record: r,
66541                 field: field,
66542                 value: r.data[field],
66543                 row: row,
66544                 column: col,
66545                 cancel:false 
66546             };
66547             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
66548                 this.editing = true;
66549                 var ed = this.colModel.getCellEditor(col, row);
66550                 
66551                 if (!ed) {
66552                     return;
66553                 }
66554                 if(!ed.rendered){
66555                     ed.render(ed.parentEl || document.body);
66556                 }
66557                 ed.field.reset();
66558                
66559                 cell.hide();
66560                 
66561                 (function(){ // complex but required for focus issues in safari, ie and opera
66562                     ed.row = row;
66563                     ed.col = col;
66564                     ed.record = r;
66565                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
66566                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
66567                     this.activeEditor = ed;
66568                     var v = r.data[field];
66569                     ed.startEdit(this.view.getCell(row, col), v);
66570                     // combo's with 'displayField and name set
66571                     if (ed.field.displayField && ed.field.name) {
66572                         ed.field.el.dom.value = r.data[ed.field.name];
66573                     }
66574                     
66575                     
66576                 }).defer(50, this);
66577             }
66578         }
66579     },
66580         
66581     /**
66582      * Stops any active editing
66583      */
66584     stopEditing : function(){
66585         if(this.activeEditor){
66586             this.activeEditor.completeEdit();
66587         }
66588         this.activeEditor = null;
66589     },
66590         
66591          /**
66592      * Called to get grid's drag proxy text, by default returns this.ddText.
66593      * @return {String}
66594      */
66595     getDragDropText : function(){
66596         var count = this.selModel.getSelectedCell() ? 1 : 0;
66597         return String.format(this.ddText, count, count == 1 ? '' : 's');
66598     }
66599         
66600 });/*
66601  * Based on:
66602  * Ext JS Library 1.1.1
66603  * Copyright(c) 2006-2007, Ext JS, LLC.
66604  *
66605  * Originally Released Under LGPL - original licence link has changed is not relivant.
66606  *
66607  * Fork - LGPL
66608  * <script type="text/javascript">
66609  */
66610
66611 // private - not really -- you end up using it !
66612 // This is a support class used internally by the Grid components
66613
66614 /**
66615  * @class Roo.grid.GridEditor
66616  * @extends Roo.Editor
66617  * Class for creating and editable grid elements.
66618  * @param {Object} config any settings (must include field)
66619  */
66620 Roo.grid.GridEditor = function(field, config){
66621     if (!config && field.field) {
66622         config = field;
66623         field = Roo.factory(config.field, Roo.form);
66624     }
66625     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
66626     field.monitorTab = false;
66627 };
66628
66629 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
66630     
66631     /**
66632      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
66633      */
66634     
66635     alignment: "tl-tl",
66636     autoSize: "width",
66637     hideEl : false,
66638     cls: "x-small-editor x-grid-editor",
66639     shim:false,
66640     shadow:"frame"
66641 });/*
66642  * Based on:
66643  * Ext JS Library 1.1.1
66644  * Copyright(c) 2006-2007, Ext JS, LLC.
66645  *
66646  * Originally Released Under LGPL - original licence link has changed is not relivant.
66647  *
66648  * Fork - LGPL
66649  * <script type="text/javascript">
66650  */
66651   
66652
66653   
66654 Roo.grid.PropertyRecord = Roo.data.Record.create([
66655     {name:'name',type:'string'},  'value'
66656 ]);
66657
66658
66659 Roo.grid.PropertyStore = function(grid, source){
66660     this.grid = grid;
66661     this.store = new Roo.data.Store({
66662         recordType : Roo.grid.PropertyRecord
66663     });
66664     this.store.on('update', this.onUpdate,  this);
66665     if(source){
66666         this.setSource(source);
66667     }
66668     Roo.grid.PropertyStore.superclass.constructor.call(this);
66669 };
66670
66671
66672
66673 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
66674     setSource : function(o){
66675         this.source = o;
66676         this.store.removeAll();
66677         var data = [];
66678         for(var k in o){
66679             if(this.isEditableValue(o[k])){
66680                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
66681             }
66682         }
66683         this.store.loadRecords({records: data}, {}, true);
66684     },
66685
66686     onUpdate : function(ds, record, type){
66687         if(type == Roo.data.Record.EDIT){
66688             var v = record.data['value'];
66689             var oldValue = record.modified['value'];
66690             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
66691                 this.source[record.id] = v;
66692                 record.commit();
66693                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
66694             }else{
66695                 record.reject();
66696             }
66697         }
66698     },
66699
66700     getProperty : function(row){
66701        return this.store.getAt(row);
66702     },
66703
66704     isEditableValue: function(val){
66705         if(val && val instanceof Date){
66706             return true;
66707         }else if(typeof val == 'object' || typeof val == 'function'){
66708             return false;
66709         }
66710         return true;
66711     },
66712
66713     setValue : function(prop, value){
66714         this.source[prop] = value;
66715         this.store.getById(prop).set('value', value);
66716     },
66717
66718     getSource : function(){
66719         return this.source;
66720     }
66721 });
66722
66723 Roo.grid.PropertyColumnModel = function(grid, store){
66724     this.grid = grid;
66725     var g = Roo.grid;
66726     g.PropertyColumnModel.superclass.constructor.call(this, [
66727         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
66728         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
66729     ]);
66730     this.store = store;
66731     this.bselect = Roo.DomHelper.append(document.body, {
66732         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
66733             {tag: 'option', value: 'true', html: 'true'},
66734             {tag: 'option', value: 'false', html: 'false'}
66735         ]
66736     });
66737     Roo.id(this.bselect);
66738     var f = Roo.form;
66739     this.editors = {
66740         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
66741         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
66742         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
66743         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
66744         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
66745     };
66746     this.renderCellDelegate = this.renderCell.createDelegate(this);
66747     this.renderPropDelegate = this.renderProp.createDelegate(this);
66748 };
66749
66750 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
66751     
66752     
66753     nameText : 'Name',
66754     valueText : 'Value',
66755     
66756     dateFormat : 'm/j/Y',
66757     
66758     
66759     renderDate : function(dateVal){
66760         return dateVal.dateFormat(this.dateFormat);
66761     },
66762
66763     renderBool : function(bVal){
66764         return bVal ? 'true' : 'false';
66765     },
66766
66767     isCellEditable : function(colIndex, rowIndex){
66768         return colIndex == 1;
66769     },
66770
66771     getRenderer : function(col){
66772         return col == 1 ?
66773             this.renderCellDelegate : this.renderPropDelegate;
66774     },
66775
66776     renderProp : function(v){
66777         return this.getPropertyName(v);
66778     },
66779
66780     renderCell : function(val){
66781         var rv = val;
66782         if(val instanceof Date){
66783             rv = this.renderDate(val);
66784         }else if(typeof val == 'boolean'){
66785             rv = this.renderBool(val);
66786         }
66787         return Roo.util.Format.htmlEncode(rv);
66788     },
66789
66790     getPropertyName : function(name){
66791         var pn = this.grid.propertyNames;
66792         return pn && pn[name] ? pn[name] : name;
66793     },
66794
66795     getCellEditor : function(colIndex, rowIndex){
66796         var p = this.store.getProperty(rowIndex);
66797         var n = p.data['name'], val = p.data['value'];
66798         
66799         if(typeof(this.grid.customEditors[n]) == 'string'){
66800             return this.editors[this.grid.customEditors[n]];
66801         }
66802         if(typeof(this.grid.customEditors[n]) != 'undefined'){
66803             return this.grid.customEditors[n];
66804         }
66805         if(val instanceof Date){
66806             return this.editors['date'];
66807         }else if(typeof val == 'number'){
66808             return this.editors['number'];
66809         }else if(typeof val == 'boolean'){
66810             return this.editors['boolean'];
66811         }else{
66812             return this.editors['string'];
66813         }
66814     }
66815 });
66816
66817 /**
66818  * @class Roo.grid.PropertyGrid
66819  * @extends Roo.grid.EditorGrid
66820  * This class represents the  interface of a component based property grid control.
66821  * <br><br>Usage:<pre><code>
66822  var grid = new Roo.grid.PropertyGrid("my-container-id", {
66823       
66824  });
66825  // set any options
66826  grid.render();
66827  * </code></pre>
66828   
66829  * @constructor
66830  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
66831  * The container MUST have some type of size defined for the grid to fill. The container will be
66832  * automatically set to position relative if it isn't already.
66833  * @param {Object} config A config object that sets properties on this grid.
66834  */
66835 Roo.grid.PropertyGrid = function(container, config){
66836     config = config || {};
66837     var store = new Roo.grid.PropertyStore(this);
66838     this.store = store;
66839     var cm = new Roo.grid.PropertyColumnModel(this, store);
66840     store.store.sort('name', 'ASC');
66841     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
66842         ds: store.store,
66843         cm: cm,
66844         enableColLock:false,
66845         enableColumnMove:false,
66846         stripeRows:false,
66847         trackMouseOver: false,
66848         clicksToEdit:1
66849     }, config));
66850     this.getGridEl().addClass('x-props-grid');
66851     this.lastEditRow = null;
66852     this.on('columnresize', this.onColumnResize, this);
66853     this.addEvents({
66854          /**
66855              * @event beforepropertychange
66856              * Fires before a property changes (return false to stop?)
66857              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
66858              * @param {String} id Record Id
66859              * @param {String} newval New Value
66860          * @param {String} oldval Old Value
66861              */
66862         "beforepropertychange": true,
66863         /**
66864              * @event propertychange
66865              * Fires after a property changes
66866              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
66867              * @param {String} id Record Id
66868              * @param {String} newval New Value
66869          * @param {String} oldval Old Value
66870              */
66871         "propertychange": true
66872     });
66873     this.customEditors = this.customEditors || {};
66874 };
66875 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
66876     
66877      /**
66878      * @cfg {Object} customEditors map of colnames=> custom editors.
66879      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
66880      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
66881      * false disables editing of the field.
66882          */
66883     
66884       /**
66885      * @cfg {Object} propertyNames map of property Names to their displayed value
66886          */
66887     
66888     render : function(){
66889         Roo.grid.PropertyGrid.superclass.render.call(this);
66890         this.autoSize.defer(100, this);
66891     },
66892
66893     autoSize : function(){
66894         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
66895         if(this.view){
66896             this.view.fitColumns();
66897         }
66898     },
66899
66900     onColumnResize : function(){
66901         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
66902         this.autoSize();
66903     },
66904     /**
66905      * Sets the data for the Grid
66906      * accepts a Key => Value object of all the elements avaiable.
66907      * @param {Object} data  to appear in grid.
66908      */
66909     setSource : function(source){
66910         this.store.setSource(source);
66911         //this.autoSize();
66912     },
66913     /**
66914      * Gets all the data from the grid.
66915      * @return {Object} data  data stored in grid
66916      */
66917     getSource : function(){
66918         return this.store.getSource();
66919     }
66920 });/*
66921   
66922  * Licence LGPL
66923  
66924  */
66925  
66926 /**
66927  * @class Roo.grid.Calendar
66928  * @extends Roo.grid.Grid
66929  * This class extends the Grid to provide a calendar widget
66930  * <br><br>Usage:<pre><code>
66931  var grid = new Roo.grid.Calendar("my-container-id", {
66932      ds: myDataStore,
66933      cm: myColModel,
66934      selModel: mySelectionModel,
66935      autoSizeColumns: true,
66936      monitorWindowResize: false,
66937      trackMouseOver: true
66938      eventstore : real data store..
66939  });
66940  // set any options
66941  grid.render();
66942   
66943   * @constructor
66944  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
66945  * The container MUST have some type of size defined for the grid to fill. The container will be
66946  * automatically set to position relative if it isn't already.
66947  * @param {Object} config A config object that sets properties on this grid.
66948  */
66949 Roo.grid.Calendar = function(container, config){
66950         // initialize the container
66951         this.container = Roo.get(container);
66952         this.container.update("");
66953         this.container.setStyle("overflow", "hidden");
66954     this.container.addClass('x-grid-container');
66955
66956     this.id = this.container.id;
66957
66958     Roo.apply(this, config);
66959     // check and correct shorthanded configs
66960     
66961     var rows = [];
66962     var d =1;
66963     for (var r = 0;r < 6;r++) {
66964         
66965         rows[r]=[];
66966         for (var c =0;c < 7;c++) {
66967             rows[r][c]= '';
66968         }
66969     }
66970     if (this.eventStore) {
66971         this.eventStore= Roo.factory(this.eventStore, Roo.data);
66972         this.eventStore.on('load',this.onLoad, this);
66973         this.eventStore.on('beforeload',this.clearEvents, this);
66974          
66975     }
66976     
66977     this.dataSource = new Roo.data.Store({
66978             proxy: new Roo.data.MemoryProxy(rows),
66979             reader: new Roo.data.ArrayReader({}, [
66980                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
66981     });
66982
66983     this.dataSource.load();
66984     this.ds = this.dataSource;
66985     this.ds.xmodule = this.xmodule || false;
66986     
66987     
66988     var cellRender = function(v,x,r)
66989     {
66990         return String.format(
66991             '<div class="fc-day  fc-widget-content"><div>' +
66992                 '<div class="fc-event-container"></div>' +
66993                 '<div class="fc-day-number">{0}</div>'+
66994                 
66995                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
66996             '</div></div>', v);
66997     
66998     }
66999     
67000     
67001     this.colModel = new Roo.grid.ColumnModel( [
67002         {
67003             xtype: 'ColumnModel',
67004             xns: Roo.grid,
67005             dataIndex : 'weekday0',
67006             header : 'Sunday',
67007             renderer : cellRender
67008         },
67009         {
67010             xtype: 'ColumnModel',
67011             xns: Roo.grid,
67012             dataIndex : 'weekday1',
67013             header : 'Monday',
67014             renderer : cellRender
67015         },
67016         {
67017             xtype: 'ColumnModel',
67018             xns: Roo.grid,
67019             dataIndex : 'weekday2',
67020             header : 'Tuesday',
67021             renderer : cellRender
67022         },
67023         {
67024             xtype: 'ColumnModel',
67025             xns: Roo.grid,
67026             dataIndex : 'weekday3',
67027             header : 'Wednesday',
67028             renderer : cellRender
67029         },
67030         {
67031             xtype: 'ColumnModel',
67032             xns: Roo.grid,
67033             dataIndex : 'weekday4',
67034             header : 'Thursday',
67035             renderer : cellRender
67036         },
67037         {
67038             xtype: 'ColumnModel',
67039             xns: Roo.grid,
67040             dataIndex : 'weekday5',
67041             header : 'Friday',
67042             renderer : cellRender
67043         },
67044         {
67045             xtype: 'ColumnModel',
67046             xns: Roo.grid,
67047             dataIndex : 'weekday6',
67048             header : 'Saturday',
67049             renderer : cellRender
67050         }
67051     ]);
67052     this.cm = this.colModel;
67053     this.cm.xmodule = this.xmodule || false;
67054  
67055         
67056           
67057     //this.selModel = new Roo.grid.CellSelectionModel();
67058     //this.sm = this.selModel;
67059     //this.selModel.init(this);
67060     
67061     
67062     if(this.width){
67063         this.container.setWidth(this.width);
67064     }
67065
67066     if(this.height){
67067         this.container.setHeight(this.height);
67068     }
67069     /** @private */
67070         this.addEvents({
67071         // raw events
67072         /**
67073          * @event click
67074          * The raw click event for the entire grid.
67075          * @param {Roo.EventObject} e
67076          */
67077         "click" : true,
67078         /**
67079          * @event dblclick
67080          * The raw dblclick event for the entire grid.
67081          * @param {Roo.EventObject} e
67082          */
67083         "dblclick" : true,
67084         /**
67085          * @event contextmenu
67086          * The raw contextmenu event for the entire grid.
67087          * @param {Roo.EventObject} e
67088          */
67089         "contextmenu" : true,
67090         /**
67091          * @event mousedown
67092          * The raw mousedown event for the entire grid.
67093          * @param {Roo.EventObject} e
67094          */
67095         "mousedown" : true,
67096         /**
67097          * @event mouseup
67098          * The raw mouseup event for the entire grid.
67099          * @param {Roo.EventObject} e
67100          */
67101         "mouseup" : true,
67102         /**
67103          * @event mouseover
67104          * The raw mouseover event for the entire grid.
67105          * @param {Roo.EventObject} e
67106          */
67107         "mouseover" : true,
67108         /**
67109          * @event mouseout
67110          * The raw mouseout event for the entire grid.
67111          * @param {Roo.EventObject} e
67112          */
67113         "mouseout" : true,
67114         /**
67115          * @event keypress
67116          * The raw keypress event for the entire grid.
67117          * @param {Roo.EventObject} e
67118          */
67119         "keypress" : true,
67120         /**
67121          * @event keydown
67122          * The raw keydown event for the entire grid.
67123          * @param {Roo.EventObject} e
67124          */
67125         "keydown" : true,
67126
67127         // custom events
67128
67129         /**
67130          * @event cellclick
67131          * Fires when a cell is clicked
67132          * @param {Grid} this
67133          * @param {Number} rowIndex
67134          * @param {Number} columnIndex
67135          * @param {Roo.EventObject} e
67136          */
67137         "cellclick" : true,
67138         /**
67139          * @event celldblclick
67140          * Fires when a cell is double clicked
67141          * @param {Grid} this
67142          * @param {Number} rowIndex
67143          * @param {Number} columnIndex
67144          * @param {Roo.EventObject} e
67145          */
67146         "celldblclick" : true,
67147         /**
67148          * @event rowclick
67149          * Fires when a row is clicked
67150          * @param {Grid} this
67151          * @param {Number} rowIndex
67152          * @param {Roo.EventObject} e
67153          */
67154         "rowclick" : true,
67155         /**
67156          * @event rowdblclick
67157          * Fires when a row is double clicked
67158          * @param {Grid} this
67159          * @param {Number} rowIndex
67160          * @param {Roo.EventObject} e
67161          */
67162         "rowdblclick" : true,
67163         /**
67164          * @event headerclick
67165          * Fires when a header is clicked
67166          * @param {Grid} this
67167          * @param {Number} columnIndex
67168          * @param {Roo.EventObject} e
67169          */
67170         "headerclick" : true,
67171         /**
67172          * @event headerdblclick
67173          * Fires when a header cell is double clicked
67174          * @param {Grid} this
67175          * @param {Number} columnIndex
67176          * @param {Roo.EventObject} e
67177          */
67178         "headerdblclick" : true,
67179         /**
67180          * @event rowcontextmenu
67181          * Fires when a row is right clicked
67182          * @param {Grid} this
67183          * @param {Number} rowIndex
67184          * @param {Roo.EventObject} e
67185          */
67186         "rowcontextmenu" : true,
67187         /**
67188          * @event cellcontextmenu
67189          * Fires when a cell is right clicked
67190          * @param {Grid} this
67191          * @param {Number} rowIndex
67192          * @param {Number} cellIndex
67193          * @param {Roo.EventObject} e
67194          */
67195          "cellcontextmenu" : true,
67196         /**
67197          * @event headercontextmenu
67198          * Fires when a header is right clicked
67199          * @param {Grid} this
67200          * @param {Number} columnIndex
67201          * @param {Roo.EventObject} e
67202          */
67203         "headercontextmenu" : true,
67204         /**
67205          * @event bodyscroll
67206          * Fires when the body element is scrolled
67207          * @param {Number} scrollLeft
67208          * @param {Number} scrollTop
67209          */
67210         "bodyscroll" : true,
67211         /**
67212          * @event columnresize
67213          * Fires when the user resizes a column
67214          * @param {Number} columnIndex
67215          * @param {Number} newSize
67216          */
67217         "columnresize" : true,
67218         /**
67219          * @event columnmove
67220          * Fires when the user moves a column
67221          * @param {Number} oldIndex
67222          * @param {Number} newIndex
67223          */
67224         "columnmove" : true,
67225         /**
67226          * @event startdrag
67227          * Fires when row(s) start being dragged
67228          * @param {Grid} this
67229          * @param {Roo.GridDD} dd The drag drop object
67230          * @param {event} e The raw browser event
67231          */
67232         "startdrag" : true,
67233         /**
67234          * @event enddrag
67235          * Fires when a drag operation is complete
67236          * @param {Grid} this
67237          * @param {Roo.GridDD} dd The drag drop object
67238          * @param {event} e The raw browser event
67239          */
67240         "enddrag" : true,
67241         /**
67242          * @event dragdrop
67243          * Fires when dragged row(s) are dropped on a valid DD target
67244          * @param {Grid} this
67245          * @param {Roo.GridDD} dd The drag drop object
67246          * @param {String} targetId The target drag drop object
67247          * @param {event} e The raw browser event
67248          */
67249         "dragdrop" : true,
67250         /**
67251          * @event dragover
67252          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
67253          * @param {Grid} this
67254          * @param {Roo.GridDD} dd The drag drop object
67255          * @param {String} targetId The target drag drop object
67256          * @param {event} e The raw browser event
67257          */
67258         "dragover" : true,
67259         /**
67260          * @event dragenter
67261          *  Fires when the dragged row(s) first cross another DD target while being dragged
67262          * @param {Grid} this
67263          * @param {Roo.GridDD} dd The drag drop object
67264          * @param {String} targetId The target drag drop object
67265          * @param {event} e The raw browser event
67266          */
67267         "dragenter" : true,
67268         /**
67269          * @event dragout
67270          * Fires when the dragged row(s) leave another DD target while being dragged
67271          * @param {Grid} this
67272          * @param {Roo.GridDD} dd The drag drop object
67273          * @param {String} targetId The target drag drop object
67274          * @param {event} e The raw browser event
67275          */
67276         "dragout" : true,
67277         /**
67278          * @event rowclass
67279          * Fires when a row is rendered, so you can change add a style to it.
67280          * @param {GridView} gridview   The grid view
67281          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
67282          */
67283         'rowclass' : true,
67284
67285         /**
67286          * @event render
67287          * Fires when the grid is rendered
67288          * @param {Grid} grid
67289          */
67290         'render' : true,
67291             /**
67292              * @event select
67293              * Fires when a date is selected
67294              * @param {DatePicker} this
67295              * @param {Date} date The selected date
67296              */
67297         'select': true,
67298         /**
67299              * @event monthchange
67300              * Fires when the displayed month changes 
67301              * @param {DatePicker} this
67302              * @param {Date} date The selected month
67303              */
67304         'monthchange': true,
67305         /**
67306              * @event evententer
67307              * Fires when mouse over an event
67308              * @param {Calendar} this
67309              * @param {event} Event
67310              */
67311         'evententer': true,
67312         /**
67313              * @event eventleave
67314              * Fires when the mouse leaves an
67315              * @param {Calendar} this
67316              * @param {event}
67317              */
67318         'eventleave': true,
67319         /**
67320              * @event eventclick
67321              * Fires when the mouse click an
67322              * @param {Calendar} this
67323              * @param {event}
67324              */
67325         'eventclick': true,
67326         /**
67327              * @event eventrender
67328              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
67329              * @param {Calendar} this
67330              * @param {data} data to be modified
67331              */
67332         'eventrender': true
67333         
67334     });
67335
67336     Roo.grid.Grid.superclass.constructor.call(this);
67337     this.on('render', function() {
67338         this.view.el.addClass('x-grid-cal'); 
67339         
67340         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
67341
67342     },this);
67343     
67344     if (!Roo.grid.Calendar.style) {
67345         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
67346             
67347             
67348             '.x-grid-cal .x-grid-col' :  {
67349                 height: 'auto !important',
67350                 'vertical-align': 'top'
67351             },
67352             '.x-grid-cal  .fc-event-hori' : {
67353                 height: '14px'
67354             }
67355              
67356             
67357         }, Roo.id());
67358     }
67359
67360     
67361     
67362 };
67363 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
67364     /**
67365      * @cfg {Store} eventStore The store that loads events.
67366      */
67367     eventStore : 25,
67368
67369      
67370     activeDate : false,
67371     startDay : 0,
67372     autoWidth : true,
67373     monitorWindowResize : false,
67374
67375     
67376     resizeColumns : function() {
67377         var col = (this.view.el.getWidth() / 7) - 3;
67378         // loop through cols, and setWidth
67379         for(var i =0 ; i < 7 ; i++){
67380             this.cm.setColumnWidth(i, col);
67381         }
67382     },
67383      setDate :function(date) {
67384         
67385         Roo.log('setDate?');
67386         
67387         this.resizeColumns();
67388         var vd = this.activeDate;
67389         this.activeDate = date;
67390 //        if(vd && this.el){
67391 //            var t = date.getTime();
67392 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
67393 //                Roo.log('using add remove');
67394 //                
67395 //                this.fireEvent('monthchange', this, date);
67396 //                
67397 //                this.cells.removeClass("fc-state-highlight");
67398 //                this.cells.each(function(c){
67399 //                   if(c.dateValue == t){
67400 //                       c.addClass("fc-state-highlight");
67401 //                       setTimeout(function(){
67402 //                            try{c.dom.firstChild.focus();}catch(e){}
67403 //                       }, 50);
67404 //                       return false;
67405 //                   }
67406 //                   return true;
67407 //                });
67408 //                return;
67409 //            }
67410 //        }
67411         
67412         var days = date.getDaysInMonth();
67413         
67414         var firstOfMonth = date.getFirstDateOfMonth();
67415         var startingPos = firstOfMonth.getDay()-this.startDay;
67416         
67417         if(startingPos < this.startDay){
67418             startingPos += 7;
67419         }
67420         
67421         var pm = date.add(Date.MONTH, -1);
67422         var prevStart = pm.getDaysInMonth()-startingPos;
67423 //        
67424         
67425         
67426         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
67427         
67428         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
67429         //this.cells.addClassOnOver('fc-state-hover');
67430         
67431         var cells = this.cells.elements;
67432         var textEls = this.textNodes;
67433         
67434         //Roo.each(cells, function(cell){
67435         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
67436         //});
67437         
67438         days += startingPos;
67439
67440         // convert everything to numbers so it's fast
67441         var day = 86400000;
67442         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
67443         //Roo.log(d);
67444         //Roo.log(pm);
67445         //Roo.log(prevStart);
67446         
67447         var today = new Date().clearTime().getTime();
67448         var sel = date.clearTime().getTime();
67449         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
67450         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
67451         var ddMatch = this.disabledDatesRE;
67452         var ddText = this.disabledDatesText;
67453         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
67454         var ddaysText = this.disabledDaysText;
67455         var format = this.format;
67456         
67457         var setCellClass = function(cal, cell){
67458             
67459             //Roo.log('set Cell Class');
67460             cell.title = "";
67461             var t = d.getTime();
67462             
67463             //Roo.log(d);
67464             
67465             
67466             cell.dateValue = t;
67467             if(t == today){
67468                 cell.className += " fc-today";
67469                 cell.className += " fc-state-highlight";
67470                 cell.title = cal.todayText;
67471             }
67472             if(t == sel){
67473                 // disable highlight in other month..
67474                 cell.className += " fc-state-highlight";
67475                 
67476             }
67477             // disabling
67478             if(t < min) {
67479                 //cell.className = " fc-state-disabled";
67480                 cell.title = cal.minText;
67481                 return;
67482             }
67483             if(t > max) {
67484                 //cell.className = " fc-state-disabled";
67485                 cell.title = cal.maxText;
67486                 return;
67487             }
67488             if(ddays){
67489                 if(ddays.indexOf(d.getDay()) != -1){
67490                     // cell.title = ddaysText;
67491                    // cell.className = " fc-state-disabled";
67492                 }
67493             }
67494             if(ddMatch && format){
67495                 var fvalue = d.dateFormat(format);
67496                 if(ddMatch.test(fvalue)){
67497                     cell.title = ddText.replace("%0", fvalue);
67498                    cell.className = " fc-state-disabled";
67499                 }
67500             }
67501             
67502             if (!cell.initialClassName) {
67503                 cell.initialClassName = cell.dom.className;
67504             }
67505             
67506             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
67507         };
67508
67509         var i = 0;
67510         
67511         for(; i < startingPos; i++) {
67512             cells[i].dayName =  (++prevStart);
67513             Roo.log(textEls[i]);
67514             d.setDate(d.getDate()+1);
67515             
67516             //cells[i].className = "fc-past fc-other-month";
67517             setCellClass(this, cells[i]);
67518         }
67519         
67520         var intDay = 0;
67521         
67522         for(; i < days; i++){
67523             intDay = i - startingPos + 1;
67524             cells[i].dayName =  (intDay);
67525             d.setDate(d.getDate()+1);
67526             
67527             cells[i].className = ''; // "x-date-active";
67528             setCellClass(this, cells[i]);
67529         }
67530         var extraDays = 0;
67531         
67532         for(; i < 42; i++) {
67533             //textEls[i].innerHTML = (++extraDays);
67534             
67535             d.setDate(d.getDate()+1);
67536             cells[i].dayName = (++extraDays);
67537             cells[i].className = "fc-future fc-other-month";
67538             setCellClass(this, cells[i]);
67539         }
67540         
67541         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
67542         
67543         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
67544         
67545         // this will cause all the cells to mis
67546         var rows= [];
67547         var i =0;
67548         for (var r = 0;r < 6;r++) {
67549             for (var c =0;c < 7;c++) {
67550                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
67551             }    
67552         }
67553         
67554         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
67555         for(i=0;i<cells.length;i++) {
67556             
67557             this.cells.elements[i].dayName = cells[i].dayName ;
67558             this.cells.elements[i].className = cells[i].className;
67559             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
67560             this.cells.elements[i].title = cells[i].title ;
67561             this.cells.elements[i].dateValue = cells[i].dateValue ;
67562         }
67563         
67564         
67565         
67566         
67567         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
67568         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
67569         
67570         ////if(totalRows != 6){
67571             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
67572            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
67573        // }
67574         
67575         this.fireEvent('monthchange', this, date);
67576         
67577         
67578     },
67579  /**
67580      * Returns the grid's SelectionModel.
67581      * @return {SelectionModel}
67582      */
67583     getSelectionModel : function(){
67584         if(!this.selModel){
67585             this.selModel = new Roo.grid.CellSelectionModel();
67586         }
67587         return this.selModel;
67588     },
67589
67590     load: function() {
67591         this.eventStore.load()
67592         
67593         
67594         
67595     },
67596     
67597     findCell : function(dt) {
67598         dt = dt.clearTime().getTime();
67599         var ret = false;
67600         this.cells.each(function(c){
67601             //Roo.log("check " +c.dateValue + '?=' + dt);
67602             if(c.dateValue == dt){
67603                 ret = c;
67604                 return false;
67605             }
67606             return true;
67607         });
67608         
67609         return ret;
67610     },
67611     
67612     findCells : function(rec) {
67613         var s = rec.data.start_dt.clone().clearTime().getTime();
67614        // Roo.log(s);
67615         var e= rec.data.end_dt.clone().clearTime().getTime();
67616        // Roo.log(e);
67617         var ret = [];
67618         this.cells.each(function(c){
67619              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
67620             
67621             if(c.dateValue > e){
67622                 return ;
67623             }
67624             if(c.dateValue < s){
67625                 return ;
67626             }
67627             ret.push(c);
67628         });
67629         
67630         return ret;    
67631     },
67632     
67633     findBestRow: function(cells)
67634     {
67635         var ret = 0;
67636         
67637         for (var i =0 ; i < cells.length;i++) {
67638             ret  = Math.max(cells[i].rows || 0,ret);
67639         }
67640         return ret;
67641         
67642     },
67643     
67644     
67645     addItem : function(rec)
67646     {
67647         // look for vertical location slot in
67648         var cells = this.findCells(rec);
67649         
67650         rec.row = this.findBestRow(cells);
67651         
67652         // work out the location.
67653         
67654         var crow = false;
67655         var rows = [];
67656         for(var i =0; i < cells.length; i++) {
67657             if (!crow) {
67658                 crow = {
67659                     start : cells[i],
67660                     end :  cells[i]
67661                 };
67662                 continue;
67663             }
67664             if (crow.start.getY() == cells[i].getY()) {
67665                 // on same row.
67666                 crow.end = cells[i];
67667                 continue;
67668             }
67669             // different row.
67670             rows.push(crow);
67671             crow = {
67672                 start: cells[i],
67673                 end : cells[i]
67674             };
67675             
67676         }
67677         
67678         rows.push(crow);
67679         rec.els = [];
67680         rec.rows = rows;
67681         rec.cells = cells;
67682         for (var i = 0; i < cells.length;i++) {
67683             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
67684             
67685         }
67686         
67687         
67688     },
67689     
67690     clearEvents: function() {
67691         
67692         if (!this.eventStore.getCount()) {
67693             return;
67694         }
67695         // reset number of rows in cells.
67696         Roo.each(this.cells.elements, function(c){
67697             c.rows = 0;
67698         });
67699         
67700         this.eventStore.each(function(e) {
67701             this.clearEvent(e);
67702         },this);
67703         
67704     },
67705     
67706     clearEvent : function(ev)
67707     {
67708         if (ev.els) {
67709             Roo.each(ev.els, function(el) {
67710                 el.un('mouseenter' ,this.onEventEnter, this);
67711                 el.un('mouseleave' ,this.onEventLeave, this);
67712                 el.remove();
67713             },this);
67714             ev.els = [];
67715         }
67716     },
67717     
67718     
67719     renderEvent : function(ev,ctr) {
67720         if (!ctr) {
67721              ctr = this.view.el.select('.fc-event-container',true).first();
67722         }
67723         
67724          
67725         this.clearEvent(ev);
67726             //code
67727        
67728         
67729         
67730         ev.els = [];
67731         var cells = ev.cells;
67732         var rows = ev.rows;
67733         this.fireEvent('eventrender', this, ev);
67734         
67735         for(var i =0; i < rows.length; i++) {
67736             
67737             cls = '';
67738             if (i == 0) {
67739                 cls += ' fc-event-start';
67740             }
67741             if ((i+1) == rows.length) {
67742                 cls += ' fc-event-end';
67743             }
67744             
67745             //Roo.log(ev.data);
67746             // how many rows should it span..
67747             var cg = this.eventTmpl.append(ctr,Roo.apply({
67748                 fccls : cls
67749                 
67750             }, ev.data) , true);
67751             
67752             
67753             cg.on('mouseenter' ,this.onEventEnter, this, ev);
67754             cg.on('mouseleave' ,this.onEventLeave, this, ev);
67755             cg.on('click', this.onEventClick, this, ev);
67756             
67757             ev.els.push(cg);
67758             
67759             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
67760             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
67761             //Roo.log(cg);
67762              
67763             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
67764             cg.setWidth(ebox.right - sbox.x -2);
67765         }
67766     },
67767     
67768     renderEvents: function()
67769     {   
67770         // first make sure there is enough space..
67771         
67772         if (!this.eventTmpl) {
67773             this.eventTmpl = new Roo.Template(
67774                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
67775                     '<div class="fc-event-inner">' +
67776                         '<span class="fc-event-time">{time}</span>' +
67777                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
67778                     '</div>' +
67779                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
67780                 '</div>'
67781             );
67782                 
67783         }
67784                
67785         
67786         
67787         this.cells.each(function(c) {
67788             //Roo.log(c.select('.fc-day-content div',true).first());
67789             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
67790         });
67791         
67792         var ctr = this.view.el.select('.fc-event-container',true).first();
67793         
67794         var cls;
67795         this.eventStore.each(function(ev){
67796             
67797             this.renderEvent(ev);
67798              
67799              
67800         }, this);
67801         this.view.layout();
67802         
67803     },
67804     
67805     onEventEnter: function (e, el,event,d) {
67806         this.fireEvent('evententer', this, el, event);
67807     },
67808     
67809     onEventLeave: function (e, el,event,d) {
67810         this.fireEvent('eventleave', this, el, event);
67811     },
67812     
67813     onEventClick: function (e, el,event,d) {
67814         this.fireEvent('eventclick', this, el, event);
67815     },
67816     
67817     onMonthChange: function () {
67818         this.store.load();
67819     },
67820     
67821     onLoad: function () {
67822         
67823         //Roo.log('calendar onload');
67824 //         
67825         if(this.eventStore.getCount() > 0){
67826             
67827            
67828             
67829             this.eventStore.each(function(d){
67830                 
67831                 
67832                 // FIXME..
67833                 var add =   d.data;
67834                 if (typeof(add.end_dt) == 'undefined')  {
67835                     Roo.log("Missing End time in calendar data: ");
67836                     Roo.log(d);
67837                     return;
67838                 }
67839                 if (typeof(add.start_dt) == 'undefined')  {
67840                     Roo.log("Missing Start time in calendar data: ");
67841                     Roo.log(d);
67842                     return;
67843                 }
67844                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
67845                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
67846                 add.id = add.id || d.id;
67847                 add.title = add.title || '??';
67848                 
67849                 this.addItem(d);
67850                 
67851              
67852             },this);
67853         }
67854         
67855         this.renderEvents();
67856     }
67857     
67858
67859 });
67860 /*
67861  grid : {
67862                 xtype: 'Grid',
67863                 xns: Roo.grid,
67864                 listeners : {
67865                     render : function ()
67866                     {
67867                         _this.grid = this;
67868                         
67869                         if (!this.view.el.hasClass('course-timesheet')) {
67870                             this.view.el.addClass('course-timesheet');
67871                         }
67872                         if (this.tsStyle) {
67873                             this.ds.load({});
67874                             return; 
67875                         }
67876                         Roo.log('width');
67877                         Roo.log(_this.grid.view.el.getWidth());
67878                         
67879                         
67880                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
67881                             '.course-timesheet .x-grid-row' : {
67882                                 height: '80px'
67883                             },
67884                             '.x-grid-row td' : {
67885                                 'vertical-align' : 0
67886                             },
67887                             '.course-edit-link' : {
67888                                 'color' : 'blue',
67889                                 'text-overflow' : 'ellipsis',
67890                                 'overflow' : 'hidden',
67891                                 'white-space' : 'nowrap',
67892                                 'cursor' : 'pointer'
67893                             },
67894                             '.sub-link' : {
67895                                 'color' : 'green'
67896                             },
67897                             '.de-act-sup-link' : {
67898                                 'color' : 'purple',
67899                                 'text-decoration' : 'line-through'
67900                             },
67901                             '.de-act-link' : {
67902                                 'color' : 'red',
67903                                 'text-decoration' : 'line-through'
67904                             },
67905                             '.course-timesheet .course-highlight' : {
67906                                 'border-top-style': 'dashed !important',
67907                                 'border-bottom-bottom': 'dashed !important'
67908                             },
67909                             '.course-timesheet .course-item' : {
67910                                 'font-family'   : 'tahoma, arial, helvetica',
67911                                 'font-size'     : '11px',
67912                                 'overflow'      : 'hidden',
67913                                 'padding-left'  : '10px',
67914                                 'padding-right' : '10px',
67915                                 'padding-top' : '10px' 
67916                             }
67917                             
67918                         }, Roo.id());
67919                                 this.ds.load({});
67920                     }
67921                 },
67922                 autoWidth : true,
67923                 monitorWindowResize : false,
67924                 cellrenderer : function(v,x,r)
67925                 {
67926                     return v;
67927                 },
67928                 sm : {
67929                     xtype: 'CellSelectionModel',
67930                     xns: Roo.grid
67931                 },
67932                 dataSource : {
67933                     xtype: 'Store',
67934                     xns: Roo.data,
67935                     listeners : {
67936                         beforeload : function (_self, options)
67937                         {
67938                             options.params = options.params || {};
67939                             options.params._month = _this.monthField.getValue();
67940                             options.params.limit = 9999;
67941                             options.params['sort'] = 'when_dt';    
67942                             options.params['dir'] = 'ASC';    
67943                             this.proxy.loadResponse = this.loadResponse;
67944                             Roo.log("load?");
67945                             //this.addColumns();
67946                         },
67947                         load : function (_self, records, options)
67948                         {
67949                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
67950                                 // if you click on the translation.. you can edit it...
67951                                 var el = Roo.get(this);
67952                                 var id = el.dom.getAttribute('data-id');
67953                                 var d = el.dom.getAttribute('data-date');
67954                                 var t = el.dom.getAttribute('data-time');
67955                                 //var id = this.child('span').dom.textContent;
67956                                 
67957                                 //Roo.log(this);
67958                                 Pman.Dialog.CourseCalendar.show({
67959                                     id : id,
67960                                     when_d : d,
67961                                     when_t : t,
67962                                     productitem_active : id ? 1 : 0
67963                                 }, function() {
67964                                     _this.grid.ds.load({});
67965                                 });
67966                            
67967                            });
67968                            
67969                            _this.panel.fireEvent('resize', [ '', '' ]);
67970                         }
67971                     },
67972                     loadResponse : function(o, success, response){
67973                             // this is overridden on before load..
67974                             
67975                             Roo.log("our code?");       
67976                             //Roo.log(success);
67977                             //Roo.log(response)
67978                             delete this.activeRequest;
67979                             if(!success){
67980                                 this.fireEvent("loadexception", this, o, response);
67981                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
67982                                 return;
67983                             }
67984                             var result;
67985                             try {
67986                                 result = o.reader.read(response);
67987                             }catch(e){
67988                                 Roo.log("load exception?");
67989                                 this.fireEvent("loadexception", this, o, response, e);
67990                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
67991                                 return;
67992                             }
67993                             Roo.log("ready...");        
67994                             // loop through result.records;
67995                             // and set this.tdate[date] = [] << array of records..
67996                             _this.tdata  = {};
67997                             Roo.each(result.records, function(r){
67998                                 //Roo.log(r.data);
67999                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
68000                                     _this.tdata[r.data.when_dt.format('j')] = [];
68001                                 }
68002                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
68003                             });
68004                             
68005                             //Roo.log(_this.tdata);
68006                             
68007                             result.records = [];
68008                             result.totalRecords = 6;
68009                     
68010                             // let's generate some duumy records for the rows.
68011                             //var st = _this.dateField.getValue();
68012                             
68013                             // work out monday..
68014                             //st = st.add(Date.DAY, -1 * st.format('w'));
68015                             
68016                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
68017                             
68018                             var firstOfMonth = date.getFirstDayOfMonth();
68019                             var days = date.getDaysInMonth();
68020                             var d = 1;
68021                             var firstAdded = false;
68022                             for (var i = 0; i < result.totalRecords ; i++) {
68023                                 //var d= st.add(Date.DAY, i);
68024                                 var row = {};
68025                                 var added = 0;
68026                                 for(var w = 0 ; w < 7 ; w++){
68027                                     if(!firstAdded && firstOfMonth != w){
68028                                         continue;
68029                                     }
68030                                     if(d > days){
68031                                         continue;
68032                                     }
68033                                     firstAdded = true;
68034                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
68035                                     row['weekday'+w] = String.format(
68036                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
68037                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
68038                                                     d,
68039                                                     date.format('Y-m-')+dd
68040                                                 );
68041                                     added++;
68042                                     if(typeof(_this.tdata[d]) != 'undefined'){
68043                                         Roo.each(_this.tdata[d], function(r){
68044                                             var is_sub = '';
68045                                             var deactive = '';
68046                                             var id = r.id;
68047                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
68048                                             if(r.parent_id*1>0){
68049                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
68050                                                 id = r.parent_id;
68051                                             }
68052                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
68053                                                 deactive = 'de-act-link';
68054                                             }
68055                                             
68056                                             row['weekday'+w] += String.format(
68057                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
68058                                                     id, //0
68059                                                     r.product_id_name, //1
68060                                                     r.when_dt.format('h:ia'), //2
68061                                                     is_sub, //3
68062                                                     deactive, //4
68063                                                     desc // 5
68064                                             );
68065                                         });
68066                                     }
68067                                     d++;
68068                                 }
68069                                 
68070                                 // only do this if something added..
68071                                 if(added > 0){ 
68072                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
68073                                 }
68074                                 
68075                                 
68076                                 // push it twice. (second one with an hour..
68077                                 
68078                             }
68079                             //Roo.log(result);
68080                             this.fireEvent("load", this, o, o.request.arg);
68081                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
68082                         },
68083                     sortInfo : {field: 'when_dt', direction : 'ASC' },
68084                     proxy : {
68085                         xtype: 'HttpProxy',
68086                         xns: Roo.data,
68087                         method : 'GET',
68088                         url : baseURL + '/Roo/Shop_course.php'
68089                     },
68090                     reader : {
68091                         xtype: 'JsonReader',
68092                         xns: Roo.data,
68093                         id : 'id',
68094                         fields : [
68095                             {
68096                                 'name': 'id',
68097                                 'type': 'int'
68098                             },
68099                             {
68100                                 'name': 'when_dt',
68101                                 'type': 'string'
68102                             },
68103                             {
68104                                 'name': 'end_dt',
68105                                 'type': 'string'
68106                             },
68107                             {
68108                                 'name': 'parent_id',
68109                                 'type': 'int'
68110                             },
68111                             {
68112                                 'name': 'product_id',
68113                                 'type': 'int'
68114                             },
68115                             {
68116                                 'name': 'productitem_id',
68117                                 'type': 'int'
68118                             },
68119                             {
68120                                 'name': 'guid',
68121                                 'type': 'int'
68122                             }
68123                         ]
68124                     }
68125                 },
68126                 toolbar : {
68127                     xtype: 'Toolbar',
68128                     xns: Roo,
68129                     items : [
68130                         {
68131                             xtype: 'Button',
68132                             xns: Roo.Toolbar,
68133                             listeners : {
68134                                 click : function (_self, e)
68135                                 {
68136                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
68137                                     sd.setMonth(sd.getMonth()-1);
68138                                     _this.monthField.setValue(sd.format('Y-m-d'));
68139                                     _this.grid.ds.load({});
68140                                 }
68141                             },
68142                             text : "Back"
68143                         },
68144                         {
68145                             xtype: 'Separator',
68146                             xns: Roo.Toolbar
68147                         },
68148                         {
68149                             xtype: 'MonthField',
68150                             xns: Roo.form,
68151                             listeners : {
68152                                 render : function (_self)
68153                                 {
68154                                     _this.monthField = _self;
68155                                    // _this.monthField.set  today
68156                                 },
68157                                 select : function (combo, date)
68158                                 {
68159                                     _this.grid.ds.load({});
68160                                 }
68161                             },
68162                             value : (function() { return new Date(); })()
68163                         },
68164                         {
68165                             xtype: 'Separator',
68166                             xns: Roo.Toolbar
68167                         },
68168                         {
68169                             xtype: 'TextItem',
68170                             xns: Roo.Toolbar,
68171                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
68172                         },
68173                         {
68174                             xtype: 'Fill',
68175                             xns: Roo.Toolbar
68176                         },
68177                         {
68178                             xtype: 'Button',
68179                             xns: Roo.Toolbar,
68180                             listeners : {
68181                                 click : function (_self, e)
68182                                 {
68183                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
68184                                     sd.setMonth(sd.getMonth()+1);
68185                                     _this.monthField.setValue(sd.format('Y-m-d'));
68186                                     _this.grid.ds.load({});
68187                                 }
68188                             },
68189                             text : "Next"
68190                         }
68191                     ]
68192                 },
68193                  
68194             }
68195         };
68196         
68197         *//*
68198  * Based on:
68199  * Ext JS Library 1.1.1
68200  * Copyright(c) 2006-2007, Ext JS, LLC.
68201  *
68202  * Originally Released Under LGPL - original licence link has changed is not relivant.
68203  *
68204  * Fork - LGPL
68205  * <script type="text/javascript">
68206  */
68207  
68208 /**
68209  * @class Roo.LoadMask
68210  * A simple utility class for generically masking elements while loading data.  If the element being masked has
68211  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
68212  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
68213  * element's UpdateManager load indicator and will be destroyed after the initial load.
68214  * @constructor
68215  * Create a new LoadMask
68216  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
68217  * @param {Object} config The config object
68218  */
68219 Roo.LoadMask = function(el, config){
68220     this.el = Roo.get(el);
68221     Roo.apply(this, config);
68222     if(this.store){
68223         this.store.on('beforeload', this.onBeforeLoad, this);
68224         this.store.on('load', this.onLoad, this);
68225         this.store.on('loadexception', this.onLoadException, this);
68226         this.removeMask = false;
68227     }else{
68228         var um = this.el.getUpdateManager();
68229         um.showLoadIndicator = false; // disable the default indicator
68230         um.on('beforeupdate', this.onBeforeLoad, this);
68231         um.on('update', this.onLoad, this);
68232         um.on('failure', this.onLoad, this);
68233         this.removeMask = true;
68234     }
68235 };
68236
68237 Roo.LoadMask.prototype = {
68238     /**
68239      * @cfg {Boolean} removeMask
68240      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
68241      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
68242      */
68243     removeMask : false,
68244     /**
68245      * @cfg {String} msg
68246      * The text to display in a centered loading message box (defaults to 'Loading...')
68247      */
68248     msg : 'Loading...',
68249     /**
68250      * @cfg {String} msgCls
68251      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
68252      */
68253     msgCls : 'x-mask-loading',
68254
68255     /**
68256      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
68257      * @type Boolean
68258      */
68259     disabled: false,
68260
68261     /**
68262      * Disables the mask to prevent it from being displayed
68263      */
68264     disable : function(){
68265        this.disabled = true;
68266     },
68267
68268     /**
68269      * Enables the mask so that it can be displayed
68270      */
68271     enable : function(){
68272         this.disabled = false;
68273     },
68274     
68275     onLoadException : function()
68276     {
68277         Roo.log(arguments);
68278         
68279         if (typeof(arguments[3]) != 'undefined') {
68280             Roo.MessageBox.alert("Error loading",arguments[3]);
68281         } 
68282         /*
68283         try {
68284             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
68285                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
68286             }   
68287         } catch(e) {
68288             
68289         }
68290         */
68291     
68292         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
68293     },
68294     // private
68295     onLoad : function()
68296     {
68297         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
68298     },
68299
68300     // private
68301     onBeforeLoad : function(){
68302         if(!this.disabled){
68303             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
68304         }
68305     },
68306
68307     // private
68308     destroy : function(){
68309         if(this.store){
68310             this.store.un('beforeload', this.onBeforeLoad, this);
68311             this.store.un('load', this.onLoad, this);
68312             this.store.un('loadexception', this.onLoadException, this);
68313         }else{
68314             var um = this.el.getUpdateManager();
68315             um.un('beforeupdate', this.onBeforeLoad, this);
68316             um.un('update', this.onLoad, this);
68317             um.un('failure', this.onLoad, this);
68318         }
68319     }
68320 };/*
68321  * Based on:
68322  * Ext JS Library 1.1.1
68323  * Copyright(c) 2006-2007, Ext JS, LLC.
68324  *
68325  * Originally Released Under LGPL - original licence link has changed is not relivant.
68326  *
68327  * Fork - LGPL
68328  * <script type="text/javascript">
68329  */
68330
68331
68332 /**
68333  * @class Roo.XTemplate
68334  * @extends Roo.Template
68335  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
68336 <pre><code>
68337 var t = new Roo.XTemplate(
68338         '&lt;select name="{name}"&gt;',
68339                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
68340         '&lt;/select&gt;'
68341 );
68342  
68343 // then append, applying the master template values
68344  </code></pre>
68345  *
68346  * Supported features:
68347  *
68348  *  Tags:
68349
68350 <pre><code>
68351       {a_variable} - output encoded.
68352       {a_variable.format:("Y-m-d")} - call a method on the variable
68353       {a_variable:raw} - unencoded output
68354       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
68355       {a_variable:this.method_on_template(...)} - call a method on the template object.
68356  
68357 </code></pre>
68358  *  The tpl tag:
68359 <pre><code>
68360         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
68361         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
68362         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
68363         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
68364   
68365         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
68366         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
68367 </code></pre>
68368  *      
68369  */
68370 Roo.XTemplate = function()
68371 {
68372     Roo.XTemplate.superclass.constructor.apply(this, arguments);
68373     if (this.html) {
68374         this.compile();
68375     }
68376 };
68377
68378
68379 Roo.extend(Roo.XTemplate, Roo.Template, {
68380
68381     /**
68382      * The various sub templates
68383      */
68384     tpls : false,
68385     /**
68386      *
68387      * basic tag replacing syntax
68388      * WORD:WORD()
68389      *
68390      * // you can fake an object call by doing this
68391      *  x.t:(test,tesT) 
68392      * 
68393      */
68394     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
68395
68396     /**
68397      * compile the template
68398      *
68399      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
68400      *
68401      */
68402     compile: function()
68403     {
68404         var s = this.html;
68405      
68406         s = ['<tpl>', s, '</tpl>'].join('');
68407     
68408         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
68409             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
68410             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
68411             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
68412             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
68413             m,
68414             id     = 0,
68415             tpls   = [];
68416     
68417         while(true == !!(m = s.match(re))){
68418             var forMatch   = m[0].match(nameRe),
68419                 ifMatch   = m[0].match(ifRe),
68420                 execMatch   = m[0].match(execRe),
68421                 namedMatch   = m[0].match(namedRe),
68422                 
68423                 exp  = null, 
68424                 fn   = null,
68425                 exec = null,
68426                 name = forMatch && forMatch[1] ? forMatch[1] : '';
68427                 
68428             if (ifMatch) {
68429                 // if - puts fn into test..
68430                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
68431                 if(exp){
68432                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
68433                 }
68434             }
68435             
68436             if (execMatch) {
68437                 // exec - calls a function... returns empty if true is  returned.
68438                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
68439                 if(exp){
68440                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
68441                 }
68442             }
68443             
68444             
68445             if (name) {
68446                 // for = 
68447                 switch(name){
68448                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
68449                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
68450                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
68451                 }
68452             }
68453             var uid = namedMatch ? namedMatch[1] : id;
68454             
68455             
68456             tpls.push({
68457                 id:     namedMatch ? namedMatch[1] : id,
68458                 target: name,
68459                 exec:   exec,
68460                 test:   fn,
68461                 body:   m[1] || ''
68462             });
68463             if (namedMatch) {
68464                 s = s.replace(m[0], '');
68465             } else { 
68466                 s = s.replace(m[0], '{xtpl'+ id + '}');
68467             }
68468             ++id;
68469         }
68470         this.tpls = [];
68471         for(var i = tpls.length-1; i >= 0; --i){
68472             this.compileTpl(tpls[i]);
68473             this.tpls[tpls[i].id] = tpls[i];
68474         }
68475         this.master = tpls[tpls.length-1];
68476         return this;
68477     },
68478     /**
68479      * same as applyTemplate, except it's done to one of the subTemplates
68480      * when using named templates, you can do:
68481      *
68482      * var str = pl.applySubTemplate('your-name', values);
68483      *
68484      * 
68485      * @param {Number} id of the template
68486      * @param {Object} values to apply to template
68487      * @param {Object} parent (normaly the instance of this object)
68488      */
68489     applySubTemplate : function(id, values, parent)
68490     {
68491         
68492         
68493         var t = this.tpls[id];
68494         
68495         
68496         try { 
68497             if(t.test && !t.test.call(this, values, parent)){
68498                 return '';
68499             }
68500         } catch(e) {
68501             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
68502             Roo.log(e.toString());
68503             Roo.log(t.test);
68504             return ''
68505         }
68506         try { 
68507             
68508             if(t.exec && t.exec.call(this, values, parent)){
68509                 return '';
68510             }
68511         } catch(e) {
68512             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
68513             Roo.log(e.toString());
68514             Roo.log(t.exec);
68515             return ''
68516         }
68517         try {
68518             var vs = t.target ? t.target.call(this, values, parent) : values;
68519             parent = t.target ? values : parent;
68520             if(t.target && vs instanceof Array){
68521                 var buf = [];
68522                 for(var i = 0, len = vs.length; i < len; i++){
68523                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
68524                 }
68525                 return buf.join('');
68526             }
68527             return t.compiled.call(this, vs, parent);
68528         } catch (e) {
68529             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
68530             Roo.log(e.toString());
68531             Roo.log(t.compiled);
68532             return '';
68533         }
68534     },
68535
68536     compileTpl : function(tpl)
68537     {
68538         var fm = Roo.util.Format;
68539         var useF = this.disableFormats !== true;
68540         var sep = Roo.isGecko ? "+" : ",";
68541         var undef = function(str) {
68542             Roo.log("Property not found :"  + str);
68543             return '';
68544         };
68545         
68546         var fn = function(m, name, format, args)
68547         {
68548             //Roo.log(arguments);
68549             args = args ? args.replace(/\\'/g,"'") : args;
68550             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
68551             if (typeof(format) == 'undefined') {
68552                 format= 'htmlEncode';
68553             }
68554             if (format == 'raw' ) {
68555                 format = false;
68556             }
68557             
68558             if(name.substr(0, 4) == 'xtpl'){
68559                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
68560             }
68561             
68562             // build an array of options to determine if value is undefined..
68563             
68564             // basically get 'xxxx.yyyy' then do
68565             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
68566             //    (function () { Roo.log("Property not found"); return ''; })() :
68567             //    ......
68568             
68569             var udef_ar = [];
68570             var lookfor = '';
68571             Roo.each(name.split('.'), function(st) {
68572                 lookfor += (lookfor.length ? '.': '') + st;
68573                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
68574             });
68575             
68576             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
68577             
68578             
68579             if(format && useF){
68580                 
68581                 args = args ? ',' + args : "";
68582                  
68583                 if(format.substr(0, 5) != "this."){
68584                     format = "fm." + format + '(';
68585                 }else{
68586                     format = 'this.call("'+ format.substr(5) + '", ';
68587                     args = ", values";
68588                 }
68589                 
68590                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
68591             }
68592              
68593             if (args.length) {
68594                 // called with xxyx.yuu:(test,test)
68595                 // change to ()
68596                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
68597             }
68598             // raw.. - :raw modifier..
68599             return "'"+ sep + udef_st  + name + ")"+sep+"'";
68600             
68601         };
68602         var body;
68603         // branched to use + in gecko and [].join() in others
68604         if(Roo.isGecko){
68605             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
68606                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
68607                     "';};};";
68608         }else{
68609             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
68610             body.push(tpl.body.replace(/(\r\n|\n)/g,
68611                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
68612             body.push("'].join('');};};");
68613             body = body.join('');
68614         }
68615         
68616         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
68617        
68618         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
68619         eval(body);
68620         
68621         return this;
68622     },
68623
68624     applyTemplate : function(values){
68625         return this.master.compiled.call(this, values, {});
68626         //var s = this.subs;
68627     },
68628
68629     apply : function(){
68630         return this.applyTemplate.apply(this, arguments);
68631     }
68632
68633  });
68634
68635 Roo.XTemplate.from = function(el){
68636     el = Roo.getDom(el);
68637     return new Roo.XTemplate(el.value || el.innerHTML);
68638 };Roo.dialog = {};
68639 /*
68640 * Licence: LGPL
68641 */
68642
68643 /**
68644  * @class Roo.dialog.UploadCropbox
68645  * @extends Roo.BoxComponent
68646  * Dialog UploadCropbox class
68647  * @cfg {String} emptyText show when image has been loaded
68648  * @cfg {String} rotateNotify show when image too small to rotate
68649  * @cfg {Number} errorTimeout default 3000
68650  * @cfg {Number} minWidth default 300
68651  * @cfg {Number} minHeight default 300
68652  * @cfg {Number} outputMaxWidth default 1200
68653  * @cfg {Number} windowSize default 300
68654  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
68655  * @cfg {Boolean} isDocument (true|false) default false
68656  * @cfg {String} url action url
68657  * @cfg {String} paramName default 'imageUpload'
68658  * @cfg {String} method default POST
68659  * @cfg {Boolean} loadMask (true|false) default true
68660  * @cfg {Boolean} loadingText default 'Loading...'
68661  * 
68662  * @constructor
68663  * Create a new UploadCropbox
68664  * @param {Object} config The config object
68665  */
68666
68667  Roo.dialog.UploadCropbox = function(config){
68668     Roo.dialog.UploadCropbox.superclass.constructor.call(this, config);
68669     
68670     this.addEvents({
68671         /**
68672          * @event beforeselectfile
68673          * Fire before select file
68674          * @param {Roo.dialog.UploadCropbox} this
68675          */
68676         "beforeselectfile" : true,
68677         /**
68678          * @event initial
68679          * Fire after initEvent
68680          * @param {Roo.dialog.UploadCropbox} this
68681          */
68682         "initial" : true,
68683         /**
68684          * @event crop
68685          * Fire after initEvent
68686          * @param {Roo.dialog.UploadCropbox} this
68687          * @param {String} data
68688          */
68689         "crop" : true,
68690         /**
68691          * @event prepare
68692          * Fire when preparing the file data
68693          * @param {Roo.dialog.UploadCropbox} this
68694          * @param {Object} file
68695          */
68696         "prepare" : true,
68697         /**
68698          * @event exception
68699          * Fire when get exception
68700          * @param {Roo.dialog.UploadCropbox} this
68701          * @param {XMLHttpRequest} xhr
68702          */
68703         "exception" : true,
68704         /**
68705          * @event beforeloadcanvas
68706          * Fire before load the canvas
68707          * @param {Roo.dialog.UploadCropbox} this
68708          * @param {String} src
68709          */
68710         "beforeloadcanvas" : true,
68711         /**
68712          * @event trash
68713          * Fire when trash image
68714          * @param {Roo.dialog.UploadCropbox} this
68715          */
68716         "trash" : true,
68717         /**
68718          * @event download
68719          * Fire when download the image
68720          * @param {Roo.dialog.UploadCropbox} this
68721          */
68722         "download" : true,
68723         /**
68724          * @event footerbuttonclick
68725          * Fire when footerbuttonclick
68726          * @param {Roo.dialog.UploadCropbox} this
68727          * @param {String} type
68728          */
68729         "footerbuttonclick" : true,
68730         /**
68731          * @event resize
68732          * Fire when resize
68733          * @param {Roo.dialog.UploadCropbox} this
68734          */
68735         "resize" : true,
68736         /**
68737          * @event rotate
68738          * Fire when rotate the image
68739          * @param {Roo.dialog.UploadCropbox} this
68740          * @param {String} pos
68741          */
68742         "rotate" : true,
68743         /**
68744          * @event inspect
68745          * Fire when inspect the file
68746          * @param {Roo.dialog.UploadCropbox} this
68747          * @param {Object} file
68748          */
68749         "inspect" : true,
68750         /**
68751          * @event upload
68752          * Fire when xhr upload the file
68753          * @param {Roo.dialog.UploadCropbox} this
68754          * @param {Object} data
68755          */
68756         "upload" : true,
68757         /**
68758          * @event arrange
68759          * Fire when arrange the file data
68760          * @param {Roo.dialog.UploadCropbox} this
68761          * @param {Object} formData
68762          */
68763         "arrange" : true,
68764         /**
68765          * @event loadcanvas
68766          * Fire after load the canvas
68767          * @param {Roo.dialog.UploadCropbox}
68768          * @param {Object} imgEl
68769          */
68770         "loadcanvas" : true
68771     });
68772     
68773     this.buttons = this.buttons || Roo.dialog.UploadCropbox.footer.STANDARD;
68774 };
68775
68776 Roo.extend(Roo.dialog.UploadCropbox, Roo.Component,  {
68777     
68778     emptyText : 'Click to upload image',
68779     rotateNotify : 'Image is too small to rotate',
68780     errorTimeout : 3000,
68781     scale : 0,
68782     baseScale : 1,
68783     rotate : 0,
68784     dragable : false,
68785     pinching : false,
68786     mouseX : 0,
68787     mouseY : 0,
68788     cropData : false,
68789     minWidth : 300,
68790     minHeight : 300,
68791     outputMaxWidth : 1200,
68792     windowSize : 300,
68793     file : false,
68794     exif : {},
68795     baseRotate : 1,
68796     cropType : 'image/jpeg',
68797     buttons : false,
68798     canvasLoaded : false,
68799     isDocument : false,
68800     method : 'POST',
68801     paramName : 'imageUpload',
68802     loadMask : true,
68803     loadingText : 'Loading...',
68804     maskEl : false,
68805     
68806     getAutoCreate : function()
68807     {
68808         var cfg = {
68809             tag : 'div',
68810             cls : 'roo-upload-cropbox',
68811             cn : [
68812                 {
68813                     tag : 'input',
68814                     cls : 'roo-upload-cropbox-selector',
68815                     type : 'file'
68816                 },
68817                 {
68818                     tag : 'div',
68819                     cls : 'roo-upload-cropbox-body',
68820                     style : 'cursor:pointer',
68821                     cn : [
68822                         {
68823                             tag : 'div',
68824                             cls : 'roo-upload-cropbox-preview'
68825                         },
68826                         {
68827                             tag : 'div',
68828                             cls : 'roo-upload-cropbox-thumb'
68829                         },
68830                         {
68831                             tag : 'div',
68832                             cls : 'roo-upload-cropbox-empty-notify',
68833                             html : this.emptyText
68834                         },
68835                         {
68836                             tag : 'div',
68837                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
68838                             html : this.rotateNotify
68839                         }
68840                     ]
68841                 },
68842                 {
68843                     tag : 'div',
68844                     cls : 'roo-upload-cropbox-footer',
68845                     cn : {
68846                         tag : 'div',
68847                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
68848                         cn : []
68849                     }
68850                 }
68851             ]
68852         };
68853         
68854         return cfg;
68855     },
68856     
68857     onRender : function(ct, position)
68858     {
68859         Roo.dialog.UploadCropbox.superclass.onRender.call(this, ct, position);
68860
68861         if(this.el){
68862             if (this.el.attr('xtype')) {
68863                 this.el.attr('xtypex', this.el.attr('xtype'));
68864                 this.el.dom.removeAttribute('xtype');
68865                 
68866                 this.initEvents();
68867             }
68868         }
68869         else {
68870             var cfg = Roo.apply({},  this.getAutoCreate());
68871         
68872             cfg.id = this.id || Roo.id();
68873             
68874             if (this.cls) {
68875                 cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
68876             }
68877             
68878             if (this.style) { // fixme needs to support more complex style data.
68879                 cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
68880             }
68881             
68882             this.el = ct.createChild(cfg, position);
68883             
68884             this.initEvents();
68885         }
68886         
68887         if (this.buttons.length) {
68888             
68889             Roo.each(this.buttons, function(bb) {
68890                 
68891                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
68892                 
68893                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
68894                 
68895             }, this);
68896         }
68897         
68898         if(this.loadMask){
68899             this.maskEl = this.el;
68900         }
68901     },
68902     
68903     initEvents : function()
68904     {
68905         this.urlAPI = (window.createObjectURL && window) || 
68906                                 (window.URL && URL.revokeObjectURL && URL) || 
68907                                 (window.webkitURL && webkitURL);
68908                         
68909         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
68910         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
68911         
68912         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
68913         this.selectorEl.hide();
68914         
68915         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
68916         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
68917         
68918         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
68919         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
68920         this.thumbEl.hide();
68921         
68922         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
68923         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
68924         
68925         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
68926         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
68927         this.errorEl.hide();
68928         
68929         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
68930         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
68931         this.footerEl.hide();
68932         
68933         this.setThumbBoxSize();
68934         
68935         this.bind();
68936         
68937         this.resize();
68938         
68939         this.fireEvent('initial', this);
68940     },
68941
68942     bind : function()
68943     {
68944         var _this = this;
68945         
68946         window.addEventListener("resize", function() { _this.resize(); } );
68947         
68948         this.bodyEl.on('click', this.beforeSelectFile, this);
68949         
68950         if(Roo.isTouch){
68951             this.bodyEl.on('touchstart', this.onTouchStart, this);
68952             this.bodyEl.on('touchmove', this.onTouchMove, this);
68953             this.bodyEl.on('touchend', this.onTouchEnd, this);
68954         }
68955         
68956         if(!Roo.isTouch){
68957             this.bodyEl.on('mousedown', this.onMouseDown, this);
68958             this.bodyEl.on('mousemove', this.onMouseMove, this);
68959             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
68960             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
68961             Roo.get(document).on('mouseup', this.onMouseUp, this);
68962         }
68963         
68964         this.selectorEl.on('change', this.onFileSelected, this);
68965     },
68966     
68967     reset : function()
68968     {    
68969         this.scale = 0;
68970         this.baseScale = 1;
68971         this.rotate = 0;
68972         this.baseRotate = 1;
68973         this.dragable = false;
68974         this.pinching = false;
68975         this.mouseX = 0;
68976         this.mouseY = 0;
68977         this.cropData = false;
68978         this.notifyEl.dom.innerHTML = this.emptyText;
68979         
68980         // this.selectorEl.dom.value = '';
68981         
68982     },
68983     
68984     resize : function()
68985     {
68986         if(this.fireEvent('resize', this) != false){
68987             this.setThumbBoxPosition();
68988             this.setCanvasPosition();
68989         }
68990     },
68991     
68992     onFooterButtonClick : function(e, el, o, type)
68993     {
68994         switch (type) {
68995             case 'rotate-left' :
68996                 this.onRotateLeft(e);
68997                 break;
68998             case 'rotate-right' :
68999                 this.onRotateRight(e);
69000                 break;
69001             case 'picture' :
69002                 this.beforeSelectFile(e);
69003                 break;
69004             case 'trash' :
69005                 this.trash(e);
69006                 break;
69007             case 'crop' :
69008                 this.crop(e);
69009                 break;
69010             case 'download' :
69011                 this.download(e);
69012                 break;
69013             case 'center' :
69014                 this.center(e);
69015                 break;
69016             default :
69017                 break;
69018         }
69019         
69020         this.fireEvent('footerbuttonclick', this, type);
69021     },
69022     
69023     beforeSelectFile : function(e)
69024     {
69025         e.preventDefault();
69026         
69027         if(this.fireEvent('beforeselectfile', this) != false){
69028             this.selectorEl.dom.click();
69029         }
69030     },
69031     
69032     onFileSelected : function(e)
69033     {
69034         e.preventDefault();
69035         
69036         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
69037             return;
69038         }
69039         
69040         var file = this.selectorEl.dom.files[0];
69041         
69042         if(this.fireEvent('inspect', this, file) != false){
69043             this.prepare(file);
69044         }
69045         
69046     },
69047     
69048     trash : function(e)
69049     {
69050         this.fireEvent('trash', this);
69051     },
69052     
69053     download : function(e)
69054     {
69055         this.fireEvent('download', this);
69056     },
69057
69058     center : function(e)
69059     {
69060         this.setCanvasPosition();
69061     },
69062     
69063     loadCanvas : function(src)
69064     {   
69065         if(this.fireEvent('beforeloadcanvas', this, src) != false){
69066             
69067             this.reset();
69068             
69069             this.imageEl = document.createElement('img');
69070             
69071             var _this = this;
69072             
69073             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
69074             
69075             this.imageEl.src = src;
69076         }
69077     },
69078     
69079     onLoadCanvas : function()
69080     {   
69081         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
69082         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
69083
69084         if(this.fireEvent('loadcanvas', this, this.imageEl) != false){
69085         
69086             this.bodyEl.un('click', this.beforeSelectFile, this);
69087             
69088             this.notifyEl.hide();
69089             this.thumbEl.show();
69090             this.footerEl.show();
69091             
69092             this.baseRotateLevel();
69093             
69094             if(this.isDocument){
69095                 this.setThumbBoxSize();
69096             }
69097             
69098             this.setThumbBoxPosition();
69099             
69100             this.baseScaleLevel();
69101             
69102             this.draw();
69103             
69104             this.resize();
69105             
69106             this.canvasLoaded = true;
69107         
69108         }
69109         
69110         if(this.loadMask){
69111             this.maskEl.unmask();
69112         }
69113         
69114     },
69115     
69116     setCanvasPosition : function(center = true)
69117     {   
69118         if(!this.canvasEl){
69119             return;
69120         }
69121
69122         var newCenterLeft = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
69123         var newCenterTop = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
69124
69125         if(center) {
69126             this.previewEl.setLeft(newCenterLeft);
69127             this.previewEl.setTop(newCenterTop);
69128
69129             return;
69130         }
69131         
69132         var oldScaleLevel = this.baseScale * Math.pow(1.02, this.startScale);
69133         var oldCanvasWidth = Math.floor(this.imageEl.OriginWidth * oldScaleLevel);
69134         var oldCanvasHeight = Math.floor(this.imageEl.OriginHeight * oldScaleLevel);
69135
69136         var oldCenterLeft = Math.ceil((this.bodyEl.getWidth() - oldCanvasWidth) / 2);
69137         var oldCenterTop = Math.ceil((this.bodyEl.getHeight() - oldCanvasHeight) / 2);
69138
69139         var leftDiff = newCenterLeft - oldCenterLeft;
69140         var topDiff = newCenterTop - oldCenterTop;
69141
69142         var newPreviewLeft = this.previewEl.getLeft(true) + leftDiff;
69143         var newPreviewTop = this.previewEl.getTop(true) + topDiff;
69144
69145         this.previewEl.setLeft(newPreviewLeft);
69146         this.previewEl.setTop(newPreviewTop);
69147         
69148     },
69149     
69150     onMouseDown : function(e)
69151     {   
69152         e.stopEvent();
69153         
69154         this.dragable = true;
69155         this.pinching = false;
69156         
69157         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
69158             this.dragable = false;
69159             return;
69160         }
69161         
69162         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
69163         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
69164         
69165     },
69166     
69167     onMouseMove : function(e)
69168     {   
69169         e.stopEvent();
69170         
69171         if(!this.canvasLoaded){
69172             return;
69173         }
69174         
69175         if (!this.dragable){
69176             return;
69177         }
69178
69179         var maxPaddingLeft = this.canvasEl.width / 0.9 * 0.05;
69180         var maxPaddingTop = maxPaddingLeft * this.minHeight / this.minWidth;
69181
69182         if ((this.imageEl.OriginWidth / this.imageEl.OriginHeight <= this.minWidth / this.minHeight)) {
69183             maxPaddingLeft = (this.canvasEl.height * this.minWidth / this.minHeight - this.canvasEl.width) / 2 + maxPaddingLeft;
69184         }
69185
69186         if ((this.imageEl.OriginWidth / this.imageEl.OriginHeight >= this.minWidth / this.minHeight)) {
69187             maxPaddingTop = (this.canvasEl.width * this.minHeight / this.minWidth - this.canvasEl.height) / 2 + maxPaddingTop;
69188         }
69189         
69190         var minX = Math.ceil(this.thumbEl.getLeft(true) + this.thumbEl.getWidth() - this.canvasEl.width - maxPaddingLeft);
69191         var minY = Math.ceil(this.thumbEl.getTop(true) + this.thumbEl.getHeight() - this.canvasEl.height - maxPaddingTop);
69192         
69193         var maxX = Math.ceil(this.thumbEl.getLeft(true) + maxPaddingLeft);
69194         var maxY = Math.ceil(this.thumbEl.getTop(true) +  maxPaddingTop);
69195
69196         if(minX > maxX) {
69197             var tempX = minX;
69198             minX = maxX;
69199             maxX = tempX;
69200         }
69201
69202         if(minY > maxY) {
69203             var tempY = minY;
69204             minY = maxY;
69205             maxY = tempY;
69206         }
69207
69208         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
69209         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
69210         
69211         x = x - this.mouseX;
69212         y = y - this.mouseY;
69213
69214         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
69215         var bgY = Math.ceil(y + this.previewEl.getTop(true));
69216         
69217         bgX = (bgX < minX) ? minX : ((bgX > maxX) ? maxX : bgX);
69218         bgY = (bgY < minY) ? minY : ((bgY > maxY) ? maxY : bgY);
69219         
69220         this.previewEl.setLeft(bgX);
69221         this.previewEl.setTop(bgY);
69222         
69223         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
69224         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
69225     },
69226     
69227     onMouseUp : function(e)
69228     {   
69229         e.stopEvent();
69230         
69231         this.dragable = false;
69232     },
69233     
69234     onMouseWheel : function(e)
69235     {   
69236         e.stopEvent();
69237         
69238         this.startScale = this.scale;
69239         this.scale = (e.getWheelDelta() > 0) ? (this.scale + 1) : (this.scale - 1);
69240         
69241         if(!this.zoomable()){
69242             this.scale = this.startScale;
69243             return;
69244         }
69245
69246         
69247         this.draw();
69248         
69249         return;
69250     },
69251     
69252     zoomable : function()
69253     {
69254         var minScale = this.thumbEl.getWidth() / this.minWidth;
69255         
69256         if(this.minWidth < this.minHeight){
69257             minScale = this.thumbEl.getHeight() / this.minHeight;
69258         }
69259         
69260         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
69261         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
69262  
69263         var maxWidth = this.imageEl.OriginWidth;
69264         var maxHeight = this.imageEl.OriginHeight;
69265
69266
69267         var newCanvasWidth = Math.floor(this.imageEl.OriginWidth * this.getScaleLevel());
69268         var newCanvasHeight = Math.floor(this.imageEl.OriginHeight * this.getScaleLevel());
69269
69270         var oldCenterLeft = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
69271         var oldCenterTop = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
69272
69273         var newCenterLeft = Math.ceil((this.bodyEl.getWidth() - newCanvasWidth) / 2);
69274         var newCenterTop = Math.ceil((this.bodyEl.getHeight() - newCanvasHeight) / 2);
69275
69276         var leftDiff = newCenterLeft - oldCenterLeft;
69277         var topDiff = newCenterTop - oldCenterTop;
69278
69279         var newPreviewLeft = this.previewEl.getLeft(true) + leftDiff;
69280         var newPreviewTop = this.previewEl.getTop(true) + topDiff;
69281
69282         var paddingLeft = newPreviewLeft - this.thumbEl.getLeft(true);
69283         var paddingTop = newPreviewTop - this.thumbEl.getTop(true);
69284
69285         var paddingRight = this.thumbEl.getLeft(true) + this.thumbEl.getWidth() - newCanvasWidth - newPreviewLeft;
69286         var paddingBottom = this.thumbEl.getTop(true) + this.thumbEl.getHeight() - newCanvasHeight - newPreviewTop;
69287
69288         var maxPaddingLeft = newCanvasWidth / 0.9 * 0.05;
69289         var maxPaddingTop = maxPaddingLeft * this.minHeight / this.minWidth;
69290
69291         if ((this.imageEl.OriginWidth / this.imageEl.OriginHeight <= this.minWidth / this.minHeight)) {
69292             maxPaddingLeft = (newCanvasHeight * this.minWidth / this.minHeight - newCanvasWidth) / 2 + maxPaddingLeft;
69293         }
69294
69295         if ((this.imageEl.OriginWidth / this.imageEl.OriginHeight >= this.minWidth / this.minHeight)) {
69296             maxPaddingTop = (newCanvasWidth * this.minHeight / this.minWidth - newCanvasHeight) / 2 + maxPaddingTop;
69297         }
69298         
69299         if(
69300                 this.isDocument &&
69301                 (this.rotate == 0 || this.rotate == 180) && 
69302                 (
69303                     width > this.imageEl.OriginWidth || 
69304                     height > this.imageEl.OriginHeight ||
69305                     (width < this.minWidth && height < this.minHeight)
69306                 )
69307         ){
69308             return false;
69309         }
69310         
69311         if(
69312                 this.isDocument &&
69313                 (this.rotate == 90 || this.rotate == 270) && 
69314                 (
69315                     width > this.imageEl.OriginWidth || 
69316                     height > this.imageEl.OriginHeight ||
69317                     (width < this.minHeight && height < this.minWidth)
69318                 )
69319         ){
69320             return false;
69321         }
69322         
69323         if(
69324                 !this.isDocument &&
69325                 (this.rotate == 0 || this.rotate == 180) && 
69326                 (
69327                     // for zoom out
69328                     paddingLeft > maxPaddingLeft ||
69329                     paddingRight > maxPaddingLeft ||
69330                     paddingTop > maxPaddingTop ||
69331                     paddingBottom > maxPaddingTop ||
69332                     // for zoom in
69333                     width > maxWidth ||
69334                     height > maxHeight
69335                 )
69336         ){
69337             return false;
69338         }
69339         
69340         if(
69341                 !this.isDocument &&
69342                 (this.rotate == 90 || this.rotate == 270) && 
69343                 (
69344                     width < this.minHeight || 
69345                     width > this.imageEl.OriginWidth || 
69346                     height < this.minWidth || 
69347                     height > this.imageEl.OriginHeight
69348                 )
69349         ){
69350             return false;
69351         }
69352         
69353         return true;
69354         
69355     },
69356     
69357     onRotateLeft : function(e)
69358     {   
69359         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
69360             
69361             var minScale = this.thumbEl.getWidth() / this.minWidth;
69362             
69363             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
69364             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
69365             
69366             this.startScale = this.scale;
69367             
69368             while (this.getScaleLevel() < minScale){
69369             
69370                 this.scale = this.scale + 1;
69371                 
69372                 if(!this.zoomable()){
69373                     break;
69374                 }
69375                 
69376                 if(
69377                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
69378                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
69379                 ){
69380                     continue;
69381                 }
69382                 
69383                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
69384
69385                 this.draw();
69386                 
69387                 return;
69388             }
69389             
69390             this.scale = this.startScale;
69391             
69392             this.onRotateFail();
69393             
69394             return false;
69395         }
69396         
69397         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
69398
69399         if(this.isDocument){
69400             this.setThumbBoxSize();
69401             this.setThumbBoxPosition();
69402             this.setCanvasPosition();
69403         }
69404         
69405         this.draw();
69406         
69407         this.fireEvent('rotate', this, 'left');
69408         
69409     },
69410     
69411     onRotateRight : function(e)
69412     {
69413         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
69414             
69415             var minScale = this.thumbEl.getWidth() / this.minWidth;
69416         
69417             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
69418             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
69419             
69420             this.startScale = this.scale;
69421             
69422             while (this.getScaleLevel() < minScale){
69423             
69424                 this.scale = this.scale + 1;
69425                 
69426                 if(!this.zoomable()){
69427                     break;
69428                 }
69429                 
69430                 if(
69431                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
69432                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
69433                 ){
69434                     continue;
69435                 }
69436                 
69437                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
69438
69439                 this.draw();
69440                 
69441                 return;
69442             }
69443             
69444             this.scale = this.startScale;
69445             
69446             this.onRotateFail();
69447             
69448             return false;
69449         }
69450         
69451         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
69452
69453         if(this.isDocument){
69454             this.setThumbBoxSize();
69455             this.setThumbBoxPosition();
69456             this.setCanvasPosition();
69457         }
69458         
69459         this.draw();
69460         
69461         this.fireEvent('rotate', this, 'right');
69462     },
69463     
69464     onRotateFail : function()
69465     {
69466         this.errorEl.show(true);
69467         
69468         var _this = this;
69469         
69470         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
69471     },
69472     
69473     draw : function()
69474     {
69475         this.previewEl.dom.innerHTML = '';
69476         
69477         var canvasEl = document.createElement("canvas");
69478         
69479         var contextEl = canvasEl.getContext("2d");
69480         
69481         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
69482         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
69483         var center = this.imageEl.OriginWidth / 2;
69484         
69485         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
69486             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
69487             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
69488             center = this.imageEl.OriginHeight / 2;
69489         }
69490         
69491         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
69492         
69493         contextEl.translate(center, center);
69494         contextEl.rotate(this.rotate * Math.PI / 180);
69495
69496         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
69497         
69498         this.canvasEl = document.createElement("canvas");
69499         
69500         this.contextEl = this.canvasEl.getContext("2d");
69501         
69502         switch (this.rotate) {
69503             case 0 :
69504                 
69505                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
69506                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
69507                 
69508                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
69509                 
69510                 break;
69511             case 90 : 
69512                 
69513                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
69514                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
69515                 
69516                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
69517                     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);
69518                     break;
69519                 }
69520                 
69521                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
69522                 
69523                 break;
69524             case 180 :
69525                 
69526                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
69527                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
69528                 
69529                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
69530                     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);
69531                     break;
69532                 }
69533                 
69534                 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);
69535                 
69536                 break;
69537             case 270 :
69538                 
69539                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
69540                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
69541         
69542                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
69543                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
69544                     break;
69545                 }
69546                 
69547                 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);
69548                 
69549                 break;
69550             default : 
69551                 break;
69552         }
69553         
69554         this.previewEl.appendChild(this.canvasEl);
69555         
69556         this.setCanvasPosition(false);
69557     },
69558     
69559     crop : function()
69560     {
69561         if(!this.canvasLoaded){
69562             return;
69563         }
69564         
69565         var imageCanvas = document.createElement("canvas");
69566         
69567         var imageContext = imageCanvas.getContext("2d");
69568         
69569         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
69570         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
69571         
69572         var center = imageCanvas.width / 2;
69573         
69574         imageContext.translate(center, center);
69575         
69576         imageContext.rotate(this.rotate * Math.PI / 180);
69577         
69578         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
69579         
69580         var canvas = document.createElement("canvas");
69581         
69582         var context = canvas.getContext("2d");
69583
69584         canvas.width = this.thumbEl.getWidth() / this.getScaleLevel();
69585         
69586         canvas.height = this.thumbEl.getHeight() / this.getScaleLevel();
69587
69588         switch (this.rotate) {
69589             case 0 :
69590                 
69591                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
69592                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
69593                 
69594                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
69595                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
69596                 
69597                 var sx = this.thumbEl.getLeft(true) - this.previewEl.getLeft(true);
69598                 var sy = this.thumbEl.getTop(true) - this.previewEl.getTop(true);
69599
69600                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
69601                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
69602
69603                 if(canvas.width > this.outputMaxWidth) {
69604                     var scale = this.outputMaxWidth / canvas.width;
69605                     canvas.width = canvas.width * scale;
69606                     canvas.height = canvas.height * scale;
69607                     context.scale(scale, scale);
69608                 }
69609
69610                 context.fillStyle = 'white';
69611                 context.fillRect(0, 0, this.thumbEl.getWidth() / this.getScaleLevel(), this.thumbEl.getHeight() / this.getScaleLevel());
69612
69613
69614                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
69615                 
69616                 break;
69617             case 90 : 
69618                 
69619                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
69620                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
69621                 
69622                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
69623                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
69624                 
69625                 var targetWidth = this.minWidth - 2 * x;
69626                 var targetHeight = this.minHeight - 2 * y;
69627                 
69628                 var scale = 1;
69629                 
69630                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
69631                     scale = targetWidth / width;
69632                 }
69633                 
69634                 if(x > 0 && y == 0){
69635                     scale = targetHeight / height;
69636                 }
69637                 
69638                 if(x > 0 && y > 0){
69639                     scale = targetWidth / width;
69640                     
69641                     if(width < height){
69642                         scale = targetHeight / height;
69643                     }
69644                 }
69645                 
69646                 context.scale(scale, scale);
69647                 
69648                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
69649                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
69650
69651                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
69652                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
69653                 
69654                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
69655                 
69656                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
69657                 
69658                 break;
69659             case 180 :
69660                 
69661                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
69662                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
69663                 
69664                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
69665                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
69666                 
69667                 var targetWidth = this.minWidth - 2 * x;
69668                 var targetHeight = this.minHeight - 2 * y;
69669                 
69670                 var scale = 1;
69671                 
69672                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
69673                     scale = targetWidth / width;
69674                 }
69675                 
69676                 if(x > 0 && y == 0){
69677                     scale = targetHeight / height;
69678                 }
69679                 
69680                 if(x > 0 && y > 0){
69681                     scale = targetWidth / width;
69682                     
69683                     if(width < height){
69684                         scale = targetHeight / height;
69685                     }
69686                 }
69687                 
69688                 context.scale(scale, scale);
69689                 
69690                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
69691                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
69692
69693                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
69694                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
69695
69696                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
69697                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
69698                 
69699                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
69700                 
69701                 break;
69702             case 270 :
69703                 
69704                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
69705                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
69706                 
69707                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
69708                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
69709                 
69710                 var targetWidth = this.minWidth - 2 * x;
69711                 var targetHeight = this.minHeight - 2 * y;
69712                 
69713                 var scale = 1;
69714                 
69715                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
69716                     scale = targetWidth / width;
69717                 }
69718                 
69719                 if(x > 0 && y == 0){
69720                     scale = targetHeight / height;
69721                 }
69722                 
69723                 if(x > 0 && y > 0){
69724                     scale = targetWidth / width;
69725                     
69726                     if(width < height){
69727                         scale = targetHeight / height;
69728                     }
69729                 }
69730                 
69731                 context.scale(scale, scale);
69732                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
69733                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
69734
69735                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
69736                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
69737                 
69738                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
69739                 
69740                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
69741                 
69742                 break;
69743             default : 
69744                 break;
69745         }
69746         
69747         this.cropData = canvas.toDataURL(this.cropType);
69748         
69749         if(this.fireEvent('crop', this, this.cropData) !== false){
69750             this.process(this.file, this.cropData);
69751         }
69752         
69753         return;
69754         
69755     },
69756     
69757     setThumbBoxSize : function()
69758     {
69759         var width, height;
69760         
69761         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
69762             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
69763             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
69764             
69765             this.minWidth = width;
69766             this.minHeight = height;
69767             
69768             if(this.rotate == 90 || this.rotate == 270){
69769                 this.minWidth = height;
69770                 this.minHeight = width;
69771             }
69772         }
69773         
69774         height = this.windowSize;
69775         width = Math.ceil(this.minWidth * height / this.minHeight);
69776         
69777         if(this.minWidth > this.minHeight){
69778             width = this.windowSize;
69779             height = Math.ceil(this.minHeight * width / this.minWidth);
69780         }
69781         
69782         this.thumbEl.setStyle({
69783             width : width + 'px',
69784             height : height + 'px'
69785         });
69786
69787         return;
69788             
69789     },
69790     
69791     setThumbBoxPosition : function()
69792     {
69793         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
69794         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
69795         
69796         this.thumbEl.setLeft(x);
69797         this.thumbEl.setTop(y);
69798         
69799     },
69800     
69801     baseRotateLevel : function()
69802     {
69803         this.baseRotate = 1;
69804         
69805         if(
69806                 typeof(this.exif) != 'undefined' &&
69807                 typeof(this.exif[Roo.dialog.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
69808                 [1, 3, 6, 8].indexOf(this.exif[Roo.dialog.UploadCropbox['tags']['Orientation']]) != -1
69809         ){
69810             this.baseRotate = this.exif[Roo.dialog.UploadCropbox['tags']['Orientation']];
69811         }
69812         
69813         this.rotate = Roo.dialog.UploadCropbox['Orientation'][this.baseRotate];
69814         
69815     },
69816     
69817     baseScaleLevel : function()
69818     {
69819         var width, height;
69820         
69821         if(this.isDocument){
69822             
69823             if(this.baseRotate == 6 || this.baseRotate == 8){
69824             
69825                 height = this.thumbEl.getHeight();
69826                 this.baseScale = height / this.imageEl.OriginWidth;
69827
69828                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
69829                     width = this.thumbEl.getWidth();
69830                     this.baseScale = width / this.imageEl.OriginHeight;
69831                 }
69832
69833                 return;
69834             }
69835
69836             height = this.thumbEl.getHeight();
69837             this.baseScale = height / this.imageEl.OriginHeight;
69838
69839             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
69840                 width = this.thumbEl.getWidth();
69841                 this.baseScale = width / this.imageEl.OriginWidth;
69842             }
69843
69844             return;
69845         }
69846         
69847         if(this.baseRotate == 6 || this.baseRotate == 8){
69848             
69849             width = this.thumbEl.getHeight();
69850             this.baseScale = width / this.imageEl.OriginHeight;
69851             
69852             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
69853                 height = this.thumbEl.getWidth();
69854                 this.baseScale = height / this.imageEl.OriginHeight;
69855             }
69856             
69857             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
69858                 height = this.thumbEl.getWidth();
69859                 this.baseScale = height / this.imageEl.OriginHeight;
69860                 
69861                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
69862                     width = this.thumbEl.getHeight();
69863                     this.baseScale = width / this.imageEl.OriginWidth;
69864                 }
69865             }
69866             
69867             return;
69868         }
69869         
69870         width = this.thumbEl.getWidth();
69871         this.baseScale = width / this.imageEl.OriginWidth;
69872         
69873         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
69874             height = this.thumbEl.getHeight();
69875             this.baseScale = height / this.imageEl.OriginHeight;
69876         }
69877         
69878         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
69879             
69880             height = this.thumbEl.getHeight();
69881             this.baseScale = height / this.imageEl.OriginHeight;
69882             
69883             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
69884                 width = this.thumbEl.getWidth();
69885                 this.baseScale = width / this.imageEl.OriginWidth;
69886             }
69887             
69888         }
69889
69890         if(this.imageEl.OriginWidth < this.minWidth || this.imageEl.OriginHeight < this.minHeight) {
69891             this.baseScale = width / this.minWidth;
69892         }
69893
69894         return;
69895     },
69896     
69897     getScaleLevel : function()
69898     {
69899         return this.baseScale * Math.pow(1.02, this.scale);
69900     },
69901     
69902     onTouchStart : function(e)
69903     {
69904         if(!this.canvasLoaded){
69905             this.beforeSelectFile(e);
69906             return;
69907         }
69908         
69909         var touches = e.browserEvent.touches;
69910         
69911         if(!touches){
69912             return;
69913         }
69914         
69915         if(touches.length == 1){
69916             this.onMouseDown(e);
69917             return;
69918         }
69919         
69920         if(touches.length != 2){
69921             return;
69922         }
69923         
69924         var coords = [];
69925         
69926         for(var i = 0, finger; finger = touches[i]; i++){
69927             coords.push(finger.pageX, finger.pageY);
69928         }
69929         
69930         var x = Math.pow(coords[0] - coords[2], 2);
69931         var y = Math.pow(coords[1] - coords[3], 2);
69932         
69933         this.startDistance = Math.sqrt(x + y);
69934         
69935         this.startScale = this.scale;
69936         
69937         this.pinching = true;
69938         this.dragable = false;
69939         
69940     },
69941     
69942     onTouchMove : function(e)
69943     {
69944         if(!this.pinching && !this.dragable){
69945             return;
69946         }
69947         
69948         var touches = e.browserEvent.touches;
69949         
69950         if(!touches){
69951             return;
69952         }
69953         
69954         if(this.dragable){
69955             this.onMouseMove(e);
69956             return;
69957         }
69958         
69959         var coords = [];
69960         
69961         for(var i = 0, finger; finger = touches[i]; i++){
69962             coords.push(finger.pageX, finger.pageY);
69963         }
69964         
69965         var x = Math.pow(coords[0] - coords[2], 2);
69966         var y = Math.pow(coords[1] - coords[3], 2);
69967         
69968         this.endDistance = Math.sqrt(x + y);
69969         
69970         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
69971         
69972         if(!this.zoomable()){
69973             this.scale = this.startScale;
69974             return;
69975         }
69976         
69977         this.draw();
69978         
69979     },
69980     
69981     onTouchEnd : function(e)
69982     {
69983         this.pinching = false;
69984         this.dragable = false;
69985         
69986     },
69987     
69988     process : function(file, crop)
69989     {
69990         if(this.loadMask){
69991             this.maskEl.mask(this.loadingText);
69992         }
69993         
69994         this.xhr = new XMLHttpRequest();
69995         
69996         file.xhr = this.xhr;
69997
69998         this.xhr.open(this.method, this.url, true);
69999         
70000         var headers = {
70001             "Accept": "application/json",
70002             "Cache-Control": "no-cache",
70003             "X-Requested-With": "XMLHttpRequest"
70004         };
70005         
70006         for (var headerName in headers) {
70007             var headerValue = headers[headerName];
70008             if (headerValue) {
70009                 this.xhr.setRequestHeader(headerName, headerValue);
70010             }
70011         }
70012         
70013         var _this = this;
70014         
70015         this.xhr.onload = function()
70016         {
70017             _this.xhrOnLoad(_this.xhr);
70018         }
70019         
70020         this.xhr.onerror = function()
70021         {
70022             _this.xhrOnError(_this.xhr);
70023         }
70024         
70025         var formData = new FormData();
70026
70027         formData.append('returnHTML', 'NO');
70028
70029         if(crop){
70030             formData.append('crop', crop);
70031             var blobBin = atob(crop.split(',')[1]);
70032             var array = [];
70033             for(var i = 0; i < blobBin.length; i++) {
70034                 array.push(blobBin.charCodeAt(i));
70035             }
70036             var croppedFile =new Blob([new Uint8Array(array)], {type: this.cropType});
70037             formData.append(this.paramName, croppedFile, file.name);
70038         }
70039         
70040         if(typeof(file.filename) != 'undefined'){
70041             formData.append('filename', file.filename);
70042         }
70043         
70044         if(typeof(file.mimetype) != 'undefined'){
70045             formData.append('mimetype', file.mimetype);
70046         }
70047
70048         if(this.fireEvent('arrange', this, formData) != false){
70049             this.xhr.send(formData);
70050         };
70051     },
70052     
70053     xhrOnLoad : function(xhr)
70054     {
70055         if(this.loadMask){
70056             this.maskEl.unmask();
70057         }
70058         
70059         if (xhr.readyState !== 4) {
70060             this.fireEvent('exception', this, xhr);
70061             return;
70062         }
70063
70064         var response = Roo.decode(xhr.responseText);
70065         
70066         if(!response.success){
70067             this.fireEvent('exception', this, xhr);
70068             return;
70069         }
70070         
70071         var response = Roo.decode(xhr.responseText);
70072         
70073         this.fireEvent('upload', this, response);
70074         
70075     },
70076     
70077     xhrOnError : function()
70078     {
70079         if(this.loadMask){
70080             this.maskEl.unmask();
70081         }
70082         
70083         Roo.log('xhr on error');
70084         
70085         var response = Roo.decode(xhr.responseText);
70086           
70087         Roo.log(response);
70088         
70089     },
70090     
70091     prepare : function(file)
70092     {   
70093         if(this.loadMask){
70094             this.maskEl.mask(this.loadingText);
70095         }
70096         
70097         this.file = false;
70098         this.exif = {};
70099         
70100         if(typeof(file) === 'string'){
70101             this.loadCanvas(file);
70102             return;
70103         }
70104         
70105         if(!file || !this.urlAPI){
70106             return;
70107         }
70108         
70109         this.file = file;
70110         if(typeof(file.type) != 'undefined' && file.type.length != 0) {
70111             this.cropType = file.type;
70112         }
70113         
70114         var _this = this;
70115         
70116         if(this.fireEvent('prepare', this, this.file) != false){
70117             
70118             var reader = new FileReader();
70119             
70120             reader.onload = function (e) {
70121                 if (e.target.error) {
70122                     Roo.log(e.target.error);
70123                     return;
70124                 }
70125                 
70126                 var buffer = e.target.result,
70127                     dataView = new DataView(buffer),
70128                     offset = 2,
70129                     maxOffset = dataView.byteLength - 4,
70130                     markerBytes,
70131                     markerLength;
70132                 
70133                 if (dataView.getUint16(0) === 0xffd8) {
70134                     while (offset < maxOffset) {
70135                         markerBytes = dataView.getUint16(offset);
70136                         
70137                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
70138                             markerLength = dataView.getUint16(offset + 2) + 2;
70139                             if (offset + markerLength > dataView.byteLength) {
70140                                 Roo.log('Invalid meta data: Invalid segment size.');
70141                                 break;
70142                             }
70143                             
70144                             if(markerBytes == 0xffe1){
70145                                 _this.parseExifData(
70146                                     dataView,
70147                                     offset,
70148                                     markerLength
70149                                 );
70150                             }
70151                             
70152                             offset += markerLength;
70153                             
70154                             continue;
70155                         }
70156                         
70157                         break;
70158                     }
70159                     
70160                 }
70161                 
70162                 var url = _this.urlAPI.createObjectURL(_this.file);
70163                 
70164                 _this.loadCanvas(url);
70165                 
70166                 return;
70167             }
70168             
70169             reader.readAsArrayBuffer(this.file);
70170             
70171         }
70172         
70173     },
70174     
70175     parseExifData : function(dataView, offset, length)
70176     {
70177         var tiffOffset = offset + 10,
70178             littleEndian,
70179             dirOffset;
70180     
70181         if (dataView.getUint32(offset + 4) !== 0x45786966) {
70182             // No Exif data, might be XMP data instead
70183             return;
70184         }
70185         
70186         // Check for the ASCII code for "Exif" (0x45786966):
70187         if (dataView.getUint32(offset + 4) !== 0x45786966) {
70188             // No Exif data, might be XMP data instead
70189             return;
70190         }
70191         if (tiffOffset + 8 > dataView.byteLength) {
70192             Roo.log('Invalid Exif data: Invalid segment size.');
70193             return;
70194         }
70195         // Check for the two null bytes:
70196         if (dataView.getUint16(offset + 8) !== 0x0000) {
70197             Roo.log('Invalid Exif data: Missing byte alignment offset.');
70198             return;
70199         }
70200         // Check the byte alignment:
70201         switch (dataView.getUint16(tiffOffset)) {
70202         case 0x4949:
70203             littleEndian = true;
70204             break;
70205         case 0x4D4D:
70206             littleEndian = false;
70207             break;
70208         default:
70209             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
70210             return;
70211         }
70212         // Check for the TIFF tag marker (0x002A):
70213         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
70214             Roo.log('Invalid Exif data: Missing TIFF marker.');
70215             return;
70216         }
70217         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
70218         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
70219         
70220         this.parseExifTags(
70221             dataView,
70222             tiffOffset,
70223             tiffOffset + dirOffset,
70224             littleEndian
70225         );
70226     },
70227     
70228     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
70229     {
70230         var tagsNumber,
70231             dirEndOffset,
70232             i;
70233         if (dirOffset + 6 > dataView.byteLength) {
70234             Roo.log('Invalid Exif data: Invalid directory offset.');
70235             return;
70236         }
70237         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
70238         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
70239         if (dirEndOffset + 4 > dataView.byteLength) {
70240             Roo.log('Invalid Exif data: Invalid directory size.');
70241             return;
70242         }
70243         for (i = 0; i < tagsNumber; i += 1) {
70244             this.parseExifTag(
70245                 dataView,
70246                 tiffOffset,
70247                 dirOffset + 2 + 12 * i, // tag offset
70248                 littleEndian
70249             );
70250         }
70251         // Return the offset to the next directory:
70252         return dataView.getUint32(dirEndOffset, littleEndian);
70253     },
70254     
70255     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
70256     {
70257         var tag = dataView.getUint16(offset, littleEndian);
70258         
70259         this.exif[tag] = this.getExifValue(
70260             dataView,
70261             tiffOffset,
70262             offset,
70263             dataView.getUint16(offset + 2, littleEndian), // tag type
70264             dataView.getUint32(offset + 4, littleEndian), // tag length
70265             littleEndian
70266         );
70267     },
70268     
70269     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
70270     {
70271         var tagType = Roo.dialog.UploadCropbox.exifTagTypes[type],
70272             tagSize,
70273             dataOffset,
70274             values,
70275             i,
70276             str,
70277             c;
70278     
70279         if (!tagType) {
70280             Roo.log('Invalid Exif data: Invalid tag type.');
70281             return;
70282         }
70283         
70284         tagSize = tagType.size * length;
70285         // Determine if the value is contained in the dataOffset bytes,
70286         // or if the value at the dataOffset is a pointer to the actual data:
70287         dataOffset = tagSize > 4 ?
70288                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
70289         if (dataOffset + tagSize > dataView.byteLength) {
70290             Roo.log('Invalid Exif data: Invalid data offset.');
70291             return;
70292         }
70293         if (length === 1) {
70294             return tagType.getValue(dataView, dataOffset, littleEndian);
70295         }
70296         values = [];
70297         for (i = 0; i < length; i += 1) {
70298             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
70299         }
70300         
70301         if (tagType.ascii) {
70302             str = '';
70303             // Concatenate the chars:
70304             for (i = 0; i < values.length; i += 1) {
70305                 c = values[i];
70306                 // Ignore the terminating NULL byte(s):
70307                 if (c === '\u0000') {
70308                     break;
70309                 }
70310                 str += c;
70311             }
70312             return str;
70313         }
70314         return values;
70315     }
70316     
70317 });
70318
70319 Roo.apply(Roo.dialog.UploadCropbox, {
70320     tags : {
70321         'Orientation': 0x0112
70322     },
70323     
70324     Orientation: {
70325             1: 0, //'top-left',
70326 //            2: 'top-right',
70327             3: 180, //'bottom-right',
70328 //            4: 'bottom-left',
70329 //            5: 'left-top',
70330             6: 90, //'right-top',
70331 //            7: 'right-bottom',
70332             8: 270 //'left-bottom'
70333     },
70334     
70335     exifTagTypes : {
70336         // byte, 8-bit unsigned int:
70337         1: {
70338             getValue: function (dataView, dataOffset) {
70339                 return dataView.getUint8(dataOffset);
70340             },
70341             size: 1
70342         },
70343         // ascii, 8-bit byte:
70344         2: {
70345             getValue: function (dataView, dataOffset) {
70346                 return String.fromCharCode(dataView.getUint8(dataOffset));
70347             },
70348             size: 1,
70349             ascii: true
70350         },
70351         // short, 16 bit int:
70352         3: {
70353             getValue: function (dataView, dataOffset, littleEndian) {
70354                 return dataView.getUint16(dataOffset, littleEndian);
70355             },
70356             size: 2
70357         },
70358         // long, 32 bit int:
70359         4: {
70360             getValue: function (dataView, dataOffset, littleEndian) {
70361                 return dataView.getUint32(dataOffset, littleEndian);
70362             },
70363             size: 4
70364         },
70365         // rational = two long values, first is numerator, second is denominator:
70366         5: {
70367             getValue: function (dataView, dataOffset, littleEndian) {
70368                 return dataView.getUint32(dataOffset, littleEndian) /
70369                     dataView.getUint32(dataOffset + 4, littleEndian);
70370             },
70371             size: 8
70372         },
70373         // slong, 32 bit signed int:
70374         9: {
70375             getValue: function (dataView, dataOffset, littleEndian) {
70376                 return dataView.getInt32(dataOffset, littleEndian);
70377             },
70378             size: 4
70379         },
70380         // srational, two slongs, first is numerator, second is denominator:
70381         10: {
70382             getValue: function (dataView, dataOffset, littleEndian) {
70383                 return dataView.getInt32(dataOffset, littleEndian) /
70384                     dataView.getInt32(dataOffset + 4, littleEndian);
70385             },
70386             size: 8
70387         }
70388     },
70389     
70390     footer : {
70391         STANDARD : [
70392             {
70393                 tag : 'div',
70394                 cls : 'btn-group roo-upload-cropbox-rotate-left',
70395                 action : 'rotate-left',
70396                 cn : [
70397                     {
70398                         tag : 'button',
70399                         cls : 'btn btn-default',
70400                         html : '<i class="fa fa-undo"></i>'
70401                     }
70402                 ]
70403             },
70404             {
70405                 tag : 'div',
70406                 cls : 'btn-group roo-upload-cropbox-picture',
70407                 action : 'picture',
70408                 cn : [
70409                     {
70410                         tag : 'button',
70411                         cls : 'btn btn-default',
70412                         html : '<i class="fa fa-picture-o"></i>'
70413                     }
70414                 ]
70415             },
70416             {
70417                 tag : 'div',
70418                 cls : 'btn-group roo-upload-cropbox-rotate-right',
70419                 action : 'rotate-right',
70420                 cn : [
70421                     {
70422                         tag : 'button',
70423                         cls : 'btn btn-default',
70424                         html : '<i class="fa fa-repeat"></i>'
70425                     }
70426                 ]
70427             }
70428         ],
70429         DOCUMENT : [
70430             {
70431                 tag : 'div',
70432                 cls : 'btn-group roo-upload-cropbox-rotate-left',
70433                 action : 'rotate-left',
70434                 cn : [
70435                     {
70436                         tag : 'button',
70437                         cls : 'btn btn-default',
70438                         html : '<i class="fa fa-undo"></i>'
70439                     }
70440                 ]
70441             },
70442             {
70443                 tag : 'div',
70444                 cls : 'btn-group roo-upload-cropbox-download',
70445                 action : 'download',
70446                 cn : [
70447                     {
70448                         tag : 'button',
70449                         cls : 'btn btn-default',
70450                         html : '<i class="fa fa-download"></i>'
70451                     }
70452                 ]
70453             },
70454             {
70455                 tag : 'div',
70456                 cls : 'btn-group roo-upload-cropbox-crop',
70457                 action : 'crop',
70458                 cn : [
70459                     {
70460                         tag : 'button',
70461                         cls : 'btn btn-default',
70462                         html : '<i class="fa fa-crop"></i>'
70463                     }
70464                 ]
70465             },
70466             {
70467                 tag : 'div',
70468                 cls : 'btn-group roo-upload-cropbox-trash',
70469                 action : 'trash',
70470                 cn : [
70471                     {
70472                         tag : 'button',
70473                         cls : 'btn btn-default',
70474                         html : '<i class="fa fa-trash"></i>'
70475                     }
70476                 ]
70477             },
70478             {
70479                 tag : 'div',
70480                 cls : 'btn-group roo-upload-cropbox-rotate-right',
70481                 action : 'rotate-right',
70482                 cn : [
70483                     {
70484                         tag : 'button',
70485                         cls : 'btn btn-default',
70486                         html : '<i class="fa fa-repeat"></i>'
70487                     }
70488                 ]
70489             }
70490         ],
70491         ROTATOR : [
70492             {
70493                 tag : 'div',
70494                 cls : 'btn-group roo-upload-cropbox-rotate-left',
70495                 action : 'rotate-left',
70496                 cn : [
70497                     {
70498                         tag : 'button',
70499                         cls : 'btn btn-default',
70500                         html : '<i class="fa fa-undo"></i>'
70501                     }
70502                 ]
70503             },
70504             {
70505                 tag : 'div',
70506                 cls : 'btn-group roo-upload-cropbox-rotate-right',
70507                 action : 'rotate-right',
70508                 cn : [
70509                     {
70510                         tag : 'button',
70511                         cls : 'btn btn-default',
70512                         html : '<i class="fa fa-repeat"></i>'
70513                     }
70514                 ]
70515             }
70516         ],
70517         CENTER : [
70518             {
70519                 tag : 'div',
70520                 cls : 'btn-group roo-upload-cropbox-center',
70521                 action : 'center',
70522                 cn : [
70523                     {
70524                         tag : 'button',
70525                         cls : 'btn btn-default',
70526                         html : 'CENTER'
70527                     }
70528                 ]
70529             }
70530         ]
70531     }
70532 });